]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_httpd.cpp
Sync helpop chmodes s and p with docs
[user/henk/code/inspircd.git] / src / modules / m_httpd.cpp
index 3b8bc20b8567563167d108854172f3b6b8409631..f3ec3298bd8823b154b4c9fb5017159ba7bea800 100644 (file)
-/*       +------------------------------------+       
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
  *
- *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
- *                    E-mail:
- *             <brain@chatspike.net>
- *               <Craig@chatspike.net>
- *     
- * Written by Craig Edwards, Craig McLure, and others.
- * This program is free but copyrighted software; see
- *         the file COPYING for details.
+ *   Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com>
+ *   Copyright (C) 2018 edef <edef@edef.eu>
+ *   Copyright (C) 2013-2014, 2017-2020 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2012-2016 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
+ *   Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
+ *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007 John Brooks <special@inspircd.org>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2006, 2008, 2010 Craig Edwards <brain@inspircd.org>
  *
- * ---------------------------------------------------
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-using namespace std;
-
-#include <algorithm>
-#include "modules.h"
-#include "inspircd.h"
-#include "httpd.h"
-
-/* $ModDesc: Provides HTTP serving facilities to modules */
+/// $CompilerFlags: -Ivendor_directory("http_parser")
 
-class ModuleHttp;
 
-static ModuleHttp* HttpModule;
-static bool claimed;
+#include "inspircd.h"
+#include "iohook.h"
+#include "modules/httpd.h"
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+#endif
+
+// Fix warnings about the use of commas at end of enumerator lists and long long
+// on C++03.
+#if defined __clang__
+# pragma clang diagnostic ignored "-Wc++11-extensions"
+# pragma clang diagnostic ignored "-Wc++11-long-long"
+#elif defined __GNUC__
+# pragma GCC diagnostic ignored "-Wlong-long"
+# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))
+#  pragma GCC diagnostic ignored "-Wpedantic"
+# else
+#  pragma GCC diagnostic ignored "-pedantic"
+# endif
+#endif
+
+// Fix warnings about shadowing in http_parser.
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wshadow"
+#endif
+
+#include <http_parser.c>
+
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+class ModuleHttpServer;
+
+static ModuleHttpServer* HttpModule;
+static insp::intrusive_list<HttpServerSocket> sockets;
+static Events::ModuleEventProvider* aclevprov;
+static Events::ModuleEventProvider* reqevprov;
+static http_parser_settings parser_settings;
 
-/** HTTP socket states
+/** A socket used for HTTP transport
  */
-enum HttpState
+class HttpServerSocket : public BufferedSocket, public Timer, public insp::intrusive_list_node<HttpServerSocket>
 {
-       HTTP_LISTEN = 0,
-       HTTP_SERVE_WAIT_REQUEST = 1,
-       HTTP_SERVE_RECV_POSTDATA = 2,
-       HTTP_SERVE_SEND_DATA = 3
-};
+ private:
+       friend class ModuleHttpServer;
 
-class HttpSocket;
+       http_parser parser;
+       http_parser_url url;
+       std::string ip;
+       std::string uri;
+       HTTPHeaders headers;
+       std::string body;
+       size_t total_buffers;
+       int status_code;
 
-/** This class is used to handle HTTP socket timeouts
- */
-class HTTPTimeout : public InspTimer
-{
- private:
-       /** HttpSocket we are attached to
-        */
-       HttpSocket* s;
-       /** Socketengine the file descriptor is in
+       /** True if this object is in the cull list
         */
-       SocketEngine* SE;
- public:
-       /** Attach timeout to HttpSocket
-        */
-       HTTPTimeout(HttpSocket* sock, SocketEngine* engine);
-       /** Handle timer tick
-        */
-       void Tick(time_t TIME);
-};
+       bool waitingcull;
+       bool messagecomplete;
 
