[Box Backup-commit] COMMIT r2504 - in box/trunk: . bin bin/s3simulator infrastructure lib/httpserver test/httpserver test/httpserver/testfiles

boxbackup-dev at boxbackup.org boxbackup-dev at boxbackup.org
Mon Apr 13 19:40:50 BST 2009


Author: chris
Date: 2009-04-13 19:40:50 +0100 (Mon, 13 Apr 2009)
New Revision: 2504

Added:
   box/trunk/bin/s3simulator/
   box/trunk/bin/s3simulator/s3simulator.cpp
   box/trunk/lib/httpserver/S3Simulator.cpp
   box/trunk/lib/httpserver/S3Simulator.h
   box/trunk/test/httpserver/testfiles/s3simulator.conf
Modified:
   box/trunk/infrastructure/makebuildenv.pl.in
   box/trunk/lib/httpserver/HTTPRequest.cpp
   box/trunk/lib/httpserver/HTTPRequest.h
   box/trunk/lib/httpserver/HTTPServer.cpp
   box/trunk/lib/httpserver/HTTPServer.h
   box/trunk/modules.txt
   box/trunk/test/httpserver/testhttpserver.cpp
Log:
Move S3Simulator into its own class, like S3Client, for reuse elsewhere.


Copied: box/trunk/bin/s3simulator/s3simulator.cpp (from rev 2422, box/trunk/bin/bbackupd/bbackupd.cpp)
===================================================================
--- box/trunk/bin/s3simulator/s3simulator.cpp	                        (rev 0)
+++ box/trunk/bin/s3simulator/s3simulator.cpp	2009-04-13 18:40:50 UTC (rev 2504)
@@ -0,0 +1,32 @@
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    s3simulator.cpp
+//		Purpose: main file for S3 simulator daemon
+//		Created: 2003/10/11
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+#include "S3Simulator.h"
+#include "MainHelper.h"
+
+#include "MemLeakFindOn.h"
+
+int main(int argc, const char *argv[])
+{
+	int ExitCode = 0;
+
+	MAINHELPER_START
+
+	Logging::SetProgramName("s3simulator");
+	Logging::ToConsole(true);
+	Logging::ToSyslog (true);
+	
+	S3Simulator daemon;
+	ExitCode = daemon.Main("s3simulator.conf", argc, argv);
+
+	MAINHELPER_END
+
+	return ExitCode;
+}

Modified: box/trunk/infrastructure/makebuildenv.pl.in
===================================================================
--- box/trunk/infrastructure/makebuildenv.pl.in	2009-04-13 18:39:02 UTC (rev 2503)
+++ box/trunk/infrastructure/makebuildenv.pl.in	2009-04-13 18:40:50 UTC (rev 2504)
@@ -431,6 +431,8 @@
 echo Killing any running daemons...
 kill_process bbackupd
 kill_process bbstored
+kill_process httpserver
+kill_process s3simulator
 __E
 			}
 

Modified: box/trunk/lib/httpserver/HTTPRequest.cpp
===================================================================
--- box/trunk/lib/httpserver/HTTPRequest.cpp	2009-04-13 18:39:02 UTC (rev 2503)
+++ box/trunk/lib/httpserver/HTTPRequest.cpp	2009-04-13 18:40:50 UTC (rev 2504)
@@ -535,9 +535,11 @@
 			{
 				++dataStart;
 			}
-		
-			if(p == sizeof("Content-Length")-1
-				&& ::strncasecmp(h, "Content-Length", sizeof("Content-Length")-1) == 0)
+
+			std::string header_name(ToLowerCase(std::string(h,
+				p)));
+			
+			if (header_name == "content-length")
 			{
 				// Decode number
 				long len = ::strtol(h + dataStart, NULL, 10);	// returns zero in error case, this is OK
@@ -545,14 +547,12 @@
 				// Store
 				mContentLength = len;
 			}
-			else if(p == sizeof("Content-Type")-1
-				&& ::strncasecmp(h, "Content-Type", sizeof("Content-Type")-1) == 0)
+			else if (header_name == "content-type")
 			{
 				// Store rest of string as content type
 				mContentType = h + dataStart;
 			}
-			else if(p == sizeof("Host")-1
-				&& ::strncasecmp(h, "Host", sizeof("Host")-1) == 0)
+			else if (header_name == "host")
 			{
 				// Store host header
 				mHostName = h + dataStart;
@@ -572,14 +572,12 @@
 						"port = " << mHostPort);
 				}
 			}
-			else if(p == sizeof("Cookie")-1
-				&& ::strncasecmp(h, "Cookie", sizeof("Cookie")-1) == 0)
+			else if (header_name == "cookie")
 			{
 				// Parse cookies
 				ParseCookies(header, dataStart);
 			}
