[Box Backup-commit] COMMIT r2438 - box/trunk/lib/httpserver

boxbackup-dev@boxbackup.org boxbackup-dev@boxbackup.org
Mon, 5 Jan 2009 00:48:01 +0000 (GMT)


Author: chris
Date: 2009-01-05 00:48:01 +0000 (Mon, 05 Jan 2009)
New Revision: 2438

Modified:
   box/trunk/lib/httpserver/HTTPRequest.cpp
   box/trunk/lib/httpserver/HTTPRequest.h
Log:
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).


Modified: box/trunk/lib/httpserver/HTTPRequest.cpp
===================================================================
--- box/trunk/lib/httpserver/HTTPRequest.cpp	2009-01-05 00:47:23 UTC (rev 2437)
+++ box/trunk/lib/httpserver/HTTPRequest.cpp	2009-01-05 00:48:01 UTC (rev 2438)
@@ -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 @@
 	  mHTTPVersion(0),
 	  mContentLength(-1),
 	  mpCookies(0),
-	  mClientKeepAliveRequested(false)
+	  mClientKeepAliveRequested(false),
+	  mExpectContinue(false),
+	  mpStreamToReadFrom(NULL)
 {
 }
 
@@ -61,11 +64,13 @@
 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 @@
 
 	// Check the method
 	unsigned int p = 0;	// current position in string
-	if(::strncmp(requestLine.c_str(), "GET ", 4) == 0)
+	p = requestLine.find(' '); // end of first word
+	
+	if (p == std::string::npos)
 	{
-		p = 3;
-		mMethod = Method_GET;
+		// No terminating space, looks bad
+		p = requestLine.size();
 	}
-	else if(::strncmp(requestLine.c_str(), "HEAD ", 5) == 0)
-	{
-		p = 4;
-		mMethod = Method_HEAD;
-	}
-	else if(::strncmp(requestLine.c_str(), "POST ", 5) == 0)
-	{
-		p = 4;
-		mMethod = Method_POST;
-	}
 	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;
 		}
-		mMethod = Method_UNKNOWN;
+		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;
+		}
 	}
 
 	// Skip spaces to find URI
@@ -263,6 +274,15 @@
 	// 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 @@
 		// 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 @@
 //		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 @@
 		rStream.Write("HEAD"); break;
 	case Method_POST:
 		rStream.Write("POST"); break;
+	case Method_PUT:
+		rStream.Write("PUT"); break;
 	}
 
 	rStream.Write(" ");
@@ -391,6 +445,11 @@
 	{
 		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 @@
 	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 @@
 				}
 				// 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;

Modified: box/trunk/lib/httpserver/HTTPRequest.h
===================================================================
--- box/trunk/lib/httpserver/HTTPRequest.h	2009-01-05 00:47:23 UTC (rev 2437)
+++ box/trunk/lib/httpserver/HTTPRequest.h	2009-01-05 00:48:01 UTC (rev 2438)
@@ -13,6 +13,9 @@
 #include <string>
 #include <map>
 
+#include "CollectInBufferStream.h"
+
+class HTTPResponse;
 class IOStream;
 class IOStreamGetLine;
 
@@ -24,7 +27,7 @@
 //		Created: 26/3/04
 //
 // --------------------------------------------------------------------------
-class HTTPRequest
+class HTTPRequest : public CollectInBufferStream
 {
 public:
 	enum Method
@@ -33,7 +36,8 @@
 		Method_UNKNOWN = 0,
 		Method_GET = 1,
 		Method_HEAD = 2,
-		Method_POST = 3
+		Method_POST = 3,
+		Method_PUT = 4
 	};
 	
 	HTTPRequest();
@@ -56,7 +60,10 @@
 	};
 
 	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,8 +95,21 @@
 	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;
+	}
 
-
 	// --------------------------------------------------------------------------
 	//
 	// Function
@@ -109,12 +129,12 @@
 	{
 		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 @@
 	CookieJar_t *mpCookies;
 	bool mClientKeepAliveRequested;
 	std::vector<Header> mExtraHeaders;
+	bool mExpectContinue;
+	IOStream* mpStreamToReadFrom;
 };
 
 #endif // HTTPREQUEST__H