-/** A socket used for HTTP transport
- */
-class HttpSocket : public InspSocket
-{
-       FileReader* index;
-       HttpState InternalState;
-       std::stringstream headers;
-       std::string postdata;
-       std::string request_type;
-       std::string uri;
-       std::string http_version;
-       unsigned int postsize;
-       unsigned int amount;
-       HTTPTimeout* Timeout;
+       bool Tick(time_t currtime) CXX11_OVERRIDE
+       {
+               if (!messagecomplete)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP socket %d timed out", GetFd());
+                       Close();
+                       return false;
+               }
 
- public:
+               return true;
+       }
 
-       HttpSocket(InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, FileReader* index_page) : InspSocket(SI, host, port, listening, maxtime), index(index_page), postsize(0)
+       template<int (HttpServerSocket::*f)()>
+       static int Callback(http_parser* p)
        {
-               SI->Log(DEBUG,"HttpSocket constructor");
-               InternalState = HTTP_LISTEN;
-               Timeout = NULL;
+               HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data);
+               return (sock->*f)();
        }
 
-       HttpSocket(InspIRCd* SI, int newfd, char* ip, FileReader* ind) : InspSocket(SI, newfd, ip), index(ind), postsize(0)
+       template<int (HttpServerSocket::*f)(const char*, size_t)>
+       static int DataCallback(http_parser* p, const char* buf, size_t len)
        {
-               InternalState = HTTP_SERVE_WAIT_REQUEST;
-               Timeout = new HTTPTimeout(this, Instance->SE);
-               Instance->Timers->AddTimer(Timeout);
+               HttpServerSocket* sock = static_cast<HttpServerSocket*>(p->data);
+               return (sock->*f)(buf, len);
        }
 
-       ~HttpSocket()
+       static void ConfigureParser()
        {
-               if (Instance->Time() < Timeout->GetTimer())
-               {
-                       if (Timeout)
-                               Instance->Timers->DelTimer(Timeout);
-                       Timeout = NULL;
-               }
+               http_parser_settings_init(&parser_settings);
+               parser_settings.on_message_begin = Callback<&HttpServerSocket::OnMessageBegin>;
+               parser_settings.on_url = DataCallback<&HttpServerSocket::OnUrl>;
+               parser_settings.on_header_field = DataCallback<&HttpServerSocket::OnHeaderField>;
+               parser_settings.on_body = DataCallback<&HttpServerSocket::OnBody>;
+               parser_settings.on_message_complete = Callback<&HttpServerSocket::OnMessageComplete>;
+       }
+
+       int OnMessageBegin()
+       {
+               uri.clear();
+               header_state = HEADER_NONE;
+               body.clear();
+               total_buffers = 0;
+               return 0;
+       }
+
+       bool AcceptData(size_t len)
+       {
+               total_buffers += len;
+               return total_buffers < 8192;
        }
 
-       virtual int OnIncomingConnection(int newsock, char* ip)
+       int OnUrl(const char* buf, size_t len)
        {
-               if (InternalState == HTTP_LISTEN)
+               if (!AcceptData(len))
                {
-                       HttpSocket* s = new HttpSocket(this->Instance, newsock, ip, index);
-                       s = s; /* Stop GCC whining */
+                       status_code = HTTP_STATUS_URI_TOO_LONG;
+                       return -1;
                }
-               return true;
+               uri.append(buf, len);
+               return 0;
        }
 
-       virtual void OnClose()
+       enum { HEADER_NONE, HEADER_FIELD, HEADER_VALUE } header_state;
+       std::string header_field;
+       std::string header_value;
+
+       void OnHeaderComplete()
        {
+               headers.SetHeader(header_field, header_value);
+               header_field.clear();
+               header_value.clear();
        }
 