-			else if(p == sizeof("Connection")-1
-				&& ::strncasecmp(h, "Connection", sizeof("Connection")-1) == 0)
+			else if (header_name == "connection")
 			{
 				// Connection header, what is required?
 				const char *v = h + dataStart;
@@ -595,8 +593,7 @@
 			}
 			else
 			{
-				std::string name = header.substr(0, p);
-				mExtraHeaders.push_back(Header(name,
+				mExtraHeaders.push_back(Header(header_name,
 					h + dataStart));
 			}
 			

Modified: box/trunk/lib/httpserver/HTTPRequest.h
===================================================================
--- box/trunk/lib/httpserver/HTTPRequest.h	2009-04-13 18:39:02 UTC (rev 2503)
+++ box/trunk/lib/httpserver/HTTPRequest.h	2009-04-13 18:40:50 UTC (rev 2504)
@@ -97,16 +97,19 @@
 	const std::string &GetCookie(const char *CookieName) const;
 	bool GetHeader(const std::string& rName, std::string* pValueOut) const
 	{
+		std::string header = ToLowerCase(rName);
+
 		for (std::vector<Header>::const_iterator
 			i  = mExtraHeaders.begin();
 			i != mExtraHeaders.end(); i++)
 		{
-			if (i->first == rName)
+			if (i->first == header)
 			{
 				*pValueOut = i->second;
 				return true;
 			}
 		}
+
 		return false;
 	}
 	std::vector<Header> GetHeaders() { return mExtraHeaders; }
@@ -128,7 +131,7 @@
 
 	void AddHeader(const std::string& rName, const std::string& rValue)
 	{
-		mExtraHeaders.push_back(Header(rName, rValue));
+		mExtraHeaders.push_back(Header(ToLowerCase(rName), rValue));
 	}
 	bool IsExpectingContinue() const { return mExpectContinue; }
 	const char* GetVerb() const
@@ -168,6 +171,17 @@
 	bool mExpectContinue;
 	IOStream* mpStreamToReadFrom;
 	std::string mHttpVerb;
+
+	std::string ToLowerCase(const std::string& rInput) const
+	{
+		std::string output = rInput;
+		for (std::string::iterator c = output.begin();
+			c != output.end(); c++)
+		{
+			*c = tolower(*c);
+		}
+		return output;
+	}
 };
 
 #endif // HTTPREQUEST__H

Modified: box/trunk/lib/httpserver/HTTPServer.cpp
===================================================================
--- box/trunk/lib/httpserver/HTTPServer.cpp	2009-04-13 18:39:02 UTC (rev 2503)
+++ box/trunk/lib/httpserver/HTTPServer.cpp	2009-04-13 18:40:50 UTC (rev 2504)
@@ -153,21 +153,21 @@
 	
 		// Generate a response
 		HTTPResponse response(&rStream);
+		
 		try
 		{
 			Handle(request, response);
 		}
 		catch(BoxException &e)
 		{
-			char exceptionCode[64];
-			::sprintf(exceptionCode, "(%d/%d)", e.GetType(), e.GetSubType());
-			SendInternalErrorResponse(exceptionCode, rStream);
-			return;
+			char exceptionCode[256];
+			::sprintf(exceptionCode, "%s (%d/%d)", e.what(),
+				e.GetType(), e.GetSubType());
+			SendInternalErrorResponse(exceptionCode, response);
 		}
 		catch(...)
 		{
-			SendInternalErrorResponse("unknown", rStream);
-			return;
+			SendInternalErrorResponse("unknown", response);
 		}
 		
 		// Keep alive?
@@ -186,7 +186,7 @@
 		response.Send(request.GetMethod() == HTTPRequest::Method_HEAD);
 	}
 
-	// Notify derived claases
+	// Notify derived classes
 	HTTPConnectionClosing();
 }
 
@@ -194,12 +194,14 @@
 // --------------------------------------------------------------------------
 //
 // Function
-//		Name:    HTTPServer::SendInternalErrorResponse(const char *, SocketStream &)
-//		Purpose: Sends an error response to the remote side
+//		Name:    HTTPServer::SendInternalErrorResponse(const char*,
+//			 HTTPResponse&)
+//		Purpose: Generates an error message in the provided response
 //		Created: 26/3/04
 //
 // --------------------------------------------------------------------------
