From db098e95abd5297a33359218fdc49faa2f93bbb5 Mon Sep 17 00:00:00 2001 From: Pixel Date: Tue, 15 Nov 2011 22:50:26 -0800 Subject: Basic POST support done; multipart/form-data is yet to be finalized. --- src/HttpServer.cc | 133 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 51 deletions(-) (limited to 'src/HttpServer.cc') diff --git a/src/HttpServer.cc b/src/HttpServer.cc index f903093..dbc9b92 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -9,6 +9,8 @@ static const ev_tstamp s_httpTimeout = 15; namespace Balau { +typedef std::map StringMap; + class HttpWorker : public Task { public: HttpWorker(IO & io, void * server); @@ -20,27 +22,22 @@ class HttpWorker : public Task { bool handleClient(); void send400(Events::BaseEvent * evt); String httpUnescape(const char * in); + void readVariables(StringMap & variables, char * str); IO m_socket; IO m_strm; String m_name; - - uint8_t * m_postData; }; }; -Balau::HttpWorker::HttpWorker(IO & io, void * _server) : m_socket(io), m_strm(new BStream(io)), m_postData(NULL) { +Balau::HttpWorker::HttpWorker(IO & io, void * _server) : m_socket(io), m_strm(new BStream(io)) { HttpServer * server = (HttpServer *) _server; m_name.set("HttpWorker(%s)", m_socket->getName()); // copy stuff from server, such as port number, root document, base URL, etc... } Balau::HttpWorker::~HttpWorker() { - if (m_postData) { - free(m_postData); - m_postData = NULL; - } } Balau::String Balau::HttpWorker::httpUnescape(const char * in) { @@ -74,6 +71,26 @@ Balau::String Balau::HttpWorker::httpUnescape(const char * in) { return r; } +void Balau::HttpWorker::readVariables(StringMap & variables, char * str) { + char * ampPos; + do { + ampPos = strchr(str, '&'); + if (ampPos) + *ampPos = 0; + + char * val = strchr(str, '='); + + if (val) { + *val++ = 0; + } + String keyStr = httpUnescape(str); + String valStr = val ? httpUnescape(val) : String(""); + variables[keyStr] = valStr; + + str = ampPos + 1; + } while (ampPos); +} + void Balau::HttpWorker::send400(Events::BaseEvent * evt) { static const char str[] = "HTTP/1.0 400 Bad Request\r\n" @@ -93,7 +110,7 @@ void Balau::HttpWorker::send400(Events::BaseEvent * evt) { "\n"; setOkayToEAgain(false); - m_socket->forceWrite(str, sizeof(str), evt); + m_socket->forceWrite(str, sizeof(str) - 1, evt); Balau::Printer::elog(Balau::E_HTTPSERVER, "%s had an invalid request", m_name.to_charp()); } @@ -102,8 +119,6 @@ bool Balau::HttpWorker::handleClient() { waitFor(&evtTimeout); setOkayToEAgain(true); - typedef std::map StringMap; - String line; bool gotFirst = false; int method = -1; @@ -112,10 +127,6 @@ bool Balau::HttpWorker::handleClient() { String httpVersion; StringMap httpHeaders; StringMap variables; - if (m_postData) { - free(m_postData); - m_postData = NULL; - } bool persistent = false; // read client's request @@ -212,25 +223,6 @@ bool Balau::HttpWorker::handleClient() { return false; } - if (method == Http::POST) { - int lengthStr = 0; - StringMap::iterator i = httpHeaders.find("Content-Length"); - - if (i != httpHeaders.end()) - lengthStr = i->second.to_int(); - - m_postData = (uint8_t *) malloc(lengthStr); - - try { - m_strm->forceRead(m_postData, lengthStr); - } - catch (EAgain) { - Assert(evtTimeout.gotSignal()); - Balau::Printer::elog(Balau::E_HTTPSERVER, "%s timed out getting request (reading POST values)", m_name.to_charp()); - return false; - } - } - if (httpVersion == "1.1") { StringMap::iterator i = httpHeaders.find("Connection"); @@ -244,31 +236,70 @@ bool Balau::HttpWorker::handleClient() { } } - int variablesPos = uri.strchr('?'); + if (method == Http::POST) { + int length = 0; + StringMap::iterator i; + bool multipart = false; + String boundary; - if (variablesPos >= 0) { - char * variablesStr = uri.strdup(variablesPos + 1); - char * p = variablesStr; - char * ampPos; - uri = httpUnescape(uri.extract(0, variablesPos).to_charp()); + i = httpHeaders.find("Content-Length"); - do { - ampPos = strchr(p, '&'); - if (ampPos) - *ampPos = 0; + if (i != httpHeaders.end()) + length = i->second.to_int(); - char * val = strchr(p, '='); + i = httpHeaders.find("Content-Type"); + + if (i != httpHeaders.end()) { + static const String multipartStr = "multipart/form-data"; + if (i->second.extract(multipartStr.strlen()) == multipartStr) { + if (i->second[multipartStr.strlen() + 1] != ';') { + send400(&evtTimeout); + return false; + } + StringMap t; + char * b = i->second.extract(sizeof(multipartStr) + 1).trim().strdup(); + readVariables(t, b); + free(b); - if (val) { - *val++ = 0; + i = t.find("boundary"); + + if (i == t.end()) { + send400(&evtTimeout); + return false; + } + + boundary = i->second; + multipart = true; } - String keyStr = httpUnescape(p); - String valStr = val ? httpUnescape(val) : String(""); - variables[keyStr] = valStr; + } - p = ampPos + 1; - } while (ampPos); + if (multipart) { + // will handle this horror later... + Assert(!"multipart/form-data not supported for now"); + } else { + uint8_t * postData = (uint8_t *) malloc(length); + try { + m_strm->forceRead(postData, length); + } + catch (EAgain) { + Assert(evtTimeout.gotSignal()); + Balau::Printer::elog(Balau::E_HTTPSERVER, "%s timed out getting request (reading POST values)", m_name.to_charp()); + return false; + } + + readVariables(variables, (char *) postData); + + free(postData); + } + } + + int variablesPos = uri.strchr('?'); + + if (variablesPos >= 0) { + char * variablesStr = uri.strdup(variablesPos + 1); + uri = httpUnescape(uri.extract(0, variablesPos).to_charp()); + readVariables(variables, variablesStr); free(variablesStr); } -- cgit v1.2.3