-       std::string Response(int response)
+       int OnHeaderField(const char* buf, size_t len)
        {
-               switch (response)
+               if (header_state == HEADER_VALUE)
+                       OnHeaderComplete();
+               header_state = HEADER_FIELD;
+               if (!AcceptData(len))
                {
-                       case 100:
-                               return "CONTINUE";
-                       case 101:
-                               return "SWITCHING PROTOCOLS";
-                       case 200:
-                               return "OK";
-                       case 201:
-                               return "CREATED";
-                       case 202:
-                               return "ACCEPTED";
-                       case 203:
-                               return "NON-AUTHORITATIVE INFORMATION";
-                       case 204:
-                               return "NO CONTENT";
-                       case 205:
-                               return "RESET CONTENT";
-                       case 206:
-                               return "PARTIAL CONTENT";
-                       case 300:
-                               return "MULTIPLE CHOICES";
-                       case 301:
-                               return "MOVED PERMENANTLY";
-                       case 302:
-                               return "FOUND";
-                       case 303:
-                               return "SEE OTHER";
-                       case 304:
-                               return "NOT MODIFIED";
-                       case 305:
-                               return "USE PROXY";
-                       case 307:
-                               return "TEMPORARY REDIRECT";
-                       case 400:
-                               return "BAD REQUEST";
-                       case 401:
-                               return "UNAUTHORIZED";
-                       case 402:
-                               return "PAYMENT REQUIRED";
-                       case 403:
-                               return "FORBIDDEN";
-                       case 404:
-                               return "NOT FOUND";
-                       case 405:
-                               return "METHOD NOT ALLOWED";
-                       case 406:
-                               return "NOT ACCEPTABLE";
-                       case 407:
-                               return "PROXY AUTHENTICATION REQUIRED";
-                       case 408:
-                               return "REQUEST TIMEOUT";
-                       case 409:
-                               return "CONFLICT";
-                       case 410:
-                               return "GONE";
-                       case 411:
-                               return "LENGTH REQUIRED";
-                       case 412:
-                               return "PRECONDITION FAILED";
-                       case 413:
-                               return "REQUEST ENTITY TOO LARGE";
-                       case 414:
-                               return "REQUEST-URI TOO LONG";
-                       case 415:
-                               return "UNSUPPORTED MEDIA TYPE";
-                       case 416:
-                               return "REQUESTED RANGE NOT SATISFIABLE";
-                       case 417:
-                               return "EXPECTATION FAILED";
-                       case 500:
-                               return "INTERNAL SERVER ERROR";
-                       case 501:
-                               return "NOT IMPLEMENTED";
-                       case 502:
-                               return "BAD GATEWAY";
-                       case 503:
-                               return "SERVICE UNAVAILABLE";
-                       case 504:
-                               return "GATEWAY TIMEOUT";
-                       case 505:
-                               return "HTTP VERSION NOT SUPPORTED";
-                       default:
-                               return "WTF";
-                       break;
-                               
+                       status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+                       return -1;
                }
+               header_field.append(buf, len);
+               return 0;
        }
 
-       void SendHeaders(unsigned long size, int response, const std::string &extraheaders)
+       int OnHeaderValue(const char* buf, size_t len)
        {
-               time_t local = this->Instance->Time();
-               struct tm *timeinfo = gmtime(&local);
-               this->Write("HTTP/1.1 "+ConvToStr(response)+" "+Response(response)+"\r\nDate: ");
-               this->Write(asctime(timeinfo));
-               if (extraheaders.empty())
+               header_state = HEADER_VALUE;
+               if (!AcceptData(len))
                {
-                       this->Write("Content-Type: text/html\r\n");
+                       status_code = HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+                       return -1;
                }
-               else
+               header_value.append(buf, len);
+               return 0;
+       }
+
+       int OnHeadersComplete()
+       {
+               if (header_state != HEADER_NONE)
+                       OnHeaderComplete();
+               return 0;
+       }
+
+       int OnBody(const char* buf, size_t len)
+       {
+               if (!AcceptData(len))
                {
-                       this->Write(extraheaders);
+                       status_code = HTTP_STATUS_PAYLOAD_TOO_LARGE;
+                       return -1;
                }
-               this->Write("Server: InspIRCd/m_httpd.so/1.1\r\nContent-Length: "+ConvToStr(size)+
-                               "\r\nConnection: close\r\n\r\n");
+               body.append(buf, len);
+               return 0;
        }
 