-void HTTPServer::SendInternalErrorResponse(const char *Error, SocketStream &rStream)
+void HTTPServer::SendInternalErrorResponse(const std::string& rErrorMsg,
+	HTTPResponse& rResponse)
 {
 	#define ERROR_HTML_1 "<html><head><title>Internal Server Error</title></head>\n" \
 			"<h1>Internal Server Error</h1>\n" \
@@ -209,15 +211,11 @@
 			"</body>\n</html>\n"
 
 	// Generate the error page
-	HTTPResponse response(&rStream);
-	response.SetResponseCode(HTTPResponse::Code_InternalServerError);
-	response.SetContentType("text/html");
-	response.Write(ERROR_HTML_1, sizeof(ERROR_HTML_1) - 1);
-	response.Write(Error, ::strlen(Error));
-	response.Write(ERROR_HTML_2, sizeof(ERROR_HTML_2) - 1);
-
-	// Send the error response
-	response.Send();
+	// rResponse.SetResponseCode(HTTPResponse::Code_InternalServerError);
+	rResponse.SetContentType("text/html");
+	rResponse.Write(ERROR_HTML_1, sizeof(ERROR_HTML_1) - 1);
+	rResponse.IOStream::Write(rErrorMsg.c_str());
+	rResponse.Write(ERROR_HTML_2, sizeof(ERROR_HTML_2) - 1);
 }
 
 

Modified: box/trunk/lib/httpserver/HTTPServer.h
===================================================================
--- box/trunk/lib/httpserver/HTTPServer.h	2009-04-13 18:39:02 UTC (rev 2503)
+++ box/trunk/lib/httpserver/HTTPServer.h	2009-04-13 18:40:50 UTC (rev 2504)
@@ -52,15 +52,17 @@
 	virtual void HTTPConnectionOpening();
 	virtual void HTTPConnectionClosing();
 
+protected:
+	void SendInternalErrorResponse(const std::string& rErrorMsg,
+		HTTPResponse& rResponse);
+	int GetTimeout() { return mTimeout; }
+
 private:
+	int mTimeout;	// Timeout for read operations
 	const char *DaemonName() const;
 	const ConfigurationVerify *GetConfigVerify() const;
 	void Run();
 	void Connection(SocketStream &rStream);
-	void SendInternalErrorResponse(const char *Error, SocketStream &rStream);
-
-private:
-	int mTimeout;	// Timeout for read operations
 };
 
 // Root level

