From 30c37eb549c17b29a8131598cc06aeaf374896f5 Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Tue, 28 Feb 2012 04:57:59 +0100 Subject: Having a slightly better approach for writing Http responses. --- includes/Http.h | 4 ++-- includes/HttpServer.h | 24 ++++++++++++++++++++++++ src/HttpServer.cc | 45 +++++++++++++++++++++++++++++++++++++++++---- tests/test-Http.cc | 12 ++++-------- 4 files changed, 71 insertions(+), 14 deletions(-) diff --git a/includes/Http.h b/includes/Http.h index 8b0e313..62f8786 100644 --- a/includes/Http.h +++ b/includes/Http.h @@ -13,8 +13,7 @@ const char * getStatusMsg(int httpStatus); typedef std::map StringMap; typedef std::map > FileList; -class Request { - public: +struct Request { int method; String host; String uri; @@ -22,6 +21,7 @@ class Request { StringMap headers; FileList files; bool persistent; + String version; }; enum { diff --git a/includes/HttpServer.h b/includes/HttpServer.h index 37353ac..7dbf912 100644 --- a/includes/HttpServer.h +++ b/includes/HttpServer.h @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace Balau { @@ -18,6 +19,28 @@ class HttpWorker; class HttpServer { public: + class Response { + public: + Response(HttpServer * server, Http::Request req, IO out) : m_server(server), m_req(req), m_out(out), m_buffer(new Buffer()), m_responseCode(200), m_type("text/html; charset=UTF-8"), m_flushed(false) { } + void SetResponseCode(int code) { m_responseCode = code; } + void SetContentType(const String & type) { m_type = type; } + IO get() { return m_buffer; } + IO operator->() { return m_buffer; } + void Flush(); + void AddHeader(const String & line) { m_extraHeaders.push_front(line); } + void AddHeader(const String & key, const String & val) { AddHeader(key + ": " + val); } + private: + HttpServer * m_server; + Http::Request m_req; + IO m_out; + + IO m_buffer; + int m_responseCode; + String m_type; + std::list m_extraHeaders; + bool m_flushed; + }; + class Action { public: Action(const Regex & regex, const Regex & host = Regexes::any) : m_regex(regex), m_host(host), m_refCount(0) { } @@ -48,6 +71,7 @@ class HttpServer { Action::ActionMatch matches; }; ActionFound findAction(const char * uri, const char * host); + String getServerName() { return "Balau/1.0"; } private: bool m_started; void * m_listenerPtr; diff --git a/src/HttpServer.cc b/src/HttpServer.cc index a00160f..c596005 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -74,7 +74,6 @@ class HttpWorker : public Task { IO m_strm; String m_name; HttpServer * m_server; - String m_serverName; static SimpleMustache m_errorTemplate; }; @@ -122,7 +121,6 @@ void SetDefaultTemplate::doStart() { 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... } @@ -204,7 +202,7 @@ void Balau::HttpWorker::sendError(int error, const char * msg, const char * deta "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n" "Server: %s\r\n", - error, errorMsg, m_serverName.to_charp()); + error, errorMsg, m_server->getServerName().to_charp()); for (std::vector::iterator i = extraHeaders.begin(); i != extraHeaders.end(); i++) headers += *i + "\r\n"; headers += "\r\n"; @@ -222,7 +220,7 @@ void Balau::HttpWorker::sendError(int error, const char * msg, const char * deta "Connection: keep-alive\r\n" "Server: %s\r\n" "Content-Length: %lli\r\n", - error, errorMsg, m_serverName.to_charp(), length); + error, errorMsg, m_server->getServerName().to_charp(), length); for (std::vector::iterator i = extraHeaders.begin(); i != extraHeaders.end(); i++) headers += *i + "\r\n"; headers += "\r\n"; @@ -550,6 +548,7 @@ bool Balau::HttpWorker::handleClient() { req.headers = httpHeaders; req.files = files; req.persistent = persistent; + req.version = httpVersion; try { if (!f.action->Do(m_server, req, f.matches, out)) persistent = false; @@ -658,3 +657,41 @@ Balau::HttpServer::ActionFound Balau::HttpServer::findAction(const char * uri, c return r; } + +void Balau::HttpServer::Response::Flush() { + AAssert(!m_flushed, "HttpResponse already flushed."); + + m_flushed = true; + IO headers(new Buffer()); + + headers->writeString("HTTP/"); + headers->writeString(m_req.version); + headers->writeString(" "); + String response(m_responseCode); + headers->writeString(response); + headers->writeString(" "); + headers->writeString(Http::getStatusMsg(m_responseCode), -1); + headers->writeString("\r\nContent-Type: "); + headers->writeString(m_type); + headers->writeString("\r\nContent-Length: "); + String len(m_buffer->getSize()); + headers->writeString(len); + headers->writeString("\r\nServer: "); + headers->writeString(m_server->getServerName()); + headers->writeString("\r\n"); + if ((m_req.version == "1.1") && !m_req.persistent) { + headers->writeString("Connection: close\r\n"); + } + + while (!m_extraHeaders.empty()) { + String s = m_extraHeaders.front(); + m_extraHeaders.pop_front(); + headers->writeString(s); + headers->writeString("\r\n"); + } + + headers->writeString("\r\n"); + + m_out->forceWrite(headers->getBuffer(), headers->getSize()); + m_out->forceWrite(m_buffer->getBuffer(), m_buffer->getSize()); +} diff --git a/tests/test-Http.cc b/tests/test-Http.cc index e6ba38d..af78e43 100644 --- a/tests/test-Http.cc +++ b/tests/test-Http.cc @@ -13,12 +13,8 @@ class TestAction : public HttpServer::Action { }; bool TestAction::Do(HttpServer * server, Http::Request & req, HttpServer::Action::ActionMatch & match, IO out) throw (GeneralException) { - static const char str[] = -"HTTP/1.1 200 Found\r\n" -"Content-Type: text/html; charset=UTF-8\r\n" -"Content-Length: 266\r\n" -"Server: " DAEMON_NAME "\r\n" -"\r\n" + HttpServer::Response response(server, req, out); + response->writeString( "\n" "\n" @@ -29,9 +25,9 @@ bool TestAction::Do(HttpServer * server, Http::Request & req, HttpServer::Action " \n" " This is a test document.\n" " \n" -"\n"; +"\n"); - out->forceWrite(str, sizeof(str) - 1); + response.Flush(); return true; } -- cgit v1.2.3