From 1ddd55b76374bba4393aea30c8d1d9cc14e32436 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 18 Oct 2012 13:39:16 -0700 Subject: [PATCH] Sane HTTP request parser. --- src/HTTPRequest.cpp | 104 ++++++++++++++++++++++++++++++++++++++++++++ src/HTTPRequest.h | 62 ++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 src/HTTPRequest.cpp create mode 100644 src/HTTPRequest.h diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp new file mode 100644 index 000000000..a289ded0b --- /dev/null +++ b/src/HTTPRequest.cpp @@ -0,0 +1,104 @@ +#include "HTTPRequest.h" + +#include +#include +#include + +#include "Log.h" +SETUP_LOG(); + +void HTTPRequest::reset() +{ + vHeaders.clear(); + sRequestBody.clear(); + sAuthorization.clear(); + iDataSize = 0; + bShouldClose = true; + eState = await_request; +} + +HTTPRequestAction HTTPRequest::requestDone(bool forceClose) +{ + if (forceClose || bShouldClose) + return haCLOSE_CONN; + reset(); + return haREAD_LINE; +} + +std::string HTTPRequest::getReplyHeaders(bool forceClose) +{ + if (forceClose || bShouldClose) + return "Connection: close\r\n"; + else + return "Connection: Keep-Alive\r\n"; +} + +HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf) +{ + std::string line; + std::istream is(&buf); + std::getline(is, line); + boost::trim(line); + +// cLog(lsTRACE) << "HTTPRequest line: " << line; + + if (eState == await_request) + { // VERB URL PROTO + if (line.empty()) + return haREAD_LINE; + sRequest = line; + bShouldClose = sRequest.find("HTTP/1.1") == std::string::npos; + + eState = await_header; +// cLog(lsTRACE) << "Got first line. Going to header state"; + return haREAD_LINE; + } + + if (eState == await_header) + { // HEADER_NAME: HEADER_BODY + if (line.empty()) // empty line or bare \r + { + if (iDataSize == 0) + { // no body + eState = bShouldClose ? await_close : await_reset; + return haDO_REQUEST; + } + eState = getting_body; +// cLog(lsTRACE) << "Got header, need body: " << iDataSize; + return haREAD_RAW; + } + vHeaders.push_back(line); + + size_t colon = line.find(':'); + if (colon != std::string::npos) + { + std::string headerName = line.substr(0, colon); + boost::trim(headerName); + boost::to_lower(headerName); + + std::string headerValue = line.substr(colon+1); + boost::trim(headerValue); + + if (headerName == "connection") + { + boost::to_lower(headerValue); + if ((headerValue == "keep-alive") || (headerValue == "keepalive")) + bShouldClose = false; + if (headerValue == "close") + bShouldClose = true; + } + + if (headerName == "content-length") + iDataSize = boost::lexical_cast(headerValue); + + if (headerName == "authorization") + sAuthorization = headerValue; + + } + + return haREAD_LINE; + } + + assert(false); + return haERROR; +} diff --git a/src/HTTPRequest.h b/src/HTTPRequest.h new file mode 100644 index 000000000..0d36c8d4f --- /dev/null +++ b/src/HTTPRequest.h @@ -0,0 +1,62 @@ +#ifndef HTTPREQUEST__HPP +#define HTTPREQUEST__HPP + +#include +#include + +#include + +enum HTTPRequestAction +{ // What the application code needs to do + haERROR = 0, + haREAD_LINE = 1, + haREAD_RAW = 2, + haDO_REQUEST = 3, + haCLOSE_CONN = 4 +}; + +class HTTPRequest +{ // an HTTP request in progress +protected: + + enum state + { + await_request, // We are waiting for the request line + await_header, // We are waiting for request headers + getting_body, // We are waiting for the body + await_close, // We are waiting for the request to complete so we can close the connection + await_reset // We are waiting for the request to complete so we can reset the connection + }; + + state eState; + std::string sRequest; // VERB URL PROTO + std::string sRequestBody; + std::string sAuthorization; + + std::vector vHeaders; + + int iDataSize; + bool bShouldClose; + +public: + + HTTPRequest() : eState(await_request), iDataSize(0), bShouldClose(true) { ; } + void reset(); + + std::string& peekBody() { return sRequestBody; } + std::string getBody() { return sRequestBody; } + std::string& peekRequest() { return sRequest; } + std::string getRequest() { return sRequest; } + std::string& peekAuth() { return sAuthorization; } + std::string getAuth() { return sAuthorization; } + + std::vector& peekHeaders() { return vHeaders; } + std::string getReplyHeaders(bool forceClose); + + HTTPRequestAction consume(boost::asio::streambuf&); + HTTPRequestAction requestDone(bool forceClose); // call after reply is sent + + int getDataSize() { return iDataSize; } +}; + +#endif