-       virtual bool OnDataReady()
+       int OnMessageComplete()
        {
-               char* data = this->Read();
+               messagecomplete = true;
+               ServeData();
+               return 0;
+       }
 
-               /* Check that the data read is a valid pointer and it has some content */
-               if (data && *data)
+ public:
+       HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server, unsigned int timeoutsec)
+               : BufferedSocket(newfd)
+               , Timer(timeoutsec)
+               , ip(IP)
+               , status_code(0)
+               , waitingcull(false)
+               , messagecomplete(false)
+       {
+               if ((!via->iohookprovs.empty()) && (via->iohookprovs.back()))
                {
-                       headers << data;
-
-                       if (headers.str().find("\r\n\r\n") != std::string::npos)
+                       via->iohookprovs.back()->OnAccept(this, client, server);
+                       // IOHook may have errored
+                       if (!getError().empty())
                        {
-                               if (request_type == "")
-                               {
-                                       headers >> request_type;
-                                       headers >> uri;
-                                       headers >> http_version;
-
-                                       std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
-                                       std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
-                               }
-
-                               if ((InternalState == HTTP_SERVE_WAIT_REQUEST) && (request_type == "POST"))
-                               {
-                                       /* Do we need to fetch postdata? */
-                                       postdata = "";
-                                       amount = 0;
-                                       InternalState = HTTP_SERVE_RECV_POSTDATA;
-                                       std::string header_item;
-                                       while (headers >> header_item)
-                                       {
-                                               if (header_item == "Content-Length:")
-                                               {
-                                                       headers >> header_item;
-                                                       postsize = atoi(header_item.c_str());
-                                               }
-                                       }
-                                       if (!postsize)
-                                       {
-                                               InternalState = HTTP_SERVE_SEND_DATA;
-                                               SendHeaders(0, 400, "");
-                                               Timeout = new HTTPTimeout(this, Instance->SE);
-                                               Instance->Timers->AddTimer(Timeout);
-                                       }
-                                       else
-                                       {
-                                               Instance->Log(DEBUG,"%d bytes to read for POST",postsize);
-                                               std::string::size_type x = headers.str().find("\r\n\r\n");
-                                               postdata = headers.str().substr(x+4, headers.str().length());
-                                               /* Get content length and store */
-                                               Instance->Log(DEBUG,"Initial postdata: '%s'", postdata.c_str());
-                                               if (postdata.length() >= postsize)
-                                                       ServeData();
-                                       }
-                               }
-                               else if (InternalState == HTTP_SERVE_RECV_POSTDATA)
-                               {
-                                       /* Add postdata, once we have it all, send the event */
-                                       amount += strlen(data);
-                                       postdata.append(data);
-                                       if (amount >= postsize)
-                                               ServeData();
-                               }
-                               else
-                               {
-                                       ServeData();
-                               }
-                               return true;
+                               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP socket %d encountered a hook error: %s",
+                                       GetFd(), getError().c_str());
+                               Close();
+                               return;
                        }
-                       return true;
-               }
-               else
-               {
-                       return false;
                }
+
+               parser.data = this;
+               http_parser_init(&parser, HTTP_REQUEST);
+               ServerInstance->Timers.AddTimer(this);
        }
 
-       void ServeData()
+       ~HttpServerSocket()
        {
-               /* Headers are complete */
-               InternalState = HTTP_SERVE_SEND_DATA;
+               sockets.erase(this);
+       }
 
