]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
3a0d4f86102fffc121d29624c37344456b251ed3
[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 /// $CompilerFlags: -Ivendor_directory("http_parser")
25
26
27 #include "inspircd.h"
28 #include "iohook.h"
29 #include "modules/httpd.h"
30
31 // Fix warnings about the use of commas at end of enumerator lists on C++03.
32 #if defined __clang__
33 # pragma clang diagnostic ignored "-Wc++11-extensions"
34 #elif defined __GNUC__
35 # if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))
36 #  pragma GCC diagnostic ignored "-Wpedantic"
37 # else
38 #  pragma GCC diagnostic ignored "-pedantic"
39 # endif
40 #endif
41
42 // Fix warnings about shadowing in http_parser.
43 #pragma GCC diagnostic ignored "-Wshadow"
44
45 #include <http_parser.c>
46
47 class ModuleHttpServer;
48
49 static ModuleHttpServer* HttpModule;
50 static insp::intrusive_list<HttpServerSocket> sockets;
51 static Events::ModuleEventProvider* aclevprov;
52 static Events::ModuleEventProvider* reqevprov;
53 static http_parser_settings parser_settings;
54
55 /** A socket used for HTTP transport
56  */
57 class HttpServerSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<HttpServerSocket>
58 {
59         friend ModuleHttpServer;
60
61         http_parser parser;
62         std::string ip;
63         std::string uri;
64         HTTPHeaders headers;
65         std::string body;
66         size_t total_buffers;
67         int status_code;
68
69         /** True if this object is in the cull list
70          */
71         bool waitingcull;
72
73         bool Tick(time_t currtime) CXX11_OVERRIDE
74         {
75                 AddToCull();
76                 return false;
77         }
78
79         template<int (HttpServerSocket::*f)()>
80         static int Callback(http_parser* p)
81         {
82                 HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data);
83                 return (sock->*f)();
84         }
85
86         template<int (HttpServerSocket::*f)(const char*, size_t)>
87         static int DataCallback(http_parser* p, const char* buf, size_t len)
88         {
89                 HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data);
90                 return (sock->*f)(buf, len);
91         }
92
93         static void ConfigureParser()
94         {
95                 http_parser_settings_init(&parser_settings);
96                 parser_settings.on_message_begin = Callback<&HttpServerSocket::OnMessageBegin>;
97                 parser_settings.on_url = DataCallback<&HttpServerSocket::OnUrl>;
98                 parser_settings.on_header_field = DataCallback<&HttpServerSocket::OnHeaderField>;
99                 parser_settings.on_body = DataCallback<&HttpServerSocket::OnBody>;
100                 parser_settings.on_message_complete = Callback<&HttpServerSocket::OnMessageComplete>;
101         }
102
103         int OnMessageBegin()
104         {
105                 uri.clear();
106                 header_state = HEADER_NONE;
107                 body.clear();
108                 total_buffers = 0;
109                 return 0;
110         }
111
112         bool AcceptData(size_t len)
113         {
114                 total_buffers += len;
115                 return total_buffers < 8192;
116         }
117
118         int OnUrl(const char* buf, size_t len)
119         {
120                 if (!AcceptData(len))
121                 {
122                         status_code = HTTP_STATUS_URI_TOO_LONG;
123                         return -1;
124                 }
125                 uri.append(buf, len);
126                 return 0;
127         }
128
129         enum { HEADER_NONE, HEADER_FIELD, HEADER_VALUE } header_state;
130         std::string header_field;
131         std::string header_value;
132
133         void OnHeaderComplete()
134         {
135                 headers.SetHeader(header_field, header_value);
136                 header_field.clear();
137                 header_value.clear();
138         }
139
140         int OnHeaderField(const char* buf, size_t len)
141         {
142                 if (header_state == HEADER_VALUE)
143                         OnHeaderComplete();
144                 header_state = HEADER_FIELD;
145                 if (!AcceptData(len))
146                 {
147                         status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
148                         return -1;
149                 }
150                 header_field.append(buf, len);
151                 return 0;
152         }
153
154         int OnHeaderValue(const char* buf, size_t len)
155         {
156                 header_state = HEADER_VALUE;
157                 if (!AcceptData(len))
158                 {
159                         status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
160                         return -1;
161                 }
162                 header_value.append(buf, len);
163                 return 0;
164         }
165
166         int OnHeadersComplete()
167         {
168                 if (header_state != HEADER_NONE)
169                         OnHeaderComplete();
170                 return 0;
171         }
172
173         int OnBody(const char* buf, size_t len)
174         {
175                 if (!AcceptData(len))
176                 {
177                         status_code = HTTP_STATUS_PAYLOAD_TOO_LARGE;
178                         return -1;
179                 }
180                 body.append(buf, len);
181                 return 0;
182         }
183
184         int OnMessageComplete()
185         {
186                 ServeData();
187                 return 0;
188         }
189
190  public:
191         HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server, unsigned int timeoutsec)
192                 : BufferedSocket(newfd)
193                 , Timer(timeoutsec)
194                 , ip(IP)
195                 , status_code(0)
196                 , waitingcull(false)
197         {
198                 if ((!via->iohookprovs.empty()) && (via->iohookprovs.back()))
199                 {
200                         via->iohookprovs.back()->OnAccept(this, client, server);
201                         // IOHook may have errored
202                         if (!getError().empty())
203                         {
204                                 AddToCull();
205                                 return;
206                         }
207                 }
208
209                 parser.data = this;
210                 http_parser_init(&parser, HTTP_REQUEST);
211                 ServerInstance->Timers.AddTimer(this);
212         }
213
214         ~HttpServerSocket()
215         {
216                 sockets.erase(this);
217         }
218
219         void OnError(BufferedSocketError) CXX11_OVERRIDE
220         {
221                 AddToCull();
222         }
223
224         const char* Response(unsigned int response)
225         {
226                 switch (response)
227                 {
228 #define HTTP_STATUS_CASE(n, m, s) case n: return #s;
229                         HTTP_STATUS_MAP(HTTP_STATUS_CASE)
230                         default:
231                                 return "WTF";
232                         break;
233                 }
234         }
235
236         void SendHTTPError(unsigned int response)
237         {
238                 HTTPHeaders empty;
239                 std::string data = InspIRCd::Format(
240                         "<html><head></head><body>Server error %u: %s<br>"
241                         "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>", response, Response(response));
242
243                 SendHeaders(data.length(), response, empty);
244                 WriteData(data);
245                 Close();
246         }
247
248         void SendHeaders(unsigned long size, unsigned int response, HTTPHeaders &rheaders)
249         {
250                 WriteData(InspIRCd::Format("HTTP/%u.%u %u %s\r\n", parser.http_major ? parser.http_major : 1, parser.http_major ? parser.http_minor : 1, response, Response(response)));
251
252                 rheaders.CreateHeader("Date", InspIRCd::TimeString(ServerInstance->Time(), "%a, %d %b %Y %H:%M:%S GMT", true));
253                 rheaders.CreateHeader("Server", INSPIRCD_BRANCH);
254                 rheaders.SetHeader("Content-Length", ConvToStr(size));
255
256                 if (size)
257                         rheaders.CreateHeader("Content-Type", "text/html");
258                 else
259                         rheaders.RemoveHeader("Content-Type");
260
261                 /* Supporting Connection: keep-alive causes a whole world of hurt syncronizing timeouts,
262                  * so remove it, its not essential for what we need.
263                  */
264                 rheaders.SetHeader("Connection", "Close");
265
266                 WriteData(rheaders.GetFormattedHeaders());
267                 WriteData("\r\n");
268         }
269
270         void OnDataReady() CXX11_OVERRIDE
271         {
272                 if (parser.upgrade || HTTP_PARSER_ERRNO(&parser))
273                         return;
274                 http_parser_execute(&parser, &parser_settings, recvq.data(), recvq.size());
275                 if (parser.upgrade || HTTP_PARSER_ERRNO(&parser))
276                         SendHTTPError(status_code ? status_code : 400);
277         }
278
279         void ServeData()
280         {
281                 ModResult MOD_RESULT;
282                 std::string method = http_method_str(static_cast<http_method>(parser.method));
283                 HTTPRequest acl(method, uri, &headers, this, ip, body);
284                 FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
285                 if (MOD_RESULT != MOD_RES_DENY)
286                 {
287                         HTTPRequest url(method, uri, &headers, this, ip, body);
288                         FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (url));
289                         if (MOD_RESULT == MOD_RES_PASSTHRU)
290                         {
291                                 SendHTTPError(404);
292                         }
293                 }
294         }
295
296         void Page(std::stringstream* n, unsigned int response, HTTPHeaders* hheaders)
297         {
298                 SendHeaders(n->str().length(), response, *hheaders);
299                 WriteData(n->str());
300                 Close();
301         }
302
303         void AddToCull()
304         {
305                 if (waitingcull)
306                         return;
307
308                 waitingcull = true;
309                 Close();
310                 ServerInstance->GlobalCulls.AddItem(this);
311         }
312 };
313
314 class HTTPdAPIImpl : public HTTPdAPIBase
315 {
316  public:
317         HTTPdAPIImpl(Module* parent)
318                 : HTTPdAPIBase(parent)
319         {
320         }
321
322         void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
323         {
324                 resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
325         }
326 };
327
328 class ModuleHttpServer : public Module
329 {
330         HTTPdAPIImpl APIImpl;
331         unsigned int timeoutsec;
332         Events::ModuleEventProvider acleventprov;
333         Events::ModuleEventProvider reqeventprov;
334
335  public:
336         ModuleHttpServer()
337                 : APIImpl(this)
338                 , acleventprov(this, "event/http-acl")
339                 , reqeventprov(this, "event/http-request")
340         {
341                 aclevprov = &acleventprov;
342                 reqevprov = &reqeventprov;
343                 HttpServerSocket::ConfigureParser();
344         }
345
346         void init() CXX11_OVERRIDE
347         {
348                 HttpModule = this;
349         }
350
351         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
352         {
353                 ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
354                 timeoutsec = tag->getDuration("timeout", 10, 1);
355         }
356
357         ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
358         {
359                 if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "httpd"))
360                         return MOD_RES_PASSTHRU;
361
362                 sockets.push_front(new HttpServerSocket(nfd, client->addr(), from, client, server, timeoutsec));
363                 return MOD_RES_ALLOW;
364         }
365
366         void OnUnloadModule(Module* mod) CXX11_OVERRIDE
367         {
368                 for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); )
369                 {
370                         HttpServerSocket* sock = *i;
371                         ++i;
372                         if (sock->GetModHook(mod))
373                         {
374                                 sock->cull();
375                                 delete sock;
376                         }
377                 }
378         }
379
380         CullResult cull() CXX11_OVERRIDE
381         {
382                 for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
383                 {
384                         HttpServerSocket* sock = *i;
385                         sock->AddToCull();
386                 }
387                 return Module::cull();
388         }
389
390         Version GetVersion() CXX11_OVERRIDE
391         {
392                 return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
393         }
394 };
395
396 MODULE_INIT(ModuleHttpServer)