]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
Create StreamSocket for IO hooking implementation
[user/henk/code/inspircd.git] / src / modules / m_httpd.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "httpd.h"
16
17 /* $ModDesc: Provides HTTP serving facilities to modules */
18 /* $ModDep: httpd.h */
19
20 class ModuleHttpServer;
21
22 static ModuleHttpServer* HttpModule;
23 static bool claimed;
24
25 /** HTTP socket states
26  */
27 enum HttpState
28 {
29         HTTP_SERVE_WAIT_REQUEST = 0, /* Waiting for a full request */
30         HTTP_SERVE_RECV_POSTDATA = 1, /* Waiting to finish recieving POST data */
31         HTTP_SERVE_SEND_DATA = 2 /* Sending response */
32 };
33
34 /** A socket used for HTTP transport
35  */
36 class HttpServerSocket : public BufferedSocket
37 {
38         FileReader* index;
39         HttpState InternalState;
40         std::string ip;
41
42         HTTPHeaders headers;
43         std::string reqbuffer;
44         std::string postdata;
45         unsigned int postsize;
46         std::string request_type;
47         std::string uri;
48         std::string http_version;
49
50  public:
51
52         HttpServerSocket(int newfd, const char* IP, FileReader* ind)
53                 : BufferedSocket(newfd), index(ind), ip(IP), postsize(0)
54         {
55                 InternalState = HTTP_SERVE_WAIT_REQUEST;
56         }
57
58         FileReader* GetIndex()
59         {
60                 return index;
61         }
62
63         ~HttpServerSocket()
64         {
65         }
66
67         virtual void OnError(BufferedSocketError)
68         {
69         }
70
71         std::string Response(int response)
72         {
73                 switch (response)
74                 {
75                         case 100:
76                                 return "CONTINUE";
77                         case 101:
78                                 return "SWITCHING PROTOCOLS";
79                         case 200:
80                                 return "OK";
81                         case 201:
82                                 return "CREATED";
83                         case 202:
84                                 return "ACCEPTED";
85                         case 203:
86                                 return "NON-AUTHORITATIVE INFORMATION";
87                         case 204:
88                                 return "NO CONTENT";
89                         case 205:
90                                 return "RESET CONTENT";
91                         case 206:
92                                 return "PARTIAL CONTENT";
93                         case 300:
94                                 return "MULTIPLE CHOICES";
95                         case 301:
96                                 return "MOVED PERMENANTLY";
97                         case 302:
98                                 return "FOUND";
99                         case 303:
100                                 return "SEE OTHER";
101                         case 304:
102                                 return "NOT MODIFIED";
103                         case 305:
104                                 return "USE PROXY";
105                         case 307:
106                                 return "TEMPORARY REDIRECT";
107                         case 400:
108                                 return "BAD REQUEST";
109                         case 401:
110                                 return "UNAUTHORIZED";
111                         case 402:
112                                 return "PAYMENT REQUIRED";
113                         case 403:
114                                 return "FORBIDDEN";
115                         case 404:
116                                 return "NOT FOUND";
117                         case 405:
118                                 return "METHOD NOT ALLOWED";
119                         case 406:
120                                 return "NOT ACCEPTABLE";
121                         case 407:
122                                 return "PROXY AUTHENTICATION REQUIRED";
123                         case 408:
124                                 return "REQUEST TIMEOUT";
125                         case 409:
126                                 return "CONFLICT";
127                         case 410:
128                                 return "GONE";
129                         case 411:
130                                 return "LENGTH REQUIRED";
131                         case 412:
132                                 return "PRECONDITION FAILED";
133                         case 413:
134                                 return "REQUEST ENTITY TOO LARGE";
135                         case 414:
136                                 return "REQUEST-URI TOO LONG";
137                         case 415:
138                                 return "UNSUPPORTED MEDIA TYPE";
139                         case 416:
140                                 return "REQUESTED RANGE NOT SATISFIABLE";
141                         case 417:
142                                 return "EXPECTATION FAILED";
143                         case 500:
144                                 return "INTERNAL SERVER ERROR";
145                         case 501:
146                                 return "NOT IMPLEMENTED";
147                         case 502:
148                                 return "BAD GATEWAY";
149                         case 503:
150                                 return "SERVICE UNAVAILABLE";
151                         case 504:
152                                 return "GATEWAY TIMEOUT";
153                         case 505:
154                                 return "HTTP VERSION NOT SUPPORTED";
155                         default:
156                                 return "WTF";
157                         break;
158
159                 }
160         }
161
162         void SendHTTPError(int response)
163         {
164                 HTTPHeaders empty;
165                 std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
166                                    "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>";
167
168                 SendHeaders(data.length(), response, empty);
169                 WriteData(data);
170         }
171
172         void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
173         {
174
175                 WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
176
177                 time_t local = ServerInstance->Time();
178                 struct tm *timeinfo = gmtime(&local);
179                 char *date = asctime(timeinfo);
180                 date[strlen(date) - 1] = '\0';
181                 rheaders.CreateHeader("Date", date);
182
183                 rheaders.CreateHeader("Server", "InspIRCd/m_httpd.so/1.2");
184                 rheaders.SetHeader("Content-Length", ConvToStr(size));
185
186                 if (size)
187                         rheaders.CreateHeader("Content-Type", "text/html");
188                 else
189                         rheaders.RemoveHeader("Content-Type");
190
191                 /* Supporting Connection: keep-alive causes a whole world of hurt syncronizing timeouts,
192                  * so remove it, its not essential for what we need.
193                  */
194                 rheaders.SetHeader("Connection", "Close");
195
196                 WriteData(rheaders.GetFormattedHeaders());
197                 WriteData("\r\n");
198         }
199
200         void OnDataReady()
201         {
202                 if (InternalState == HTTP_SERVE_RECV_POSTDATA)
203                 {
204                         postdata.append(recvq);
205                         if (postdata.length() >= postsize)
206                                 ServeData();
207                 }
208                 else
209                 {
210                         reqbuffer.append(recvq);
211
212                         if (reqbuffer.length() >= 8192)
213                         {
214                                 ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
215                                 reqbuffer.clear();
216                                 SetError("Buffer");
217                         }
218
219                         if (InternalState == HTTP_SERVE_WAIT_REQUEST)
220                                 CheckRequestBuffer();
221                 }
222         }
223
224         void CheckRequestBuffer()
225         {
226                 std::string::size_type reqend = reqbuffer.find("\r\n\r\n");
227                 if (reqend == std::string::npos)
228                         return;
229
230                 // We have the headers; parse them all
231                 std::string::size_type hbegin = 0, hend;
232                 while ((hend = reqbuffer.find("\r\n", hbegin)) != std::string::npos)
233                 {
234                         if (hbegin == hend)
235                                 break;
236
237                         if (request_type.empty())
238                         {
239                                 std::istringstream cheader(std::string(reqbuffer, hbegin, hend - hbegin));
240                                 cheader >> request_type;
241                                 cheader >> uri;
242                                 cheader >> http_version;
243
244                                 if (request_type.empty() || uri.empty() || http_version.empty())
245                                 {
246                                         SendHTTPError(400);
247                                         return;
248                                 }
249
250                                 hbegin = hend + 2;
251                                 continue;
252                         }
253
254                         std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
255
256                         std::string::size_type fieldsep = cheader.find(':');
257                         if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
258                         {
259                                 SendHTTPError(400);
260                                 return;
261                         }
262
263                         headers.SetHeader(cheader.substr(0, fieldsep), cheader.substr(fieldsep + 2));
264
265                         hbegin = hend + 2;
266                 }
267
268                 reqbuffer.erase(0, reqend + 4);
269
270                 std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
271                 std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
272
273                 if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
274                 {
275                         SendHTTPError(505);
276                         return;
277                 }
278
279                 if (headers.IsSet("Content-Length") && (postsize = atoi(headers.GetHeader("Content-Length").c_str())) != 0)
280                 {
281                         InternalState = HTTP_SERVE_RECV_POSTDATA;
282
283                         if (reqbuffer.length() >= postsize)
284                         {
285                                 postdata = reqbuffer.substr(0, postsize);
286                                 reqbuffer.erase(0, postsize);
287                         }
288                         else if (!reqbuffer.empty())
289                         {
290                                 postdata = reqbuffer;
291                                 reqbuffer.clear();
292                         }
293
294                         if (postdata.length() >= postsize)
295                                 ServeData();
296
297                         return;
298                 }
299
300                 ServeData();
301         }
302
303         void ServeData()
304         {
305                 InternalState = HTTP_SERVE_SEND_DATA;
306
307                 if ((request_type == "GET") && (uri == "/"))
308                 {
309                         HTTPHeaders empty;
310                         SendHeaders(index->ContentSize(), 200, empty);
311                         WriteData(index->Contents());
312                 }
313                 else
314                 {
315                         claimed = false;
316                         HTTPRequest httpr(request_type,uri,&headers,this,ip,postdata);
317                         Event acl((char*)&httpr, (Module*)HttpModule, "httpd_acl");
318                         acl.Send(ServerInstance);
319                         if (!claimed)
320                         {
321                                 Event e((char*)&httpr, (Module*)HttpModule, "httpd_url");
322                                 e.Send(ServerInstance);
323                                 if (!claimed)
324                                 {
325                                         SendHTTPError(404);
326                                 }
327                         }
328                 }
329         }
330
331         void Page(std::stringstream* n, int response, HTTPHeaders *hheaders)
332         {
333                 SendHeaders(n->str().length(), response, *hheaders);
334                 WriteData(n->str());
335         }
336 };
337
338 /** Spawn HTTP sockets from a listener
339  */
340 class HttpListener : public ListenSocketBase
341 {
342         FileReader* index;
343
344  public:
345         HttpListener(InspIRCd* Instance, FileReader *idx, int port, const std::string &addr) : ListenSocketBase(Instance, port, addr)
346         {
347                 this->index = idx;
348         }
349
350         virtual void OnAcceptReady(int nfd)
351         {
352                 int port;
353                 std::string incomingip;
354                 irc::sockets::satoap(&client, incomingip, port);
355                 new HttpServerSocket(nfd, incomingip.c_str(), index);
356         }
357 };
358
359 class ModuleHttpServer : public Module
360 {
361         std::vector<HttpServerSocket *> httpsocks;
362         std::vector<HttpListener *> httplisteners;
363  public:
364
365         void ReadConfig()
366         {
367                 int port;
368                 std::string host;
369                 std::string bindip;
370                 std::string indexfile;
371                 FileReader* index;
372                 HttpListener *http;
373                 ConfigReader c(ServerInstance);
374
375                 httpsocks.clear(); // XXX this will BREAK if this module is made rehashable
376                 httplisteners.clear();
377
378                 for (int i = 0; i < c.Enumerate("http"); i++)
379                 {
380                         host = c.ReadValue("http", "host", i);
381                         bindip = c.ReadValue("http", "ip", i);
382                         port = c.ReadInteger("http", "port", i, true);
383                         indexfile = c.ReadValue("http", "index", i);
384                         index = new FileReader(ServerInstance, indexfile);
385                         if (!index->Exists())
386                                 throw ModuleException("Can't read index file: "+indexfile);
387                         http = new HttpListener(ServerInstance, index, port, (char *)bindip.c_str()); // XXX this cast SUCKS.
388                         httplisteners.push_back(http);
389                 }
390         }
391
392         ModuleHttpServer(InspIRCd* Me) : Module(Me)
393         {
394                 ReadConfig();
395                 HttpModule = this;
396                 Implementation eventlist[] = { I_OnRequest };
397                 ServerInstance->Modules->Attach(eventlist, this, 1);
398         }
399
400         virtual const char* OnRequest(Request* request)
401         {
402                 claimed = true;
403                 HTTPDocument* doc = (HTTPDocument*)request->GetData();
404                 HttpServerSocket* sock = (HttpServerSocket*)doc->sock;
405                 sock->Page(doc->GetDocument(), doc->GetResponseCode(), &doc->headers);
406                 return NULL;
407         }
408
409
410         virtual ~ModuleHttpServer()
411         {
412                 for (size_t i = 0; i < httplisteners.size(); i++)
413                 {
414                         delete httplisteners[i];
415                 }
416
417                 for (size_t i = 0; i < httpsocks.size(); i++)
418                 {
419                         ServerInstance->SE->DelFd(httpsocks[i]);
420                         httpsocks[i]->Close();
421                         delete httpsocks[i]->GetIndex();
422                 }
423         }
424
425         virtual Version GetVersion()
426         {
427                 return Version("$Id$", VF_VENDOR | VF_SERVICEPROVIDER, API_VERSION);
428         }
429 };
430
431 MODULE_INIT(ModuleHttpServer)