-               Instance->Timers->DelTimer(Timeout);
-               Timeout = NULL;
-       
-               if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
-               {
-                       SendHeaders(0, 505, "");
-               }
+       void Close() CXX11_OVERRIDE
+       {
+               if (waitingcull || !HasFd())
+                       return;
+
+               waitingcull = true;
+               BufferedSocket::Close();
+               ServerInstance->GlobalCulls.AddItem(this);
+       }
+
+       void OnError(BufferedSocketError err) CXX11_OVERRIDE
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "HTTP socket %d encountered an error: %d - %s",
+                       GetFd(), err, getError().c_str());
+               Close();
+       }
+
+       void SendHTTPError(unsigned int response, const char* errstr = NULL)
+       {
+               if (!errstr)
+                       errstr = http_status_str((http_status)response);
+
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Sending HTTP error %u: %s", response, errstr);
+               static HTTPHeaders empty;
+               std::string data = InspIRCd::Format(
+                       "<html><head></head><body style='font-family: sans-serif; text-align: center'>"
+                       "<h1 style='font-size: 48pt'>Error %u</h1><h2 style='font-size: 24pt'>%s</h2><hr>"
+                       "<small>Powered by <a href='https://www.inspircd.org'>InspIRCd</a></small></body></html>",
+                       response, errstr);
+
+               Page(data, response, &empty);
+       }
+
+       void SendHeaders(unsigned long size, unsigned int response, HTTPHeaders &rheaders)
+       {
+               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, http_status_str((http_status)response)));
+
+               rheaders.CreateHeader("Date", InspIRCd::TimeString(ServerInstance->Time(), "%a, %d %b %Y %H:%M:%S GMT", true));
+               rheaders.CreateHeader("Server", INSPIRCD_BRANCH);
+               rheaders.SetHeader("Content-Length", ConvToStr(size));
+
+               if (size)
+                       rheaders.CreateHeader("Content-Type", "text/html");
                else
+                       rheaders.RemoveHeader("Content-Type");
+
+               /* Supporting Connection: keep-alive causes a whole world of hurt synchronizing timeouts,
+                * so remove it, its not essential for what we need.
+                */
+               rheaders.SetHeader("Connection", "Close");
+
+               WriteData(rheaders.GetFormattedHeaders());
+               WriteData("\r\n");
+       }
+
+       void OnDataReady() CXX11_OVERRIDE
+       {
+               if (parser.upgrade || HTTP_PARSER_ERRNO(&parser))
+                       return;
+               http_parser_execute(&parser, &parser_settings, recvq.data(), recvq.size());
+               if (parser.upgrade)
+                       SendHTTPError(status_code ? status_code : 400);
+               else if (HTTP_PARSER_ERRNO(&parser))
+                       SendHTTPError(status_code ? status_code : 400, http_errno_description((http_errno)parser.http_errno));
+       }
+
+       void ServeData()
+       {
+               ModResult MOD_RESULT;
+               std::string method = http_method_str(static_cast<http_method>(parser.method));
+               HTTPRequestURI parsed;
+               ParseURI(uri, parsed);
+               HTTPRequest acl(method, parsed, &headers, this, ip, body);
+               FIRST_MOD_RESULT_CUSTOM(*aclevprov, HTTPACLEventListener, OnHTTPACLCheck, MOD_RESULT, (acl));
+               if (MOD_RESULT != MOD_RES_DENY)
                {
-                       if ((request_type == "GET") && (uri == "/"))
+                       HTTPRequest request(method, parsed, &headers, this, ip, body);
+                       FIRST_MOD_RESULT_CUSTOM(*reqevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request));
+                       if (MOD_RESULT == MOD_RES_PASSTHRU)
                        {
-                               SendHeaders(index->ContentSize(), 200, "");
-                               this->Write(index->Contents());
-                       }
-                       else
-                       {
-                               claimed = false;
-                               HTTPRequest httpr(request_type,uri,&headers,this,this->GetIP(),postdata);
-                               Event e((char*)&httpr, (Module*)HttpModule, "httpd_url");
-                               e.Send(this->Instance);
-                               if (!claimed)
-                               {
-                                       SendHeaders(0, 404, "");
-                                       Instance->Log(DEBUG,"Page not claimed, 404");
-                               }
+                               SendHTTPError(404);
                        }
                }
