diff options
Diffstat (limited to 'lib/httpserver/HTTPRequest.cpp')
-rw-r--r-- | lib/httpserver/HTTPRequest.cpp | 135 |
1 files changed, 84 insertions, 51 deletions
diff --git a/lib/httpserver/HTTPRequest.cpp b/lib/httpserver/HTTPRequest.cpp index 4c5dc149..a94d96b0 100644 --- a/lib/httpserver/HTTPRequest.cpp +++ b/lib/httpserver/HTTPRequest.cpp @@ -10,10 +10,11 @@ #include "Box.h" #include <string.h> -#include <strings.h> #include <stdlib.h> #include <stdio.h> +#include <sstream> + #include "HTTPRequest.h" #include "HTTPResponse.h" #include "HTTPQueryDecoder.h" @@ -94,6 +95,32 @@ HTTPRequest::~HTTPRequest() } } +// -------------------------------------------------------------------------- +// +// Function +// Name: HTTPRequest::GetMethodName() +// Purpose: Returns the name of the request's HTTP method verb +// as a string. +// Created: 28/7/15 +// +// -------------------------------------------------------------------------- + +std::string HTTPRequest::GetMethodName() const +{ + switch(mMethod) + { + case Method_UNINITIALISED: return "uninitialised"; + case Method_UNKNOWN: return "unknown"; + case Method_GET: return "GET"; + case Method_HEAD: return "HEAD"; + case Method_POST: return "POST"; + case Method_PUT: return "PUT"; + default: + std::ostringstream oss; + oss << "unknown-" << mMethod; + return oss.str(); + }; +} // -------------------------------------------------------------------------- // @@ -111,7 +138,7 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) // Check caller's logic if(mMethod != Method_UNINITIALISED) { - THROW_EXCEPTION(HTTPException, RequestAlreadyBeenRead) + THROW_EXCEPTION(HTTPException, RequestAlreadyBeenRead); } // Read the first line, which is of a different format to the rest of the lines @@ -126,8 +153,8 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) // Check the method size_t p = 0; // current position in string p = requestLine.find(' '); // end of first word - - if (p == std::string::npos) + + if(p == std::string::npos) { // No terminating space, looks bad p = requestLine.size(); @@ -163,14 +190,14 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) { ++p; } - + // Check there's a URI following... if(requestLinePtr[p] == '\0') { // Didn't get the request line, probably end of connection which had been kept alive return false; } - + // Read the URI, unescaping any %XX hex codes while(requestLinePtr[p] != ' ' && requestLinePtr[p] != '\0') { @@ -201,10 +228,10 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) { code[1] = requestLinePtr[++p]; } - + // Convert into a char code long c = ::strtol(code, NULL, 16); - + // Accept it? if(c > 0 && c <= 255) { @@ -241,28 +268,32 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) int major, minor; if(::sscanf(requestLinePtr + p + 5, "%d.%d", &major, &minor) != 2) { - THROW_EXCEPTION(HTTPException, BadRequest) + THROW_EXCEPTION_MESSAGE(HTTPException, BadRequest, + "Unable to parse HTTP version number: " << + requestLinePtr); } - + // Store version mHTTPVersion = (major * HTTPVersion__MajorMultiplier) + minor; } else { // Not good -- wrong string found - THROW_EXCEPTION(HTTPException, BadRequest) + THROW_EXCEPTION_MESSAGE(HTTPException, BadRequest, + "Unable to parse HTTP request line: " << + requestLinePtr); } } - + BOX_TRACE("HTTPRequest: method=" << mMethod << ", uri=" << mRequestURI << ", version=" << mHTTPVersion); - + // If HTTP 1.1 or greater, assume keep-alive if(mHTTPVersion >= HTTPVersion_1_1) { mClientKeepAliveRequested = true; } - + // Decode query string? if((mMethod == Method_GET || mMethod == Method_HEAD) && !mQueryString.empty()) { @@ -270,28 +301,28 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) decoder.DecodeChunk(mQueryString.c_str(), mQueryString.size()); decoder.Finish(); } - + // Now parse the headers ParseHeaders(rGetLine, Timeout); - + std::string expected; - if (GetHeader("Expect", &expected)) + if(GetHeader("Expect", &expected)) { - if (expected == "100-continue") + if(expected == "100-continue") { mExpectContinue = true; } } - + // Parse form data? if(mMethod == Method_POST && mContentLength >= 0) { // Too long? Don't allow people to be nasty by sending lots of data if(mContentLength > MAX_CONTENT_SIZE) { - THROW_EXCEPTION(HTTPException, POSTContentTooLong) + THROW_EXCEPTION(HTTPException, POSTContentTooLong); } - + // Some data in the request to follow, parsing it bit by bit HTTPQueryDecoder decoder(mQuery); // Don't forget any data left in the GetLine object @@ -317,7 +348,8 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) if(r == 0) { // Timeout, just error - THROW_EXCEPTION(HTTPException, RequestReadFailed) + THROW_EXCEPTION_MESSAGE(HTTPException, RequestReadFailed, + "Failed to read complete request with the timeout"); } decoder.DecodeChunk(buf, r); bytesToGo -= r; @@ -336,7 +368,7 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout) SetForReading(); mpStreamToReadFrom = &(rGetLine.GetUnderlyingStream()); } - + return true; } @@ -346,7 +378,7 @@ void HTTPRequest::ReadContent(IOStream& rStreamToWriteTo) CopyStreamTo(rStreamToWriteTo); IOStream::pos_type bytesCopied = GetSize(); - + while (bytesCopied < mContentLength) { char buffer[1024]; @@ -397,7 +429,8 @@ bool HTTPRequest::Send(IOStream &rStream, int Timeout, bool ExpectContinue) case HTTPVersion_1_0: rStream.Write("HTTP/1.0"); break; case HTTPVersion_1_1: rStream.Write("HTTP/1.1"); break; default: - THROW_EXCEPTION(HTTPException, NotImplemented); + THROW_EXCEPTION_MESSAGE(HTTPException, NotImplemented, + "Unsupported HTTP version: " << mHTTPVersion); } rStream.Write("\n"); @@ -428,7 +461,8 @@ bool HTTPRequest::Send(IOStream &rStream, int Timeout, bool ExpectContinue) if (mpCookies) { - THROW_EXCEPTION(HTTPException, NotImplemented); + THROW_EXCEPTION_MESSAGE(HTTPException, NotImplemented, + "Cookie support not implemented yet"); } if (mClientKeepAliveRequested) @@ -445,7 +479,7 @@ bool HTTPRequest::Send(IOStream &rStream, int Timeout, bool ExpectContinue) { oss << i->first << ": " << i->second << "\n"; } - + if (ExpectContinue) { oss << "Expect: 100-continue\n"; @@ -461,23 +495,22 @@ void HTTPRequest::SendWithStream(IOStream &rStreamToSendTo, int Timeout, IOStream* pStreamToSend, HTTPResponse& rResponse) { IOStream::pos_type size = pStreamToSend->BytesLeftToRead(); - if (size != IOStream::SizeOfStreamUnknown) { mContentLength = size; } - + Send(rStreamToSendTo, Timeout, true); - + rResponse.Receive(rStreamToSendTo, Timeout); if (rResponse.GetResponseCode() != 100) { // bad response, abort now return; } - + pStreamToSend->CopyStreamTo(rStreamToSendTo, Timeout); - + // receive the final response rResponse.Receive(rStreamToSendTo, Timeout); } @@ -502,13 +535,13 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout) THROW_EXCEPTION(HTTPException, BadRequest) } - std::string currentLine; + std::string currentLine; if(!rGetLine.GetLine(currentLine, false /* no preprocess */, Timeout)) { // Timeout THROW_EXCEPTION(HTTPException, RequestReadFailed) } - + // Is this a continuation of the previous line? bool processHeader = haveHeader; if(!currentLine.empty() && (currentLine[0] == ' ' || currentLine[0] == '\t')) @@ -517,7 +550,7 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout) processHeader = false; } //TRACE3("%d:%d:%s\n", processHeader, haveHeader, currentLine.c_str()); - + // Parse the header -- this will actually process the header // from the previous run around the loop. if(processHeader) @@ -538,7 +571,7 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout) std::string header_name(ToLowerCase(std::string(h, p))); - + if (header_name == "content-length") { // Decode number @@ -556,17 +589,17 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout) { // Store host header mHostName = h + dataStart; - + // Is there a port number to split off? std::string::size_type colon = mHostName.find_first_of(':'); if(colon != std::string::npos) { // There's a port in the string... attempt to turn it into an int mHostPort = ::strtol(mHostName.c_str() + colon + 1, 0, 10); - + // Truncate the string to just the hostname mHostName = mHostName.substr(0, colon); - + BOX_TRACE("Host: header, hostname = " << "'" << mHostName << "', host " "port = " << mHostPort); @@ -596,7 +629,7 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout) mExtraHeaders.push_back(Header(header_name, h + dataStart)); } - + // Unset have header flag, as it's now been processed haveHeader = false; } @@ -617,7 +650,7 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout) { // All done! break; - } + } } } @@ -636,13 +669,13 @@ void HTTPRequest::ParseCookies(const std::string &rHeader, int DataStarts) const char *pos = data; const char *itemStart = pos; std::string name; - + enum { s_NAME, s_VALUE, s_VALUE_QUOTED, s_FIND_NEXT_NAME } state = s_NAME; - do + do { switch(state) { @@ -665,7 +698,7 @@ void HTTPRequest::ParseCookies(const std::string &rHeader, int DataStarts) } } break; - + case s_VALUE: { if(*pos == ';' || *pos == ',' || *pos == '\0') @@ -679,7 +712,7 @@ void HTTPRequest::ParseCookies(const std::string &rHeader, int DataStarts) } } break; - + case s_VALUE_QUOTED: { if(*pos == '"') @@ -693,7 +726,7 @@ void HTTPRequest::ParseCookies(const std::string &rHeader, int DataStarts) } } break; - + case s_FIND_NEXT_NAME: { // Skip over terminators and white space to get to the next name @@ -705,7 +738,7 @@ void HTTPRequest::ParseCookies(const std::string &rHeader, int DataStarts) } } break; - + default: // Ooops THROW_EXCEPTION(HTTPException, Internal) @@ -732,7 +765,7 @@ bool HTTPRequest::GetCookie(const char *CookieName, std::string &rValueOut) cons { return false; } - + // See if it's there CookieJar_t::const_iterator v(mpCookies->find(std::string(CookieName))); if(v != mpCookies->end()) @@ -741,7 +774,7 @@ bool HTTPRequest::GetCookie(const char *CookieName, std::string &rValueOut) cons rValueOut = v->second; return true; } - + return false; } @@ -764,7 +797,7 @@ const std::string &HTTPRequest::GetCookie(const char *CookieName) const { return noCookie; } - + // See if it's there CookieJar_t::const_iterator v(mpCookies->find(std::string(CookieName))); if(v != mpCookies->end()) @@ -772,7 +805,7 @@ const std::string &HTTPRequest::GetCookie(const char *CookieName) const // Return the value return v->second; } - + return noCookie; } |