From e5cfc02faa8994b8eae51590cda626a26fa9da24 Mon Sep 17 00:00:00 2001 From: Pixel Date: Tue, 29 Nov 2011 18:08:42 -0800 Subject: Now using SimpleMustache to render the error messages. --- src/HttpServer.cc | 182 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 105 insertions(+), 77 deletions(-) (limited to 'src') 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 io, void * server); ~HttpWorker(); + static void buildErrorTemplate(IO 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 trace); + void send400() { std::vector d2; sendError(400, "The HTTP request you've sent is invalid", true, d2); } + void send404() { std::vector 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 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 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( +"\n" +"\n" +" \n" +" \n" +" {{title}}\n" +" \n" +"\n" +" \n" +"

{{msg}}

\n" +"{{#hasTrace}}\n" +"

Context:

\n" +" {{#trace}}
{{line}}
{{/trace}}
\n" +"{{/hasTrace}}\n" +" \n" +"\n" +); + +void SetDefaultTemplate::doStart() { + Balau::HttpWorker::buildErrorTemplate(m_defaultErrorTemplate); +} + Balau::HttpWorker::HttpWorker(IO 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" -"\n" -"\n" -" \n" -" Bad Request\n" -" \n" -"\n" -" \n" -" The HTTP request you've sent is invalid.\n" -" \n" -"\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 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::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" -"\n" -"\n" -" \n" -" 404 Not Found\n" -" \n" -"\n" -" \n" -" The HTTP request you've sent didn't match any action on this server.\n" -" \n" -"\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 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" -"\n" -"\n" -" \n" -" 500 Internal Error\n" -" \n" -"\n" -" \n" -" The HTTP request you've sent triggered an internal error: `"; - static const char str2[] = -"'.\n" -" \n" -"\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::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 d; + send500("unknow exception", d); + } return false; } } else { -- cgit v1.2.3