]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
Header update: 2007 -> 2008
[user/henk/code/inspircd.git] / src / modules / m_httpd.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/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_LISTEN = 0,
30         HTTP_SERVE_WAIT_REQUEST = 1, /* Waiting for a full request */
31         HTTP_SERVE_RECV_POSTDATA = 2, /* Waiting to finish recieving POST data */
32         HTTP_SERVE_SEND_DATA = 3 /* Sending response */
33 };
34
35 class HttpServerSocket;
36
37 /** This class is used to handle HTTP socket timeouts
38  */
39 class HttpServerTimeout : public Timer
40 {
41  private:
42         /** HttpServerSocket we are attached to
43          */
44         HttpServerSocket* s;
45         /** Socketengine the file descriptor is in
46          */
47         SocketEngine* SE;
48  public:
49         /** Attach timeout to HttpServerSocket
50          */
51         HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine);
52         /** Handle timer tick
53          */
54         void Tick(time_t TIME);
55 };
56
57 /** A socket used for HTTP transport
58  */
59 class HttpServerSocket : public BufferedSocket
60 {
61         FileReader* index;
62         HttpState InternalState;
63         
64         HTTPHeaders headers;
65         std::string reqbuffer;
66         std::string postdata;
67         unsigned int postsize;
68         std::string request_type;
69         std::string uri;
70         std::string http_version;
71         bool keepalive;
72         
73         HttpServerTimeout* Timeout;
74         bool DataSinceLastTick;
75         friend class HttpServerTimeout;
76         
77  public:
78
79         HttpServerSocket(InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, FileReader* index_page) : BufferedSocket(SI, host, port, listening, maxtime), index(index_page), postsize(0)
80         {
81                 InternalState = HTTP_LISTEN;
82                 Timeout = NULL;
83         }
84
85         HttpServerSocket(InspIRCd* SI, int newfd, char* ip, FileReader* ind) : BufferedSocket(SI, newfd, ip), index(ind), postsize(0), keepalive(false), DataSinceLastTick(false)
86         {
87                 InternalState = HTTP_SERVE_WAIT_REQUEST;
88                 Timeout = new HttpServerTimeout(this, Instance->SE);
89                 Instance->Timers->AddTimer(Timeout);
90         }
91
92         FileReader* GetIndex()
93         {
94                 return index;
95         }
96
97         ~HttpServerSocket()
98         {
99                 if (Timeout)
100                 {
101                         if (Instance->Time() < Timeout->GetTimer())
102                                 Instance->Timers->DelTimer(Timeout);
103                         Timeout = NULL;
104                 }
105         }
106         
107         void ResetRequest()
108         {
109                 headers.Clear();
110                 postdata.clear();
111                 postsize = 0;
112                 request_type.clear();
113                 uri.clear();
114                 http_version.clear();
115                 InternalState = HTTP_SERVE_WAIT_REQUEST;
116                 
117                 if (reqbuffer.size())
118                         CheckRequestBuffer();
119         }
120         
121         virtual int OnIncomingConnection(int newsock, char* ip)
122         {
123                 if (InternalState == HTTP_LISTEN)
124                 {
125                         new HttpServerSocket(this->Instance, newsock, ip, index);
126                 }
127                 return true;
128         }
129
130         virtual void OnClose()
131         {
132         }
133
134         std::string Response(int response)
135         {
136                 switch (response)
137                 {
138                         case 100:
139                                 return "CONTINUE";
140                         case 101:
141                                 return "SWITCHING PROTOCOLS";
142                         case 200:
143                                 return "OK";
144                         case 201:
145                                 return "CREATED";
146                         case 202:
147                                 return "ACCEPTED";
148                         case 203:
149                                 return "NON-AUTHORITATIVE INFORMATION";
150                         case 204:
151                                 return "NO CONTENT";
152                         case 205:
153                                 return "RESET CONTENT";
154                         case 206:
155                                 return "PARTIAL CONTENT";
156                         case 300:
157                                 return "MULTIPLE CHOICES";
158                         case 301:
159                                 return "MOVED PERMENANTLY";
160                         case 302:
161                                 return "FOUND";
162                         case 303:
163                                 return "SEE OTHER";
164                         case 304:
165                                 return "NOT MODIFIED";
166                         case 305:
167                                 return "USE PROXY";
168                         case 307:
169                                 return "TEMPORARY REDIRECT";
170                         case 400:
171                                 return "BAD REQUEST";
172                         case 401:
173                                 return "UNAUTHORIZED";
174                         case 402:
175                                 return "PAYMENT REQUIRED";
176                         case 403:
177                                 return "FORBIDDEN";
178                         case 404:
179                                 return "NOT FOUND";
180                         case 405:
181                                 return "METHOD NOT ALLOWED";
182                         case 406:
183                                 return "NOT ACCEPTABLE";
184                         case 407:
185                                 return "PROXY AUTHENTICATION REQUIRED";
186                         case 408:
187                                 return "REQUEST TIMEOUT";
188                         case 409:
189                                 return "CONFLICT";
190                         case 410:
191                                 return "GONE";
192                         case 411:
193                                 return "LENGTH REQUIRED";
194                         case 412:
195                                 return "PRECONDITION FAILED";
196                         case 413:
197                                 return "REQUEST ENTITY TOO LARGE";
198                         case 414:
199                                 return "REQUEST-URI TOO LONG";
200                         case 415:
201                                 return "UNSUPPORTED MEDIA TYPE";
202                         case 416:
203                                 return "REQUESTED RANGE NOT SATISFIABLE";
204                         case 417:
205                                 return "EXPECTATION FAILED";
206                         case 500:
207                                 return "INTERNAL SERVER ERROR";
208                         case 501:
209                                 return "NOT IMPLEMENTED";
210                         case 502:
211                                 return "BAD GATEWAY";
212                         case 503:
213                                 return "SERVICE UNAVAILABLE";
214                         case 504:
215                                 return "GATEWAY TIMEOUT";
216                         case 505:
217                                 return "HTTP VERSION NOT SUPPORTED";
218                         default:
219                                 return "WTF";
220                         break;
221                                 
222                 }
223         }
224         
225         void SendHTTPError(int response)
226         {
227                 HTTPHeaders empty;
228                 std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
229                                    "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>";
230                 
231                 SendHeaders(data.length(), response, empty);
232                 this->Write(data);
233                 
234                 if (keepalive)
235                         ResetRequest();
236         }
237         
238         void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
239         {
240
241                 this->Write(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
242
243                 time_t local = this->Instance->Time();
244                 struct tm *timeinfo = gmtime(&local);
245                 char *date = asctime(timeinfo);
246                 date[strlen(date) - 1] = '\0';
247                 rheaders.CreateHeader("Date", date);
248                 
249                 rheaders.CreateHeader("Server", "InspIRCd/m_httpd.so/1.1");
250                 rheaders.SetHeader("Content-Length", ConvToStr(size));
251                 
252                 if (size)
253                         rheaders.CreateHeader("Content-Type", "text/html");
254                 else
255                         rheaders.RemoveHeader("Content-Type");
256                 
257                 if (rheaders.GetHeader("Connection") == "Close")
258                         keepalive = false;
259                 else if (rheaders.GetHeader("Connection") == "Keep-Alive" && !headers.IsSet("Connection"))
260                         keepalive = true;
261                 else if (!rheaders.IsSet("Connection") && !keepalive)
262                         rheaders.SetHeader("Connection", "Close");
263                 
264                 this->Write(rheaders.GetFormattedHeaders());
265                 this->Write("\r\n");
266                 
267                 if (!size && keepalive)
268                         ResetRequest();
269         }
270
271         virtual bool OnDataReady()
272         {
273                 char* data = this->Read();
274
275                 /* Check that the data read is a valid pointer and it has some content */
276                 if (!data || !*data)
277                         return false;
278                 
279                 DataSinceLastTick = true;
280                 
281                 if (InternalState == HTTP_SERVE_RECV_POSTDATA)
282                 {
283                         postdata.append(data);
284                         if (postdata.length() >= postsize)
285                                 ServeData();
286                 }
287                 else
288                 {
289                         reqbuffer.append(data);
290                         
291                         if (reqbuffer.length() >= 8192)
292                         {
293                                 Instance->Log(DEBUG, "m_httpd dropped connection due to an oversized request buffer");
294                                 reqbuffer.clear();
295                                 return false;
296                         }
297                         
298                         if (InternalState == HTTP_SERVE_WAIT_REQUEST)
299                                 CheckRequestBuffer();
300                 }
301                 
302                 return true;
303         }
304         
305         void CheckRequestBuffer()
306         {
307                 std::string::size_type reqend = reqbuffer.find("\r\n\r\n");
308                 if (reqend == std::string::npos)
309                         return;
310                 
311                 // We have the headers; parse them all
312                 std::string::size_type hbegin = 0, hend;
313                 while ((hend = reqbuffer.find("\r\n", hbegin)) != std::string::npos)
314                 {
315                         if (hbegin == hend)
316                                 break;
317                         
318                         if (request_type.empty())
319                         {
320                                 std::istringstream cheader(std::string(reqbuffer, hbegin, hend - hbegin));
321                                 cheader >> request_type;
322                                 cheader >> uri;
323                                 cheader >> http_version;
324                                 
325                                 if (request_type.empty() || uri.empty() || http_version.empty())
326                                 {
327                                         SendHTTPError(400);
328                                         return;
329                                 }
330                                 
331                                 hbegin = hend + 2;
332                                 continue;
333                         }
334                         
335                         std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
336                         
337                         std::string::size_type fieldsep = cheader.find(':');
338                         if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
339                         {
340                                 SendHTTPError(400);
341                                 return;
342                         }
343                         
344                         headers.SetHeader(cheader.substr(0, fieldsep), cheader.substr(fieldsep + 2));
345                         
346                         hbegin = hend + 2;
347                 }
348                 
349                 reqbuffer.erase(0, reqend + 4);
350                 
351                 std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
352                 std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
353                 
354                 if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
355                 {
356                         SendHTTPError(505);
357                         return;
358                 }
359                 
360                 if (strcasecmp(headers.GetHeader("Connection").c_str(), "keep-alive") == 0)
361                         keepalive = true;
362                 
363                 if (headers.IsSet("Content-Length") && (postsize = atoi(headers.GetHeader("Content-Length").c_str())) != 0)
364                 {
365                         InternalState = HTTP_SERVE_RECV_POSTDATA;
366                         
367                         if (reqbuffer.length() >= postsize)
368                         {
369                                 postdata = reqbuffer.substr(0, postsize);
370                                 reqbuffer.erase(0, postsize);
371                         }
372                         else if (!reqbuffer.empty())
373                         {
374                                 postdata = reqbuffer;
375                                 reqbuffer.clear();
376                         }
377                         
378                         if (postdata.length() >= postsize)
379                                 ServeData();
380                         
381                         return;
382                 }
383                 
384                 ServeData();
385         }
386
387         void ServeData()
388         {
389                 InternalState = HTTP_SERVE_SEND_DATA;
390
391                 if ((request_type == "GET") && (uri == "/"))
392                 {
393                         HTTPHeaders empty;
394                         SendHeaders(index->ContentSize(), 200, empty);
395                         this->Write(index->Contents());
396                 }
397                 else
398                 {
399                         claimed = false;
400                         HTTPRequest httpr(request_type,uri,&headers,this,this->GetIP(),postdata);
401                         Event e((char*)&httpr, (Module*)HttpModule, "httpd_url");
402                         e.Send(this->Instance);
403                         if (!claimed)
404                         {
405                                 SendHTTPError(404);
406                         }
407                 }
408         }
409
410         void Page(std::stringstream* n, int response, HTTPHeaders *headers)
411         {
412                 SendHeaders(n->str().length(), response, *headers);
413                 this->Write(n->str());
414                 
415                 if (!keepalive)
416                 {
417                         Instance->SE->DelFd(this);
418                         this->Close();
419                 }
420                 else
421                         this->ResetRequest();
422         }
423 };
424
425 HttpServerTimeout::HttpServerTimeout(HttpServerSocket* sock, SocketEngine* engine) : Timer(15, time(NULL), true), s(sock), SE(engine)
426 {
427 }
428
429 void HttpServerTimeout::Tick(time_t TIME)
430 {
431         if (!s->DataSinceLastTick)
432         {
433                 SE->DelFd(s);
434                 s->Close();
435                 s->Timeout = NULL;
436                 this->CancelRepeat();
437         }
438         else
439                 s->DataSinceLastTick = false;
440 }
441
442 class ModuleHttpServer : public Module
443 {
444         std::vector<HttpServerSocket*> httpsocks;
445  public:
446
447         void ReadConfig()
448         {
449                 int port;
450                 std::string host;
451                 std::string bindip;
452                 std::string indexfile;
453                 FileReader* index;
454                 HttpServerSocket* http;
455                 ConfigReader c(ServerInstance);
456
457                 httpsocks.clear();
458
459                 for (int i = 0; i < c.Enumerate("http"); i++)
460                 {
461                         host = c.ReadValue("http", "host", i);
462                         bindip = c.ReadValue("http", "ip", i);
463                         port = c.ReadInteger("http", "port", i, true);
464                         indexfile = c.ReadValue("http", "index", i);
465                         index = new FileReader(ServerInstance, indexfile);
466                         if (!index->Exists())
467                                 throw ModuleException("Can't read index file: "+indexfile);
468                         http = new HttpServerSocket(ServerInstance, bindip, port, true, 0, index);
469                         httpsocks.push_back(http);
470                 }
471         }
472
473         ModuleHttpServer(InspIRCd* Me) : Module(Me)
474         {
475                 ReadConfig();
476                 HttpModule = this;
477                 Implementation eventlist[] = { I_OnRequest };
478                 ServerInstance->Modules->Attach(eventlist, this, 1);
479         }
480
481         char* OnRequest(Request* request)
482         {
483                 claimed = true;
484                 HTTPDocument* doc = (HTTPDocument*)request->GetData();
485                 HttpServerSocket* sock = (HttpServerSocket*)doc->sock;
486                 sock->Page(doc->GetDocument(), doc->GetResponseCode(), &doc->headers);
487                 return NULL;
488         }
489
490
491         virtual ~ModuleHttpServer()
492         {
493                 for (size_t i = 0; i < httpsocks.size(); i++)
494                 {
495                         ServerInstance->SE->DelFd(httpsocks[i]);
496                         httpsocks[i]->Close();
497                         delete httpsocks[i]->GetIndex();
498                 }
499                 ServerInstance->BufferedSocketCull();
500         }
501
502         virtual Version GetVersion()
503         {
504                 return Version(1,1,0,0,VF_VENDOR|VF_SERVICEPROVIDER,API_VERSION);
505         }
506 };
507
508 MODULE_INIT(ModuleHttpServer)