diff options
| -rw-r--r-- | src/HttpServer.cc | 182 | 
1 files changed, 105 insertions, 77 deletions
| diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 7df6406..3526a2a 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -2,6 +2,8 @@  #include "HttpServer.h"  #include "Socket.h"  #include "BStream.h" +#include "SimpleMustache.h" +#include "Main.h"  class OutputCheck : public Balau::Handle {    public: @@ -25,7 +27,6 @@ class OutputCheck : public Balau::Handle {  };  static const ev_tstamp s_httpTimeout = 5; -#define DAEMON_NAME "Balau/1.0"  namespace Balau { @@ -33,14 +34,18 @@ class HttpWorker : public Task {    public:        HttpWorker(IO<Handle> io, void * server);        ~HttpWorker(); +    static void buildErrorTemplate(IO<Handle> h) { m_errorTemplate.setTemplate(h); } +    static void buildErrorTemplate(const char * str, ssize_t s) { m_errorTemplate.setTemplate(str, s); } +    static void buildErrorTemplate(const String & str) { m_errorTemplate.setTemplate(str); }    private:      virtual void Do();      virtual const char * getName();      bool handleClient(); -    void send400(); -    void send404(); -    void send500(const char * msg); +    void sendError(int error, const char * msg, bool closeConnection, std::vector<String> trace); +    void send400() { std::vector<String> d2; sendError(400, "The HTTP request you've sent is invalid", true, d2); } +    void send404() { std::vector<String> d2; sendError(404, "The HTTP request you've sent didn't match any action on this server.", false, d2); } +    void send500(const char * msg, std::vector<String> trace) { String smsg; smsg.set("The HTTP request you've sent triggered an internal error: `%s'", msg); sendError(500, smsg.to_charp(), true, trace); }      String httpUnescape(const char * in);      void readVariables(Http::StringMap & variables, char * str); @@ -48,13 +53,50 @@ class HttpWorker : public Task {      IO<BStream> m_strm;      String m_name;      HttpServer * m_server; +    String m_serverName; +    static SimpleMustache m_errorTemplate;  };  }; +Balau::SimpleMustache Balau::HttpWorker::m_errorTemplate; + +class SetDefaultTemplate : public Balau::AtStart { +  public: +      SetDefaultTemplate() : AtStart(0) { } +    virtual void doStart(); +    static const Balau::String m_defaultErrorTemplate; +}; + +static SetDefaultTemplate setDefaultTemplate; + +const Balau::String SetDefaultTemplate::m_defaultErrorTemplate( +"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" +"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" +"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +"  <head>\n" +"    <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n" +"    <title>{{title}}</title>\n" +"  </head>\n" +"\n" +"  <body>\n" +"    <h1>{{msg}}</h1>\n" +"{{#hasTrace}}\n" +"    <br /><h2>Context:</h2>\n" +"    {{#trace}}<pre>{{line}}</pre>{{/trace}}<br />\n" +"{{/hasTrace}}\n" +"  </body>\n" +"</html>\n" +); + +void SetDefaultTemplate::doStart() { +    Balau::HttpWorker::buildErrorTemplate(m_defaultErrorTemplate); +} +  Balau::HttpWorker::HttpWorker(IO<Handle> io, void * _server) : m_socket(new WriteOnly(io)), m_strm(new BStream(io)) {      m_server = (HttpServer *) _server;      m_name.set("HttpWorker(%s)", m_socket->getName()); +    m_serverName = "Balau/1.0";      // get stuff from server, such as port number, root document, base URL, default 400/404 actions, etc...  } @@ -112,80 +154,64 @@ void Balau::HttpWorker::readVariables(Http::StringMap & variables, char * str) {      } while (ampPos);  } -void Balau::HttpWorker::send400() { -    static const char str[] = -"HTTP/1.0 400 Bad Request\r\n" -"Content-Type: text/html; charset=UTF-8\r\n" -"Connection: close\r\n" -"Server: " DAEMON_NAME "\r\n" -"\r\n" -"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" -"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" -"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" -"  <head>\n" -"    <title>Bad Request</title>\n" -"  </head>\n" -"\n" -"  <body>\n" -"    The HTTP request you've sent is invalid.\n" -"  </body>\n" -"</html>\n"; - -    if (!m_socket->isClosed()) -        m_socket->forceWrite(str, sizeof(str) - 1); -    Balau::Printer::elog(Balau::E_HTTPSERVER, "%s had an invalid request", m_name.to_charp()); +static const char * getErrorMsg(int httpError) { +    switch (httpError) { +    case 400: +        return "Bad Request"; +        break; +    case 403: +        return "Forbidden"; +        break; +    case 404: +        return "Not Found"; +        break; +    case 500: +    default: +        return "Internal Error"; +        break; +    }  } -void Balau::HttpWorker::send404() { -    static const char str[] = -"HTTP/1.1 404 Not Found\r\n" +void Balau::HttpWorker::sendError(int error, const char * msg, bool closeConnection, std::vector<String> trace) { +    SimpleMustache * tpl = &m_errorTemplate; +    const char * errorMsg = getErrorMsg(error); +    Printer::elog(Balau::E_HTTPSERVER, "%s caused a %i error (%s)", m_name.to_charp(), error, errorMsg); +    SimpleMustache::Context ctx; +    String title; +    title.set("Error %i - %s", error, errorMsg); +    ctx["title"] = title; +    ctx["hasTrace"] = !trace.empty(); +    ctx["msg"] = msg; +    if (m_socket->isClosed()) return; +    for (std::vector<String>::iterator i = trace.begin(); i != trace.end(); i++) +        ctx["trace"][(ssize_t) 0]["line"] = *i; +    if (closeConnection) { +        String headers; +        headers.set( +"HTTP/1.0 %i %s\r\n"  "Content-Type: text/html; charset=UTF-8\r\n" -"Server: " DAEMON_NAME "\r\n" -"\r\n" -"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" -"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" -"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" -"  <head>\n" -"    <title>404 Not Found</title>\n" -"  </head>\n" -"\n" -"  <body>\n" -"    The HTTP request you've sent didn't match any action on this server.\n" -"  </body>\n" -"</html>\n"; - -    if (!m_socket->isClosed()) -        m_socket->forceWrite(str, sizeof(str) - 1); -    Balau::Printer::elog(Balau::E_HTTPSERVER, "%s had an invalid request", m_name.to_charp()); -} - -void Balau::HttpWorker::send500(const char * msg) { -    static const char str[] = -"HTTP/1.1 500 Not Found\r\n" +"Connection: close\r\n" +"Server: %s\r\n" +"\r\n", error, errorMsg, m_serverName.to_charp()); +        m_socket->forceWrite(headers); +        if (m_socket->isClosed()) return; +        tpl->render(m_socket, &ctx); +    } else { +        IO<Buffer> errorText(new Buffer); +        tpl->render(errorText, &ctx); +        off_t length = errorText->getSize(); +        String headers; +        headers.set( +"HTTP/1.1 %i %s\r\n"  "Content-Type: text/html; charset=UTF-8\r\n" -"Server: " DAEMON_NAME "\r\n" -"\r\n" -"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" -"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" -"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" -"  <head>\n" -"    <title>500 Internal Error</title>\n" -"  </head>\n" -"\n" -"  <body>\n" -"    The HTTP request you've sent triggered an internal error: `"; -    static const char str2[] = -"'.\n" -"  </body>\n" -"</html>\n"; - -    if (!m_socket->isClosed()) -        m_socket->forceWrite(str, sizeof(str) - 1); -    if (!m_socket->isClosed()) -        m_socket->forceWrite(msg, strlen(msg)); -    if (!m_socket->isClosed()) -        m_socket->forceWrite(str2, sizeof(str2) - 1); -    Balau::Printer::elog(Balau::E_HTTPSERVER, "%s had an invalid request", m_name.to_charp()); +"Connection: keep-alive\r\n" +"Server: %s\r\n" +"Content-Length: %i\r\n" +"\r\n", error, errorMsg, m_serverName.to_charp(), length); +        m_socket->forceWrite(headers); +        if (m_socket->isClosed()) return; +        m_socket->forceWrite(errorText->getBuffer(), length); +    }  }  bool Balau::HttpWorker::handleClient() { @@ -449,13 +475,15 @@ bool Balau::HttpWorker::handleClient() {              for (std::vector<String>::iterator i = trace.begin(); i != trace.end(); i++)                   Printer::log(M_DEBUG, "%s", i->to_charp());              if (!out->wrote()) -                send500(e.getMsg()); +                send500(e.getMsg(), e.getTrace());              return false;          }          catch (...) {              Printer::log(M_ERROR, "%s got an unknow exception while processing its request: `%s'", m_name.to_charp()); -            if (!out->wrote()) -                send500("unknow exception"); +            if (!out->wrote()) { +                std::vector<String> d; +                send500("unknow exception", d); +            }              return false;          }      } else { | 
