summaryrefslogtreecommitdiff
path: root/lib/httpserver
diff options
context:
space:
mode:
authorChris Wilson <chris+github@qwirx.com>2009-01-05 00:48:01 +0000
committerChris Wilson <chris+github@qwirx.com>2009-01-05 00:48:01 +0000
commit1c5c91f74c636145d02f4bc91b852d669685744e (patch)
treeb1717fe60d21b7916b4bb7f0ae68be91805ab188 /lib/httpserver
parentab7be20ac1f1db80aab9201a6cf51640f97ce507 (diff)
Don't read the whole uploaded body in HTTPRequest::Receive, as the
client may be expecting a 100 Continue header (or other response) before sending it, and only the HTTPServer should send that for us. Keep track of the stream that we're reading from, in case there's a body to read later. Simplify parsing of HTTP method, and add support for PUT requests. Add support for parsing Expect headers and storing and retrieving any unrecognised headers. Add support for sending a streaming upload from an IOStream with an HTTP request as the body (e.g. for PUT requests).
Diffstat (limited to 'lib/httpserver')
-rw-r--r--lib/httpserver/HTTPRequest.cpp135
-rw-r--r--lib/httpserver/HTTPRequest.h34
2 files changed, 140 insertions, 29 deletions
diff --git a/lib/httpserver/HTTPRequest.cpp b/lib/httpserver/HTTPRequest.cpp
index 8ba44903..aa064363 100644
--- a/lib/httpserver/HTTPRequest.cpp
+++ b/lib/httpserver/HTTPRequest.cpp
@@ -15,6 +15,7 @@
#include <stdio.h>
#include "HTTPRequest.h"
+#include "HTTPResponse.h"
#include "HTTPQueryDecoder.h"
#include "autogen_HTTPException.h"
#include "IOStream.h"
@@ -44,7 +45,9 @@ HTTPRequest::HTTPRequest()
mHTTPVersion(0),
mContentLength(-1),
mpCookies(0),
- mClientKeepAliveRequested(false)
+ mClientKeepAliveRequested(false),
+ mExpectContinue(false),
+ mpStreamToReadFrom(NULL)
{
}
@@ -61,11 +64,13 @@ HTTPRequest::HTTPRequest()
HTTPRequest::HTTPRequest(enum Method method, const std::string& rURI)
: mMethod(method),
mRequestURI(rURI),
- mHostPort(80), // default if not specified
+ mHostPort(80), // default if not specified
mHTTPVersion(HTTPVersion_1_1),
mContentLength(-1),
mpCookies(0),
- mClientKeepAliveRequested(false)
+ mClientKeepAliveRequested(false),
+ mExpectContinue(false),
+ mpStreamToReadFrom(NULL)
{
}
@@ -120,30 +125,36 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout)
// Check the method
unsigned int p = 0; // current position in string
- if(::strncmp(requestLine.c_str(), "GET ", 4) == 0)
- {
- p = 3;
- mMethod = Method_GET;
- }
- else if(::strncmp(requestLine.c_str(), "HEAD ", 5) == 0)
- {
- p = 4;
- mMethod = Method_HEAD;
- }
- else if(::strncmp(requestLine.c_str(), "POST ", 5) == 0)
+ p = requestLine.find(' '); // end of first word
+
+ if (p == std::string::npos)
{
- p = 4;
- mMethod = Method_POST;
+ // No terminating space, looks bad
+ p = requestLine.size();
}
else
{
- p = requestLine.find(' ');
- if(p == std::string::npos)
+ std::string method = requestLine.substr(0, p);
+ if (method == "GET")
{
- // No terminating space, looks bad
- p = requestLine.size();
+ mMethod = Method_GET;
+ }
+ else if (method == "HEAD")
+ {
+ mMethod = Method_HEAD;
+ }
+ else if (method == "POST")
+ {
+ mMethod = Method_POST;
+ }
+ else if (method == "PUT")
+ {
+ mMethod = Method_PUT;
+ }
+ else
+ {
+ mMethod = Method_UNKNOWN;
}
- mMethod = Method_UNKNOWN;
}
// Skip spaces to find URI
@@ -263,6 +274,15 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout)
// Now parse the headers
ParseHeaders(rGetLine, Timeout);
+ std::string expected;
+ if (GetHeader("Expect", &expected))
+ {
+ if (expected == "100-continue")
+ {
+ mExpectContinue = true;
+ }
+ }
+
// Parse form data?
if(mMethod == Method_POST && mContentLength >= 0)
{
@@ -305,10 +325,42 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout)
// Finish off
decoder.Finish();
}
+ else if (mContentLength > 0)
+ {
+ IOStream::pos_type bytesToCopy = rGetLine.GetSizeOfBufferedData();
+ if (bytesToCopy > mContentLength)
+ {
+ bytesToCopy = mContentLength;
+ }
+ Write(rGetLine.GetBufferedData(), bytesToCopy);
+ SetForReading();
+ mpStreamToReadFrom = &(rGetLine.GetUnderlyingStream());
+ }
return true;
}
+void HTTPRequest::ReadContent(IOStream& rStreamToWriteTo)
+{
+ Seek(0, SeekType_Absolute);
+
+ CopyStreamTo(rStreamToWriteTo);
+ IOStream::pos_type bytesCopied = GetSize();
+
+ while (bytesCopied < mContentLength)
+ {
+ char buffer[1024];
+ IOStream::pos_type bytesToCopy = sizeof(buffer);
+ if (bytesToCopy > mContentLength - bytesCopied)
+ {
+ bytesToCopy = mContentLength - bytesCopied;
+ }
+ bytesToCopy = mpStreamToReadFrom->Read(buffer, bytesToCopy);
+ rStreamToWriteTo.Write(buffer, bytesToCopy);
+ bytesCopied += bytesToCopy;
+ }
+}
+
// --------------------------------------------------------------------------
//
// Function
@@ -317,7 +369,7 @@ bool HTTPRequest::Receive(IOStreamGetLine &rGetLine, int Timeout)
// Created: 03/01/09
//
// --------------------------------------------------------------------------
-bool HTTPRequest::Send(IOStream &rStream, int Timeout)
+bool HTTPRequest::Send(IOStream &rStream, int Timeout, bool ExpectContinue)
{
switch (mMethod)
{
@@ -331,6 +383,8 @@ bool HTTPRequest::Send(IOStream &rStream, int Timeout)
rStream.Write("HEAD"); break;
case Method_POST:
rStream.Write("POST"); break;
+ case Method_PUT:
+ rStream.Write("PUT"); break;
}
rStream.Write(" ");
@@ -391,6 +445,11 @@ bool HTTPRequest::Send(IOStream &rStream, int Timeout)
{
oss << i->first << ": " << i->second << "\n";
}
+
+ if (ExpectContinue)
+ {
+ oss << "Expect: 100-continue\n";
+ }
rStream.Write(oss.str().c_str());
rStream.Write("\n");
@@ -398,6 +457,31 @@ bool HTTPRequest::Send(IOStream &rStream, int Timeout)
return true;
}
+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);
+}
+
// --------------------------------------------------------------------------
//
// Function
@@ -509,7 +593,12 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout)
}
// else don't understand, just assume default for protocol version
}
- // else ignore it
+ else
+ {
+ std::string name = header.substr(0, p);
+ mExtraHeaders.push_back(Header(name,
+ h + dataStart));
+ }
// Unset have header flag, as it's now been processed
haveHeader = false;
diff --git a/lib/httpserver/HTTPRequest.h b/lib/httpserver/HTTPRequest.h
index dc81d593..58874092 100644
--- a/lib/httpserver/HTTPRequest.h
+++ b/lib/httpserver/HTTPRequest.h
@@ -13,6 +13,9 @@
#include <string>
#include <map>
+#include "CollectInBufferStream.h"
+
+class HTTPResponse;
class IOStream;
class IOStreamGetLine;
@@ -24,7 +27,7 @@ class IOStreamGetLine;
// Created: 26/3/04
//
// --------------------------------------------------------------------------
-class HTTPRequest
+class HTTPRequest : public CollectInBufferStream
{
public:
enum Method
@@ -33,7 +36,8 @@ public:
Method_UNKNOWN = 0,
Method_GET = 1,
Method_HEAD = 2,
- Method_POST = 3
+ Method_POST = 3,
+ Method_PUT = 4
};
HTTPRequest();
@@ -56,7 +60,10 @@ public:
};
bool Receive(IOStreamGetLine &rGetLine, int Timeout);
- bool Send(IOStream &rStream, int Timeout);
+ bool Send(IOStream &rStream, int Timeout, bool ExpectContinue = false);
+ void SendWithStream(IOStream &rStreamToSendTo, int Timeout,
+ IOStream* pStreamToSend, HTTPResponse& rResponse);
+ void ReadContent(IOStream& rStreamToWriteTo);
typedef std::map<std::string, std::string> CookieJar_t;
@@ -88,7 +95,20 @@ public:
const CookieJar_t *GetCookies() const {return mpCookies;} // WARNING: May return NULL
bool GetCookie(const char *CookieName, std::string &rValueOut) const;
const std::string &GetCookie(const char *CookieName) const;
-
+ bool GetHeader(const std::string& rName, std::string* pValueOut) const
+ {
+ for (std::vector<Header>::const_iterator
+ i = mExtraHeaders.begin();
+ i != mExtraHeaders.end(); i++)
+ {
+ if (i->first == rName)
+ {
+ *pValueOut = i->second;
+ return true;
+ }
+ }
+ return false;
+ }
// --------------------------------------------------------------------------
//
@@ -109,12 +129,12 @@ public:
{
mExtraHeaders.push_back(Header(rName, rValue));
}
-
+ bool IsExpectingContinue() const { return mExpectContinue; }
+
private:
void ParseHeaders(IOStreamGetLine &rGetLine, int Timeout);
void ParseCookies(const std::string &rHeader, int DataStarts);
-private:
enum Method mMethod;
std::string mRequestURI;
std::string mHostName;
@@ -127,6 +147,8 @@ private:
CookieJar_t *mpCookies;
bool mClientKeepAliveRequested;
std::vector<Header> mExtraHeaders;
+ bool mExpectContinue;
+ IOStream* mpStreamToReadFrom;
};
#endif // HTTPREQUEST__H