Copied: box/trunk/lib/httpserver/S3Simulator.cpp (from rev 2447, box/trunk/lib/httpserver/S3Client.cpp)
===================================================================
--- box/trunk/lib/httpserver/S3Simulator.cpp	                        (rev 0)
+++ box/trunk/lib/httpserver/S3Simulator.cpp	2009-04-13 18:40:50 UTC (rev 2504)
@@ -0,0 +1,308 @@
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    S3Client.cpp
+//		Purpose: Amazon S3 client helper implementation class
+//		Created: 09/01/2009
+//
+// --------------------------------------------------------------------------
+
+#include "Box.h"
+
+#include <cstring>
+
+// #include <cstdio>
+// #include <ctime>
+
+#include <openssl/hmac.h>
+
+#include "HTTPRequest.h"
+#include "HTTPResponse.h"
+#include "autogen_HTTPException.h"
+#include "IOStream.h"
+#include "Logging.h"
+#include "S3Simulator.h"
+#include "decode.h"
+#include "encode.h"
+
+#include "MemLeakFindOn.h"
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    HTTPServer::GetConfigVerify()
+//		Purpose: Returns additional configuration options for the
+//			 S3 simulator. Currently the access key, secret key
+//			 and store directory can be configured.
+//		Created: 09/01/09
+//
+// --------------------------------------------------------------------------
+const ConfigurationVerify* S3Simulator::GetConfigVerify() const
+{
+	static ConfigurationVerifyKey verifyserverkeys[] = 
+	{
+		HTTPSERVER_VERIFY_SERVER_KEYS(ConfigurationVerifyKey::NoDefaultValue) // no default addresses
+	};
+
+	static ConfigurationVerify verifyserver[] = 
+	{
+		{
+			"Server",
+			0,
+			verifyserverkeys,
+			ConfigTest_Exists | ConfigTest_LastEntry,
+			0
+		}
+	};
+	
+	static ConfigurationVerifyKey verifyrootkeys[] = 
+	{
+		ConfigurationVerifyKey("AccessKey", ConfigTest_Exists),
+		ConfigurationVerifyKey("SecretKey", ConfigTest_Exists),
+		ConfigurationVerifyKey("StoreDirectory", ConfigTest_Exists),
+		HTTPSERVER_VERIFY_ROOT_KEYS
+	};
+
+	static ConfigurationVerify verify =
+	{
+		"root",
+		verifyserver,
+		verifyrootkeys,
+		ConfigTest_Exists | ConfigTest_LastEntry,
+		0
+	};
+
+	return &verify;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    S3Simulator::Handle(HTTPRequest &rRequest,
+//			 HTTPResponse &rResponse)
+//		Purpose: Handles any incoming S3 request, by checking
+//			 authorization and then dispatching to one of the
+//			 private Handle* methods.
+//		Created: 09/01/09
+//
+// --------------------------------------------------------------------------
+
+void S3Simulator::Handle(HTTPRequest &rRequest, HTTPResponse &rResponse)
+{
+	// if anything goes wrong, return a 500 error
+	rResponse.SetResponseCode(HTTPResponse::Code_InternalServerError);
+	rResponse.SetContentType("text/plain");
+
+	try
+	{
+		const Configuration& rConfig(GetConfiguration());
+		std::string access_key = rConfig.GetKeyValue("AccessKey");
+		std::string secret_key = rConfig.GetKeyValue("SecretKey");
+		
+		std::string md5, date, bucket;
+		rRequest.GetHeader("content-md5", &md5);
+		rRequest.GetHeader("date", &date);
+		
+		std::string host = rRequest.GetHostName();
+		std::string s3suffix = ".s3.amazonaws.com";
+		if (host.size() > s3suffix.size())
+		{
+			std::string suffix = host.substr(host.size() -
+				s3suffix.size(), s3suffix.size());
+			if (suffix == s3suffix)
+			{
+				bucket = host.substr(0, host.size() -
+					s3suffix.size());
+			}
+		}
+		
+		std::ostringstream data;
+		data << rRequest.GetVerb() << "\n";
+		data << md5 << "\n";
+		data << rRequest.GetContentType() << "\n";
+		data << date << "\n";
+
+		// header names are already in lower case, i.e. canonical form
+
+		std::vector<HTTPRequest::Header> headers = rRequest.GetHeaders();
+		sort(headers.begin(), headers.end());
+		
+		for (std::vector<HTTPRequest::Header>::iterator
+			i = headers.begin(); i != headers.end(); i++)
+		{
+			if (i->first.substr(0, 5) == "x-amz")
+			{
+				data << i->first << ":" << i->second << "\n";
+			}
+		}		
+		
+		if (! bucket.empty())
+		{
+			data << "/" << bucket;
+		}
+		
+		data << rRequest.GetRequestURI();
+		std::string data_string = data.str();
+
+		unsigned char digest_buffer[EVP_MAX_MD_SIZE];
+		unsigned int digest_size = sizeof(digest_buffer);
+		/* unsigned char* mac = */ HMAC(EVP_sha1(),
+			secret_key.c_str(), secret_key.size(),
+			(const unsigned char*)data_string.c_str(),
+			data_string.size(), digest_buffer, &digest_size);
+		std::string digest((const char *)digest_buffer, digest_size);
+		
+		base64::encoder encoder;
+		std::string expectedAuth = "AWS " + access_key + ":" +
+			encoder.encode(digest);
+		
+		if (expectedAuth[expectedAuth.size() - 1] == '\n')
+		{
+			expectedAuth = expectedAuth.substr(0,
+				expectedAuth.size() - 1);
+		}
+		
+		std::string actualAuth;
+		if (!rRequest.GetHeader("authorization", &actualAuth) ||
+			actualAuth != expectedAuth)
+		{
+			rResponse.SetResponseCode(HTTPResponse::Code_Unauthorized);
+			SendInternalErrorResponse("Authentication Failed",
+				rResponse);
+		}	
+		else if (rRequest.GetMethod() == HTTPRequest::Method_GET)
+		{
+			HandleGet(rRequest, rResponse);
+		}
+		else if (rRequest.GetMethod() == HTTPRequest::Method_PUT)
+		{
+			HandlePut(rRequest, rResponse);
+		}
+		else
+		{
+			rResponse.SetResponseCode(HTTPResponse::Code_MethodNotAllowed);
+			SendInternalErrorResponse("Unsupported Method",
+				rResponse);
+		}
+	}
+	catch (CommonException &ce)
+	{
+		SendInternalErrorResponse(ce.what(), rResponse);
+	}
+	catch (std::exception &e)
+	{
+		SendInternalErrorResponse(e.what(), rResponse);
+	}
+	catch (...)
+	{
+		SendInternalErrorResponse("Unknown exception", rResponse);
+	}
+	
+	if (rResponse.GetResponseCode() != 200 &&
+		rResponse.GetSize() == 0)
+	{
+		// no error message written, provide a default
+		std::ostringstream s;
+		s << rResponse.GetResponseCode();
+		SendInternalErrorResponse(s.str().c_str(), rResponse);
+	}
+	
+	return;
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    S3Simulator::HandleGet(HTTPRequest &rRequest,
+//			 HTTPResponse &rResponse)
+//		Purpose: Handles an S3 GET request, i.e. downloading an
+//			 existing object.
+//		Created: 09/01/09
+//
+// --------------------------------------------------------------------------
+
+void S3Simulator::HandleGet(HTTPRequest &rRequest, HTTPResponse &rResponse)
+{
+	std::string path = GetConfiguration().GetKeyValue("StoreDirectory");
+	path += rRequest.GetRequestURI();
+	std::auto_ptr<FileStream> apFile;
+
+	try
+	{
+		apFile.reset(new FileStream(path));
+	}
+	catch (CommonException &ce)
+	{
+		if (ce.GetSubType() == CommonException::OSFileOpenError)
+		{
+			rResponse.SetResponseCode(HTTPResponse::Code_NotFound);
+		}
+		else if (ce.GetSubType() == CommonException::AccessDenied)
+		{
+			rResponse.SetResponseCode(HTTPResponse::Code_Forbidden);
+		}
+		throw;
+	}
+
+	// http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingRESTOperations.html
+	apFile->CopyStreamTo(rResponse);
+	rResponse.AddHeader("x-amz-id-2", "qBmKRcEWBBhH6XAqsKU/eg24V3jf/kWKN9dJip1L/FpbYr9FDy7wWFurfdQOEMcY");
+	rResponse.AddHeader("x-amz-request-id", "F2A8CCCA26B4B26D");
+	rResponse.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
+	rResponse.AddHeader("Last-Modified", "Sun, 1 Jan 2006 12:00:00 GMT");
+	rResponse.AddHeader("ETag", "\"828ef3fdfa96f00ad9f27c383fc9ac7f\"");
+	rResponse.AddHeader("Server", "AmazonS3");
+	rResponse.SetResponseCode(HTTPResponse::Code_OK);
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    S3Simulator::HandlePut(HTTPRequest &rRequest,
+//			 HTTPResponse &rResponse)
+//		Purpose: Handles an S3 PUT request, i.e. uploading data to
+//			 create or replace an object.
+//		Created: 09/01/09
+//
+// --------------------------------------------------------------------------
+
+void S3Simulator::HandlePut(HTTPRequest &rRequest, HTTPResponse &rResponse)
+{
+	std::string path = GetConfiguration().GetKeyValue("StoreDirectory");
+	path += rRequest.GetRequestURI();
+	std::auto_ptr<FileStream> apFile;
+
+	try
+	{
+		apFile.reset(new FileStream(path, O_CREAT | O_WRONLY));
+	}
+	catch (CommonException &ce)
+	{
+		if (ce.GetSubType() == CommonException::OSFileOpenError)
+		{
+			rResponse.SetResponseCode(HTTPResponse::Code_NotFound);
+		}
+		else if (ce.GetSubType() == CommonException::AccessDenied)
+		{
+			rResponse.SetResponseCode(HTTPResponse::Code_Forbidden);
+		}
+		throw;
+	}
+
+	if (rRequest.IsExpectingContinue())
+	{
+		rResponse.SendContinue();
+	}
+
+	rRequest.ReadContent(*apFile);
+
+	// http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTObjectPUT.html
+	rResponse.AddHeader("x-amz-id-2", "LriYPLdmOdAiIfgSm/F1YsViT1LW94/xUQxMsF7xiEb1a0wiIOIxl+zbwZ163pt7");
+	rResponse.AddHeader("x-amz-request-id", "F2A8CCCA26B4B26D");
+	rResponse.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
+	rResponse.AddHeader("Last-Modified", "Sun, 1 Jan 2006 12:00:00 GMT");
+	rResponse.AddHeader("ETag", "\"828ef3fdfa96f00ad9f27c383fc9ac7f\"");
+	rResponse.SetContentType("");
+	rResponse.AddHeader("Server", "AmazonS3");
+	rResponse.SetResponseCode(HTTPResponse::Code_OK);
+}

Copied: box/trunk/lib/httpserver/S3Simulator.h (from rev 2447, box/trunk/lib/httpserver/S3Client.h)
===================================================================
--- box/trunk/lib/httpserver/S3Simulator.h	                        (rev 0)
+++ box/trunk/lib/httpserver/S3Simulator.h	2009-04-13 18:40:50 UTC (rev 2504)
@@ -0,0 +1,40 @@
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    S3Simulator.h
+//		Purpose: Amazon S3 simulation HTTP server for S3 testing
+//		Created: 09/01/2009
+//
+// --------------------------------------------------------------------------
+
+#ifndef S3SIMULATOR__H
+#define S3SIMULATOR__H
+
+#include "HTTPServer.h"
+
+class ConfigurationVerify;
+class HTTPRequest;
+class HTTPResponse;
+
+// --------------------------------------------------------------------------
+//
+// Class
+//		Name:    S3Simulator
+//		Purpose: Amazon S3 simulation HTTP server for S3 testing
+//		Created: 09/01/2009
+//
+// --------------------------------------------------------------------------
+class S3Simulator : public HTTPServer
+{
+public:
+	S3Simulator() { }
+	~S3Simulator() { }
+
+	const ConfigurationVerify* GetConfigVerify() const;
+	virtual void Handle(HTTPRequest &rRequest, HTTPResponse &rResponse);
+	virtual void HandleGet(HTTPRequest &rRequest, HTTPResponse &rResponse);
+	virtual void HandlePut(HTTPRequest &rRequest, HTTPResponse &rResponse);
+};
+
+#endif // S3SIMULATOR__H
+

Modified: box/trunk/modules.txt
===================================================================
--- box/trunk/modules.txt	2009-04-13 18:39:02 UTC (rev 2503)
+++ box/trunk/modules.txt	2009-04-13 18:40:50 UTC (rev 2504)
@@ -44,6 +44,7 @@
 # HTTP server system
 lib/httpserver          lib/server 
 test/httpserver         lib/httpserver 
+bin/s3simulator		lib/httpserver
 
 # END_IF_DISTRIBUTION
 

Copied: box/trunk/test/httpserver/testfiles/s3simulator.conf (from rev 2422, box/trunk/test/httpserver/testfiles/httpserver.conf)
===================================================================
--- box/trunk/test/httpserver/testfiles/s3simulator.conf	                        (rev 0)
+++ box/trunk/test/httpserver/testfiles/s3simulator.conf	2009-04-13 18:40:50 UTC (rev 2504)
@@ -0,0 +1,10 @@
+AccessKey = 0PN5J17HBGZHT7JJ3X82
+SecretKey = uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o
+StoreDirectory = testfiles
+AddressPrefix = http://localhost:1080
+
+Server
+{
+	PidFile = testfiles/s3simulator.pid
+	ListenAddresses = inet:localhost:1080
+}

Modified: box/trunk/test/httpserver/testhttpserver.cpp
===================================================================
--- box/trunk/test/httpserver/testhttpserver.cpp	2009-04-13 18:39:02 UTC (rev 2503)
+++ box/trunk/test/httpserver/testhttpserver.cpp	2009-04-13 18:40:50 UTC (rev 2504)
@@ -26,6 +26,7 @@
 #include "HTTPServer.h"
 #include "IOStreamGetLine.h"
 #include "S3Client.h"
+#include "S3Simulator.h"
 #include "ServerControl.h"
 #include "Test.h"
 #include "decode.h"
@@ -125,213 +126,6 @@
 TestWebServer::TestWebServer() {}
 TestWebServer::~TestWebServer() {}
 
-class S3Simulator : public HTTPServer
-{
-public:
-	S3Simulator() { }
-	~S3Simulator() { }
-
-	virtual void Handle(HTTPRequest &rRequest, HTTPResponse &rResponse);
-	virtual void HandleGet(HTTPRequest &rRequest, HTTPResponse &rResponse);
-	virtual void HandlePut(HTTPRequest &rRequest, HTTPResponse &rResponse);
-};
-
-void S3Simulator::Handle(HTTPRequest &rRequest, HTTPResponse &rResponse)
-{
-	// if anything goes wrong, return a 500 error
-	rResponse.SetResponseCode(HTTPResponse::Code_InternalServerError);
-	rResponse.SetContentType("text/plain");
-
-	try
-	{
-		// http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAuthentication.html
-		std::string access_key = "0PN5J17HBGZHT7JJ3X82";
-		std::string secret_key = "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o";
-		
-		std::string md5, date, bucket;
-		rRequest.GetHeader("Content-MD5", &md5);
-		rRequest.GetHeader("Date", &date);
-		
-		std::string host = rRequest.GetHostName();
-		std::string s3suffix = ".s3.amazonaws.com";
-		if (host.size() > s3suffix.size())
-		{
-			std::string suffix = host.substr(host.size() -
-				s3suffix.size(), s3suffix.size());
-			if (suffix == s3suffix)
-			{
-				bucket = host.substr(0, host.size() -
-					s3suffix.size());
-			}
-		}
-		
-		std::ostringstream data;
-		data << rRequest.GetVerb() << "\n";
-		data << md5 << "\n";
-		data << rRequest.GetContentType() << "\n";
-		data << date << "\n";
-		
-		std::vector<HTTPRequest::Header> headers = rRequest.GetHeaders();
-		
-		for (std::vector<HTTPRequest::Header>::iterator
-			i = headers.begin(); i != headers.end(); i++)
-		{
-			std::string& rHeaderName = i->first;
-			
-			for (std::string::iterator c = rHeaderName.begin();
-				c != rHeaderName.end() && *c != ':'; c++)
-			{
-				*c = tolower(*c);
-			}
-		}
-		
-		sort(headers.begin(), headers.end());
-		
-		for (std::vector<HTTPRequest::Header>::iterator
-			i = headers.begin(); i != headers.end(); i++)
-		{
-			if (i->first.substr(0, 5) == "x-amz")
-			{
-				data << i->first << ":" << i->second << "\n";
-			}
-		}		
-		
-		if (! bucket.empty())
-		{
-			data << "/" << bucket;
-		}
-		
-		data << rRequest.GetRequestURI();
-		std::string data_string = data.str();
-
-		unsigned char digest_buffer[EVP_MAX_MD_SIZE];
-		unsigned int digest_size = sizeof(digest_buffer);
-		/* unsigned char* mac = */ HMAC(EVP_sha1(),
-			secret_key.c_str(), secret_key.size(),
-			(const unsigned char*)data_string.c_str(),
-			data_string.size(), digest_buffer, &digest_size);
-		std::string digest((const char *)digest_buffer, digest_size);
-		
-		base64::encoder encoder;
-		std::string expectedAuth = "AWS " + access_key + ":" +
-			encoder.encode(digest);
-		
-		if (expectedAuth[expectedAuth.size() - 1] == '\n')
-		{
-			expectedAuth = expectedAuth.substr(0,
-				expectedAuth.size() - 1);
-		}
-		
-		std::string actualAuth;
-		if (!rRequest.GetHeader("Authorization", &actualAuth) ||
-			actualAuth != expectedAuth)
-		{
-			rResponse.SetResponseCode(HTTPResponse::Code_Unauthorized);
-		}	
-		else if (rRequest.GetMethod() == HTTPRequest::Method_GET)
-		{
-			HandleGet(rRequest, rResponse);
-		}
-		else if (rRequest.GetMethod() == HTTPRequest::Method_PUT)
-		{
-			HandlePut(rRequest, rResponse);
-		}
-		else
-		{
-			rResponse.SetResponseCode(HTTPResponse::Code_MethodNotAllowed);
-		}
-	}
-	catch (CommonException &ce)
-	{
-		rResponse.IOStream::Write(ce.what());
-	}
-	catch (std::exception &e)
-	{
-		rResponse.IOStream::Write(e.what());
-	}
-	catch (...)
-	{
-		rResponse.IOStream::Write("Unknown error");
-	}
-	
-	return;
-}
-
-void S3Simulator::HandleGet(HTTPRequest &rRequest, HTTPResponse &rResponse)
-{
-	std::string path = "testfiles";
-	path += rRequest.GetRequestURI();
-	std::auto_ptr<FileStream> apFile;
-
-	try
-	{
-		apFile.reset(new FileStream(path));
-	}
-	catch (CommonException &ce)
-	{
-		if (ce.GetSubType() == CommonException::OSFileOpenError)
-		{
-			rResponse.SetResponseCode(HTTPResponse::Code_NotFound);
-		}
-		else if (ce.GetSubType() == CommonException::AccessDenied)
-		{
-			rResponse.SetResponseCode(HTTPResponse::Code_Forbidden);
-		}
-		throw;
-	}
-
-	// http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingRESTOperations.html
-	apFile->CopyStreamTo(rResponse);
-	rResponse.AddHeader("x-amz-id-2", "qBmKRcEWBBhH6XAqsKU/eg24V3jf/kWKN9dJip1L/FpbYr9FDy7wWFurfdQOEMcY");
-	rResponse.AddHeader("x-amz-request-id", "F2A8CCCA26B4B26D");
-	rResponse.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
-	rResponse.AddHeader("Last-Modified", "Sun, 1 Jan 2006 12:00:00 GMT");
-	rResponse.AddHeader("ETag", "\"828ef3fdfa96f00ad9f27c383fc9ac7f\"");
-	rResponse.AddHeader("Server", "AmazonS3");
-	rResponse.SetResponseCode(HTTPResponse::Code_OK);
-}
-
-void S3Simulator::HandlePut(HTTPRequest &rRequest, HTTPResponse &rResponse)
-{
-	std::string path = "testfiles";
-	path += rRequest.GetRequestURI();
-	std::auto_ptr<FileStream> apFile;
-
-	try
-	{
-		apFile.reset(new FileStream(path, O_CREAT | O_WRONLY));
-	}
-	catch (CommonException &ce)
-	{
-		if (ce.GetSubType() == CommonException::OSFileOpenError)
-		{
-			rResponse.SetResponseCode(HTTPResponse::Code_NotFound);
-		}
-		else if (ce.GetSubType() == CommonException::AccessDenied)
-		{
-			rResponse.SetResponseCode(HTTPResponse::Code_Forbidden);
-		}
-		throw;
-	}
-
-	if (rRequest.IsExpectingContinue())
-	{
-		rResponse.SendContinue();
-	}
-
-	rRequest.ReadContent(*apFile);
-
-	// http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTObjectPUT.html
-	rResponse.AddHeader("x-amz-id-2", "LriYPLdmOdAiIfgSm/F1YsViT1LW94/xUQxMsF7xiEb1a0wiIOIxl+zbwZ163pt7");
-	rResponse.AddHeader("x-amz-request-id", "F2A8CCCA26B4B26D");
-	rResponse.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
-	rResponse.AddHeader("Last-Modified", "Sun, 1 Jan 2006 12:00:00 GMT");
-	rResponse.AddHeader("ETag", "\"828ef3fdfa96f00ad9f27c383fc9ac7f\"");
-	rResponse.SetContentType("");
-	rResponse.AddHeader("Server", "AmazonS3");
-	rResponse.SetResponseCode(HTTPResponse::Code_OK);
-}
-
 int test(int argc, const char *argv[])
 {
 	if(argc >= 2 && ::strcmp(argv[1], "server") == 0)
@@ -446,16 +240,17 @@
 	TEST_THAT(KillServer(pid));
 	TestRemoteProcessMemLeaks("generic-httpserver.memleaks");
 
-	// correct, official signature should succeed
+	// correct, official signature should succeed, with lower-case header
 	{
 		// http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAuthentication.html
 		HTTPRequest request(HTTPRequest::Method_GET, "/photos/puppy.jpg");
 		request.SetHostName("johnsmith.s3.amazonaws.com");
-		request.AddHeader("Date", "Tue, 27 Mar 2007 19:36:42 +0000");
-		request.AddHeader("Authorization",
+		request.AddHeader("date", "Tue, 27 Mar 2007 19:36:42 +0000");
+		request.AddHeader("authorization",
 			"AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA=");
 		
 		S3Simulator simulator;
+		simulator.Configure("testfiles/s3simulator.conf");
 		
 		CollectInBufferStream response_buffer;
 		HTTPResponse response(&response_buffer);
@@ -473,11 +268,12 @@
 		// http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAuthentication.html
 		HTTPRequest request(HTTPRequest::Method_GET, "/photos/puppy.jpg");
 		request.SetHostName("johnsmith.s3.amazonaws.com");
-		request.AddHeader("Date", "Tue, 27 Mar 2007 19:36:42 +0000");
-		request.AddHeader("Authorization",
+		request.AddHeader("date", "Tue, 27 Mar 2007 19:36:42 +0000");
+		request.AddHeader("authorization",
 			"AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbB=");
 		
 		S3Simulator simulator;
+		simulator.Configure("testfiles/s3simulator.conf");
 		
 		CollectInBufferStream response_buffer;
 		HTTPResponse response(&response_buffer);
@@ -487,12 +283,19 @@
 		
 		std::string response_data((const char *)response.GetBuffer(),
 			response.GetSize());
-		TEST_EQUAL("", response_data);
+		TEST_EQUAL("<html><head>"
+			"<title>Internal Server Error</title></head>\n"
+			"<h1>Internal Server Error</h1>\n"
+			"<p>An error, type Authentication Failed occured "
+			"when processing the request.</p>"
+			"<p>Please try again later.</p></body>\n"
+			"</html>\n", response_data);
 	}
 
 	// S3Client tests
 	{
 		S3Simulator simulator;
+		simulator.Configure("testfiles/s3simulator.conf");
 		S3Client client(&simulator, "johnsmith.s3.amazonaws.com",
 			"0PN5J17HBGZHT7JJ3X82",
 			"uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o");
@@ -527,8 +330,8 @@
 		HTTPRequest request(HTTPRequest::Method_PUT,
 			"/newfile");
 		request.SetHostName("quotes.s3.amazonaws.com");
-		request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
-		request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:XtMYZf0hdOo4TdPYQknZk0Lz7rw=");
+		request.AddHeader("date", "Wed, 01 Mar  2006 12:00:00 GMT");
+		request.AddHeader("authorization", "AWS 0PN5J17HBGZHT7JJ3X82:XtMYZf0hdOo4TdPYQknZk0Lz7rw=");
 		request.AddHeader("Content-Type", "text/plain");
 		
 		FileStream fs("testfiles/testrequests.pl");
@@ -539,6 +342,7 @@
 		HTTPResponse response(&response_buffer);
 		
 		S3Simulator simulator;
+		simulator.Configure("testfiles/s3simulator.conf");
 		simulator.Handle(request, response);
 		
 		TEST_EQUAL(200, response.GetResponseCode());
@@ -558,8 +362,8 @@
 	}
 
 	// Start the S3Simulator server
-	pid = LaunchServer("./test s3server testfiles/httpserver.conf",
-		"testfiles/httpserver.pid");
+	pid = LaunchServer("./test s3server testfiles/s3simulator.conf",
+		"testfiles/s3simulator.pid");
 	TEST_THAT(pid != -1 && pid != 0);
 	if(pid <= 0)
 	{




More information about the Boxbackup-commit mailing list