]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
Split IOHook into IOHook and IOHookProvider
[user/henk/code/inspircd.git] / src / modules / m_httpd.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
6  *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
7  *   Copyright (C) 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
8  *   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
9  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
10  *
11  * This file is part of InspIRCd.  InspIRCd is free software: you can
12  * redistribute it and/or modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24
25 #include "inspircd.h"
26 #include "iohook.h"
27 #include "modules/httpd.h"
28
29 class ModuleHttpServer;
30
31 static ModuleHttpServer* HttpModule;
32 static bool claimed;
33 static std::set<HttpServerSocket*> sockets;
34
35 /** HTTP socket states
36  */
37 enum HttpState
38 {
39         HTTP_SERVE_WAIT_REQUEST = 0, /* Waiting for a full request */
40         HTTP_SERVE_RECV_POSTDATA = 1, /* Waiting to finish recieving POST data */
41         HTTP_SERVE_SEND_DATA = 2 /* Sending response */
42 };
43
44 /** A socket used for HTTP transport
45  */
46 class HttpServerSocket : public BufferedSocket
47 {
48         HttpState InternalState;
49         std::string ip;
50
51         HTTPHeaders headers;
52         std::string reqbuffer;
53         std::string postdata;
54         unsigned int postsize;
55         std::string request_type;
56         std::string uri;
57         std::string http_version;
58
59  public:
60         const time_t createtime;
61
62         HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
63                 : BufferedSocket(newfd), ip(IP), postsize(0)
64                 , createtime(ServerInstance->Time())
65         {
66                 InternalState = HTTP_SERVE_WAIT_REQUEST;
67
68                 if (via->iohookprov)
69                         via->iohookprov->OnAccept(this, client, server);
70         }
71
72         ~HttpServerSocket()
73         {
74                 sockets.erase(this);
75         }
76
77         void OnError(BufferedSocketError) CXX11_OVERRIDE
78         {
79                 ServerInstance->GlobalCulls.AddItem(this);
80         }
81
82         std::string Response(int response)
83         {
84                 switch (response)
85                 {
86                         case 100:
87                                 return "CONTINUE";
88                         case 101:
89                                 return "SWITCHING PROTOCOLS";
90                         case 200:
91                                 return "OK";
92                         case 201:
93                                 return "CREATED";
94                         case 202:
95                                 return "ACCEPTED";
96                         case 203:
97                                 return "NON-AUTHORITATIVE INFORMATION";
98                         case 204:
99                                 return "NO CONTENT";
100                         case 205:
101                                 return "RESET CONTENT";
102                         case 206:
103                                 return "PARTIAL CONTENT";
104                         case 300:
105                                 return "MULTIPLE CHOICES";
106                         case 301:
107                                 return "MOVED PERMENANTLY";
108                         case 302:
109                                 return "FOUND";
110                         case 303:
111                                 return "SEE OTHER";
112                         case 304:
113                                 return "NOT MODIFIED";
114                         case 305:
115                                 return "USE PROXY";
116                         case 307:
117                                 return "TEMPORARY REDIRECT";
118                         case 400:
119                                 return "BAD REQUEST";
120                         case 401:
121                                 return "UNAUTHORIZED";
122                         case 402:
123                                 return "PAYMENT REQUIRED";
124                         case 403:
125                                 return "FORBIDDEN";
126                         case 404:
127                                 return "NOT FOUND";
128                         case 405:
129                                 return "METHOD NOT ALLOWED";
130                         case 406:
131                                 return "NOT ACCEPTABLE";
132                         case 407:
133                                 return "PROXY AUTHENTICATION REQUIRED";
134                         case 408:
135                                 return "REQUEST TIMEOUT";
136                         case 409:
137                                 return "CONFLICT";
138                         case 410:
139                                 return "GONE";
140                         case 411:
141                                 return "LENGTH REQUIRED";
142                         case 412:
143                                 return "PRECONDITION FAILED";
144                         case 413:
145                                 return "REQUEST ENTITY TOO LARGE";
146                         case 414:
147                                 return "REQUEST-URI TOO LONG";
148                         case 415:
149                                 return "UNSUPPORTED MEDIA TYPE";
150                         case 416:
151                                 return "REQUESTED RANGE NOT SATISFIABLE";
152                         case 417:
153                                 return "EXPECTATION FAILED";
154                         case 500:
155                                 return "INTERNAL SERVER ERROR";
156                         case 501:
157                                 return "NOT IMPLEMENTED";
158                         case 502:
159                                 return "BAD GATEWAY";
160                         case 503:
161                                 return "SERVICE UNAVAILABLE";
162                         case 504:
163                                 return "GATEWAY TIMEOUT";
164                         case 505:
165                                 return "HTTP VERSION NOT SUPPORTED";
166                         default:
167                                 return "WTF";
168                         break;
169
170                 }
171         }
172
173         void SendHTTPError(int response)
174         {
175                 HTTPHeaders empty;
176                 std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
177                                    "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>";
178
179                 SendHeaders(data.length(), response, empty);
180                 WriteData(data);
181         }
182
183         void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
184         {
185
186                 WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
187
188                 time_t local = ServerInstance->Time();
189                 struct tm *timeinfo = gmtime(&local);
190                 char *date = asctime(timeinfo);
191                 date[strlen(date) - 1] = '\0';
192                 rheaders.CreateHeader("Date", date);
193
194                 rheaders.CreateHeader("Server", BRANCH);
195                 rheaders.SetHeader("Content-Length", ConvToStr(size));
196
197                 if (size)
198                         rheaders.CreateHeader("Content-Type", "text/html");
199                 else
200                         rheaders.RemoveHeader("Content-Type");
201
202                 /* Supporting Connection: keep-alive causes a whole world of hurt syncronizing timeouts,
203                  * so remove it, its not essential for what we need.
204                  */
205                 rheaders.SetHeader("Connection", "Close");
206
207                 WriteData(rheaders.GetFormattedHeaders());
208                 WriteData("\r\n");
209         }
210
211         void OnDataReady()
212         {
213                 if (InternalState == HTTP_SERVE_RECV_POSTDATA)
214                 {
215                         postdata.append(recvq);
216                         if (postdata.length() >= postsize)
217                                 ServeData();
218                 }
219                 else
220                 {
221                         reqbuffer.append(recvq);
222
223                         if (reqbuffer.length() >= 8192)
224                         {
225                                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "m_httpd dropped connection due to an oversized request buffer");
226                                 reqbuffer.clear();
227                                 SetError("Buffer");
228                         }
229
230                         if (InternalState == HTTP_SERVE_WAIT_REQUEST)
231                                 CheckRequestBuffer();
232                 }
233         }
234
235         void CheckRequestBuffer()
236         {
237                 std::string::size_type reqend = reqbuffer.find("\r\n\r\n");
238                 if (reqend == std::string::npos)
239                         return;
240
241                 // We have the headers; parse them all
242                 std::string::size_type hbegin = 0, hend;
243                 while ((hend = reqbuffer.find("\r\n", hbegin)) != std::string::npos)
244                 {
245                         if (hbegin == hend)
246                                 break;
247
248                         if (request_type.empty())
249                         {
250                                 std::istringstream cheader(std::string(reqbuffer, hbegin, hend - hbegin));
251                                 cheader >> request_type;
252                                 cheader >> uri;
253                                 cheader >> http_version;
254
255                                 if (request_type.empty() || uri.empty() || http_version.empty())
256                                 {
257                                         SendHTTPError(400);
258                                         return;
259                                 }
260
261                                 hbegin = hend + 2;
262                                 continue;
263                         }
264
265                         std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
266
267                         std::string::size_type fieldsep = cheader.find(':');
268                         if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
269                         {
270                                 SendHTTPError(400);
271                                 return;
272                         }
273
274                         headers.SetHeader(cheader.substr(0, fieldsep), cheader.substr(fieldsep + 2));
275
276                         hbegin = hend + 2;
277                 }
278
279                 reqbuffer.erase(0, reqend + 4);
280
281                 std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
282                 std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
283
284                 if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
285                 {
286                         SendHTTPError(505);
287                         return;
288                 }
289
290                 if (headers.IsSet("Content-Length") && (postsize = ConvToInt(headers.GetHeader("Content-Length"))) > 0)
291                 {
292                         InternalState = HTTP_SERVE_RECV_POSTDATA;
293
294                         if (reqbuffer.length() >= postsize)
295                         {
296                                 postdata = reqbuffer.substr(0, postsize);
297                                 reqbuffer.erase(0, postsize);
298                         }
299                         else if (!reqbuffer.empty())
300                         {
301                                 postdata = reqbuffer;
302                                 reqbuffer.clear();
303                         }
304
305                         if (postdata.length() >= postsize)
306                                 ServeData();
307
308                         return;
309                 }
310
311                 ServeData();
312         }
313
314         void ServeData()
315         {
316                 InternalState = HTTP_SERVE_SEND_DATA;
317
318                 claimed = false;
319                 HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
320                 acl.Send();
321                 if (!claimed)
322                 {
323                         HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
324                         url.Send();
325                         if (!claimed)
326                         {
327                                 SendHTTPError(404);
328                         }
329                 }
330         }
331
332         void Page(std::stringstream* n, int response, HTTPHeaders *hheaders)
333         {
334                 SendHeaders(n->str().length(), response, *hheaders);
335                 WriteData(n->str());
336         }
337 };
338
339 class HTTPdAPIImpl : public HTTPdAPIBase
340 {
341  public:
342         HTTPdAPIImpl(Module* parent)
343                 : HTTPdAPIBase(parent)
344         {
345         }
346
347         void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
348         {
349                 claimed = true;
350                 resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
351         }
352 };
353
354 class ModuleHttpServer : public Module
355 {
356         std::vector<HttpServerSocket *> httpsocks;
357         HTTPdAPIImpl APIImpl;
358         unsigned int timeoutsec;
359
360  public:
361         ModuleHttpServer()
362                 : APIImpl(this)
363         {
364         }
365
366         void init() CXX11_OVERRIDE
367         {
368                 HttpModule = this;
369         }
370
371         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
372         {
373                 ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
374                 timeoutsec = tag->getInt("timeout");
375         }
376
377         ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
378         {
379                 if (from->bind_tag->getString("type") != "httpd")
380                         return MOD_RES_PASSTHRU;
381                 int port;
382                 std::string incomingip;
383                 irc::sockets::satoap(*client, incomingip, port);
384                 sockets.insert(new HttpServerSocket(nfd, incomingip, from, client, server));
385                 return MOD_RES_ALLOW;
386         }
387
388         void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
389         {
390                 if (!timeoutsec)
391                         return;
392
393                 time_t oldest_allowed = curtime - timeoutsec;
394                 for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
395                 {
396                         HttpServerSocket* sock = *i;
397                         ++i;
398                         if (sock->createtime < oldest_allowed)
399                         {
400                                 sock->cull();
401                                 delete sock;
402                         }
403                 }
404         }
405
406         CullResult cull() CXX11_OVERRIDE
407         {
408                 std::set<HttpServerSocket*> local;
409                 local.swap(sockets);
410                 for (std::set<HttpServerSocket*>::const_iterator i = local.begin(); i != local.end(); ++i)
411                 {
412                         HttpServerSocket* sock = *i;
413                         sock->cull();
414                         delete sock;
415                 }
416                 return Module::cull();
417         }
418
419         Version GetVersion() CXX11_OVERRIDE
420         {
421                 return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
422         }
423 };
424
425 MODULE_INIT(ModuleHttpServer)