-               Timeout = new HTTPTimeout(this, Instance->SE);
-               Instance->Timers->AddTimer(Timeout);
        }
 
-       void Page(std::stringstream* n, int response, std::string& extraheaders)
+       void Page(const std::string& s, unsigned int response, HTTPHeaders* hheaders)
        {
-               Instance->Log(DEBUG,"Sending page");
-               SendHeaders(n->str().length(), response, extraheaders);
-               this->Write(n->str());
+               SendHeaders(s.length(), response, *hheaders);
+               WriteData(s);
+               BufferedSocket::Close(true);
        }
-};
 
-HTTPTimeout::HTTPTimeout(HttpSocket* sock, SocketEngine* engine) : InspTimer(60, time(NULL)), s(sock), SE(engine)
-{
-}
+       void Page(std::stringstream* n, unsigned int response, HTTPHeaders* hheaders)
+       {
+               Page(n->str(), response, hheaders);
+       }
 
-void HTTPTimeout::Tick(time_t TIME)
-{
-       SE->DelFd(s);
-       s->Close();
-       delete s;
-}
+       bool ParseURI(const std::string& uristr, HTTPRequestURI& out)
+       {
+               http_parser_url_init(&url);
+               if (http_parser_parse_url(uristr.c_str(), uristr.size(), 0, &url) != 0)
+                       return false;
 
-class ModuleHttp : public Module
-{
-       int port;
-       std::string host;
-       std::string bindip;
-       std::string indexfile;
+               if (url.field_set & (1 << UF_PATH))
+               {
+                       // Normalise the path.
+                       std::vector<std::string> pathsegments;
+                       irc::sepstream pathstream(uri.substr(url.field_data[UF_PATH].off, url.field_data[UF_PATH].len), '/');
+                       for (std::string pathsegment; pathstream.GetToken(pathsegment); )
+                       {
+                               if (pathsegment == ".")
+                               {
+                                       // Stay at the current level.
+                                       continue;
+                               }
 
-       FileReader* index;
+                               if (pathsegment == "..")
+                               {
+                                       // Traverse up to the previous level.
+                                       if (!pathsegments.empty())
+                                               pathsegments.pop_back();
+                                       continue;
+                               }
 
-       HttpSocket* http;
+                               pathsegments.push_back(pathsegment);
+                       }
 
- public:
+                       out.path.reserve(url.field_data[UF_PATH].len);
+                       out.path.append("/").append(stdalgo::string::join(pathsegments, '/'));
+               }
 
-       void ReadConfig()
-       {
-               ConfigReader c(ServerInstance);
-               this->host = c.ReadValue("http", "host", 0);
-               this->bindip = c.ReadValue("http", "ip", 0);
-               this->port = c.ReadInteger("http", "port", 0, true);
-               this->indexfile = c.ReadValue("http", "index", 0);
+               if (url.field_set & (1 << UF_FRAGMENT))
+                       out.fragment = uri.substr(url.field_data[UF_FRAGMENT].off, url.field_data[UF_FRAGMENT].len);
+
+               std::string param_str;
+               if (url.field_set & (1 << UF_QUERY))
+                       param_str = uri.substr(url.field_data[UF_QUERY].off, url.field_data[UF_QUERY].len);
 
-               if (index)
+               irc::sepstream param_stream(param_str, '&');
+               std::string token;
+               std::string::size_type eq_pos;
+               while (param_stream.GetToken(token))
                {
-                       delete index;
-                       index = NULL;
+                       eq_pos = token.find('=');
+                       if (eq_pos == std::string::npos)
+                       {
+                               out.query_params.insert(std::make_pair(token, ""));
+                       }
+                       else
+                       {
+                               out.query_params.insert(std::make_pair(token.substr(0, eq_pos), token.substr(eq_pos + 1)));
+                       }
                }
-               index = new FileReader(ServerInstance, this->indexfile);
+               return true;
        }
+};
 
-       void CreateListener()
+class HTTPdAPIImpl : public HTTPdAPIBase
+{
+ public:
+       HTTPdAPIImpl(Module* parent)
+               : HTTPdAPIBase(parent)
        {
-               http = new HttpSocket(ServerInstance, this->bindip, this->port, true, 0, index);
        }
 
-       ModuleHttp(InspIRCd* Me) : Module::Module(Me)
+       void SendResponse(HTTPDocumentResponse& resp) CXX11_OVERRIDE
        {
-               index = NULL;
-               ReadConfig();
-               CreateListener();
+               resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
        }
+};
 
-       void OnEvent(Event* event)
-       {
-       }
+class ModuleHttpServer : public Module
+{
+       HTTPdAPIImpl APIImpl;
+       unsigned int timeoutsec;
+       Events::ModuleEventProvider acleventprov;
+       Events::ModuleEventProvider reqeventprov;
 
-       char* OnRequest(Request* request)
+ public:
+       ModuleHttpServer()
+               : APIImpl(this)
+               , acleventprov(this, "event/http-acl")
+               , reqeventprov(this, "event/http-request")
        {
-               ServerInstance->Log(DEBUG,"Got HTTPDocument object");
-               claimed = true;
-               HTTPDocument* doc = (HTTPDocument*)request->GetData();
-               HttpSocket* sock = (HttpSocket*)doc->sock;
-               sock->Page(doc->GetDocument(), doc->GetResponseCode(), doc->GetExtraHeaders());
-               return NULL;
+               aclevprov = &acleventprov;
+               reqevprov = &reqeventprov;
+               HttpServerSocket::ConfigureParser();
        }
 
-       void Implements(char* List)
+       void init() CXX11_OVERRIDE
        {
-               List[I_OnEvent] = List[I_OnRequest] = 1;
+               HttpModule = this;
        }
 
-       virtual ~ModuleHttp()
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
        {
-               ServerInstance->SE->DelFd(http);
+               ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
+               timeoutsec = tag->getDuration("timeout", 10, 1);
        }
 
-       virtual Version GetVersion()
+       ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE
        {
-               return Version(1,0,0,0,VF_STATIC|VF_VENDOR|VF_SERVICEPROVIDER);
-       }
-};
+               if (!stdalgo::string::equalsci(from->bind_tag->getString("type"), "httpd"))
+                       return MOD_RES_PASSTHRU;
 
+               sockets.push_front(new HttpServerSocket(nfd, client->addr(), from, client, server, timeoutsec));
+               return MOD_RES_ALLOW;
+       }
 
-class ModuleHttpFactory : public ModuleFactory
-{
- public:
-       ModuleHttpFactory()
+       void OnUnloadModule(Module* mod) CXX11_OVERRIDE
        {
+               for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); )
+               {
+                       HttpServerSocket* sock = *i;
+                       ++i;
+                       if (sock->GetModHook(mod))
+                       {
+                               sock->cull();
+                               delete sock;
+                       }
+               }
        }
-       
-       ~ModuleHttpFactory()
+
+       CullResult cull() CXX11_OVERRIDE
        {
+               for (insp::intrusive_list<HttpServerSocket>::const_iterator i = sockets.begin(); i != sockets.end(); ++i)
+               {
+                       HttpServerSocket* sock = *i;
+                       sock->Close();
+               }
+               return Module::cull();
        }
-       
-       virtual Module * CreateModule(InspIRCd* Me)
+
+       Version GetVersion() CXX11_OVERRIDE
        {
-               HttpModule = new ModuleHttp(Me);
-               return HttpModule;
+               return Version("Allows the server administrator to serve various useful resources over HTTP.", VF_VENDOR);
        }
 };
 
-
-extern "C" void * init_module( void )
-{
-       return new ModuleHttpFactory;
-}
+MODULE_INIT(ModuleHttpServer)