summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/HttpServer.cc182
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 {