summaryrefslogtreecommitdiff
path: root/lib/httpserver/HTTPRequest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/httpserver/HTTPRequest.cpp')
-rw-r--r--lib/httpserver/HTTPRequest.cpp135
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;
}