[Box Backup-commit] COMMIT r2447 - in box/trunk: lib/httpserver test/httpserver

boxbackup-dev@boxbackup.org boxbackup-dev@boxbackup.org
Fri, 9 Jan 2009 11:50:10 +0000 (GMT)


Author: chris
Date: 2009-01-09 11:50:10 +0000 (Fri, 09 Jan 2009)
New Revision: 2447

Added:
   box/trunk/lib/httpserver/S3Client.cpp
   box/trunk/lib/httpserver/S3Client.h
Modified:
   box/trunk/test/httpserver/testhttpserver.cpp
Log:
Move S3Client class into its own files for public access.


Added: box/trunk/lib/httpserver/S3Client.cpp
===================================================================
--- box/trunk/lib/httpserver/S3Client.cpp	                        (rev 0)
+++ box/trunk/lib/httpserver/S3Client.cpp	2009-01-09 11:50:10 UTC (rev 2447)
@@ -0,0 +1,243 @@
+// --------------------------------------------------------------------------
+//
+// 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 "HTTPServer.h"
+#include "autogen_HTTPException.h"
+#include "IOStream.h"
+#include "Logging.h"
+#include "S3Client.h"
+#include "decode.h"
+#include "encode.h"
+
+#include "MemLeakFindOn.h"
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    S3Client::GetObject(const std::string& rObjectURI)
+//		Purpose: Retrieve the object with the specified URI (key)
+//			 from your S3 bucket.
+//		Created: 09/01/09
+//
+// --------------------------------------------------------------------------
+
+HTTPResponse S3Client::GetObject(const std::string& rObjectURI)
+{
+	return FinishAndSendRequest(HTTPRequest::Method_GET, rObjectURI);
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    S3Client::PutObject(const std::string& rObjectURI,
+//			 IOStream& rStreamToSend, const char* pContentType)
+//		Purpose: Upload the stream to S3, creating or overwriting the
+//			 object with the specified URI (key) in your S3
+//			 bucket.
+//		Created: 09/01/09
+//
+// --------------------------------------------------------------------------
+
+HTTPResponse S3Client::PutObject(const std::string& rObjectURI,
+	IOStream& rStreamToSend, const char* pContentType)
+{
+	return FinishAndSendRequest(HTTPRequest::Method_PUT, rObjectURI,
+		&rStreamToSend, pContentType);
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    S3Client::FinishAndSendRequest(
+//			 HTTPRequest::Method Method,
+//			 const std::string& rRequestURI,
+//			 IOStream* pStreamToSend,
+//			 const char* pStreamContentType)
+//		Purpose: Internal method which creates an HTTP request to S3,
+//			 populates the date and authorization header fields,
+//			 and sends it to S3 (or the simulator), attaching
+//			 the specified stream if any to the request. Opens a
+//			 connection to the server if necessary, which may
+//			 throw a ConnectionException. Returns the HTTP
+//			 response returned by S3, which may be a 500 error.
+//		Created: 09/01/09
+//
+// --------------------------------------------------------------------------
+
+HTTPResponse S3Client::FinishAndSendRequest(HTTPRequest::Method Method,
+	const std::string& rRequestURI, IOStream* pStreamToSend,
+	const char* pStreamContentType)
+{
+	HTTPRequest request(Method, rRequestURI);
+	request.SetHostName(mHostName);
+	
+	std::ostringstream date;
+	time_t tt = time(NULL);
+	struct tm *tp = gmtime(&tt);
+	if (!tp)
+	{
+		BOX_ERROR("Failed to get current time");
+		THROW_EXCEPTION(HTTPException, Internal);
+	}
+	const char *dow[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+	date << dow[tp->tm_wday] << ", ";
+	const char *month[] = {"Jan","Feb","Mar","Apr","May","Jun",
+		"Jul","Aug","Sep","Oct","Nov","Dec"};
+	date << std::internal << std::setfill('0') <<
+		std::setw(2) << tp->tm_mday << " " <<
+		month[tp->tm_mon] << " " <<
+		(tp->tm_year + 1900) << " ";
+	date << std::setw(2) << tp->tm_hour << ":" <<
+		std::setw(2) << tp->tm_min  << ":" <<
+		std::setw(2) << tp->tm_sec  << " GMT";
+	request.AddHeader("Date", date.str());
+
+	if (pStreamContentType)
+	{
+		request.AddHeader("Content-Type", pStreamContentType);
+	}
+	
+	std::string s3suffix = ".s3.amazonaws.com";
+	std::string bucket;
+	if (mHostName.size() > s3suffix.size())
+	{
+		std::string suffix = mHostName.substr(mHostName.size() -
+			s3suffix.size(), s3suffix.size());
+		if (suffix == s3suffix)
+		{
+			bucket = mHostName.substr(0, mHostName.size() -
+				s3suffix.size());
+		}
+	}
+	
+	std::ostringstream data;
+	data << request.GetVerb() << "\n";
+	data << "\n"; /* Content-MD5 */
+	data << request.GetContentType() << "\n";
+	data << date.str() << "\n";
+		
+	if (! bucket.empty())
+	{
+		data << "/" << bucket;
+	}
+	
+	data << request.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(),
+		mSecretKey.c_str(), mSecretKey.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 auth_code = "AWS " + mAccessKey + ":" +
+		encoder.encode(digest);
+
+	if (auth_code[auth_code.size() - 1] == '\n')
+	{
+		auth_code = auth_code.substr(0, auth_code.size() - 1);
+	}
+
+	request.AddHeader("Authorization", auth_code);
+	
+	if (mpSimulator)
+	{
+		if (pStreamToSend)
+		{
+			pStreamToSend->CopyStreamTo(request);
+		}
+
+		request.SetForReading();
+		CollectInBufferStream response_buffer;
+		HTTPResponse response(&response_buffer);
+	
+		mpSimulator->Handle(request, response);
+		return response;
+	}
+	else
+	{
+		try
+		{
+			if (!mapClientSocket.get())
+			{
+				mapClientSocket.reset(new SocketStream());
+				mapClientSocket->Open(Socket::TypeINET,
+					mHostName, mPort);
+			}
+			return SendRequest(request, pStreamToSend,
+				pStreamContentType);
+		}
+		catch (ConnectionException &ce)
+		{
+			if (ce.GetType() == ConnectionException::SocketWriteError)
+			{
+				// server may have disconnected us,
+				// try to reconnect, just once
+				mapClientSocket->Open(Socket::TypeINET,
+					mHostName, mPort);
+				return SendRequest(request, pStreamToSend,
+					pStreamContentType);
+			}
+			else
+			{
+				throw;
+			}
+		}
+	}
+}
+
+// --------------------------------------------------------------------------
+//
+// Function
+//		Name:    S3Client::SendRequest(HTTPRequest& rRequest,
+//			 IOStream* pStreamToSend,
+//			 const char* pStreamContentType)
+//		Purpose: Internal method which sends a pre-existing HTTP 
+//			 request to S3. Attaches the specified stream if any
+//			 to the request. Opens a connection to the server if
+//			 necessary, which may throw a ConnectionException.
+//			 Returns the HTTP response returned by S3, which may
+//			 be a 500 error.
+//		Created: 09/01/09
+//
+// --------------------------------------------------------------------------
+
+HTTPResponse S3Client::SendRequest(HTTPRequest& rRequest,
+	IOStream* pStreamToSend, const char* pStreamContentType)
+{		
+	HTTPResponse response;
+	
+	if (pStreamToSend)
+	{
+		rRequest.SendWithStream(*mapClientSocket,
+			30000 /* milliseconds */,
+			pStreamToSend, response);
+	}
+	else
+	{
+		rRequest.Send(*mapClientSocket, 30000 /* milliseconds */);
+		response.Receive(*mapClientSocket, 30000 /* milliseconds */);
+	}
+		
+	return response;
+}	


Property changes on: box/trunk/lib/httpserver/S3Client.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/x-c++src
Added: svn:eol-style
   + native

Added: box/trunk/lib/httpserver/S3Client.h
===================================================================
--- box/trunk/lib/httpserver/S3Client.h	                        (rev 0)
+++ box/trunk/lib/httpserver/S3Client.h	2009-01-09 11:50:10 UTC (rev 2447)
@@ -0,0 +1,72 @@
+// --------------------------------------------------------------------------
+//
+// File
+//		Name:    S3Client.h
+//		Purpose: Amazon S3 client helper implementation class
+//		Created: 09/01/2009
+//
+// --------------------------------------------------------------------------
+
+#ifndef S3CLIENT__H
+#define S3CLIENT__H
+
+#include <string>
+#include <map>
+
+#include "HTTPRequest.h"
+#include "SocketStream.h"
+
+class HTTPResponse;
+class HTTPServer;
+class IOStream;
+
+// --------------------------------------------------------------------------
+//
+// Class
+//		Name:    S3Client
+//		Purpose: Amazon S3 client helper implementation class
+//		Created: 09/01/2009
+//
+// --------------------------------------------------------------------------
+class S3Client
+{
+	public:
+	S3Client(HTTPServer* pSimulator, const std::string& rHostName,
+		const std::string& rAccessKey, const std::string& rSecretKey)
+	: mpSimulator(pSimulator),
+	  mHostName(rHostName),
+	  mAccessKey(rAccessKey),
+	  mSecretKey(rSecretKey)
+	{ }
+	
+	S3Client(std::string HostName, int Port, const std::string& rAccessKey,
+		const std::string& rSecretKey)
+	: mpSimulator(NULL),
+	  mHostName(HostName),
+	  mPort(Port),
+	  mAccessKey(rAccessKey),
+	  mSecretKey(rSecretKey)
+	{ }
+		
+	HTTPResponse GetObject(const std::string& rObjectURI);
+	HTTPResponse PutObject(const std::string& rObjectURI,
+		IOStream& rStreamToSend, const char* pContentType = NULL);
+
+	private:
+	HTTPServer* mpSimulator;
+	std::string mHostName;
+	int mPort;
+	std::auto_ptr<SocketStream> mapClientSocket;
+	std::string mAccessKey, mSecretKey;
+
+	HTTPResponse FinishAndSendRequest(HTTPRequest::Method Method,
+		const std::string& rRequestURI,
+		IOStream* pStreamToSend = NULL,
+		const char* pStreamContentType = NULL);
+	HTTPResponse SendRequest(HTTPRequest& rRequest,
+		IOStream* pStreamToSend = NULL,
+		const char* pStreamContentType = NULL);
+};
+
+#endif // S3CLIENT__H
+


Property changes on: box/trunk/lib/httpserver/S3Client.h
___________________________________________________________________
Added: svn:mime-type
   + text/x-chdr
Added: svn:eol-style
   + native

Modified: box/trunk/test/httpserver/testhttpserver.cpp
===================================================================
--- box/trunk/test/httpserver/testhttpserver.cpp	2009-01-09 10:19:46 UTC (rev 2446)
+++ box/trunk/test/httpserver/testhttpserver.cpp	2009-01-09 11:50:10 UTC (rev 2447)
@@ -20,6 +20,7 @@
 #include "HTTPResponse.h"
 #include "HTTPServer.h"
 #include "IOStreamGetLine.h"
+#include "S3Client.h"
 #include "ServerControl.h"
 #include "Test.h"
 #include "decode.h"
@@ -326,203 +327,6 @@
 	rResponse.SetResponseCode(HTTPResponse::Code_OK);
 }
 
-class S3Client
-{
-	public:
-	S3Client(S3Simulator* pSimulator, const std::string& rHostName,
-		const std::string& rAccessKey, const std::string& rSecretKey)
-	: mpSimulator(pSimulator),
-	  mHostName(rHostName),
-	  mAccessKey(rAccessKey),
-	  mSecretKey(rSecretKey)
-	{ }
-	
-	S3Client(std::string HostName, int Port, const std::string& rAccessKey,
-		const std::string& rSecretKey)
-	: mpSimulator(NULL),
-	  mHostName(HostName),
-	  mPort(Port),
-	  mAccessKey(rAccessKey),
-	  mSecretKey(rSecretKey)
-	{ }
-		
-	HTTPResponse GetObject(const std::string& rObjectURI);
-	HTTPResponse PutObject(const std::string& rObjectURI,
-		IOStream& rStreamToSend, const char* pContentType = NULL);
-
-	private:
-	S3Simulator* mpSimulator;
-	std::string mHostName;
-	int mPort;
-	std::auto_ptr<SocketStream> mapClientSocket;
-	std::string mAccessKey, mSecretKey;
-
-	HTTPResponse FinishAndSendRequest(HTTPRequest::Method Method,
-		const std::string& rRequestURI,
-		IOStream* pStreamToSend = NULL,
-		const char* pStreamContentType = NULL);
-	HTTPResponse SendRequest(HTTPRequest& rRequest,
-		IOStream* pStreamToSend = NULL,
-		const char* pStreamContentType = NULL);
-};
-
-HTTPResponse S3Client::GetObject(const std::string& rObjectURI)
-{
-	return FinishAndSendRequest(HTTPRequest::Method_GET, rObjectURI);
-}
-
-HTTPResponse S3Client::PutObject(const std::string& rObjectURI,
-	IOStream& rStreamToSend, const char* pContentType)
-{
-	return FinishAndSendRequest(HTTPRequest::Method_PUT, rObjectURI,
-		&rStreamToSend, pContentType);
-}
-
-HTTPResponse S3Client::FinishAndSendRequest(HTTPRequest::Method Method,
-	const std::string& rRequestURI, IOStream* pStreamToSend,
-	const char* pStreamContentType)
-{
-	HTTPRequest request(Method, rRequestURI);
-	request.SetHostName(mHostName);
-	
-	std::ostringstream date;
-	time_t tt = time(NULL);
-	struct tm *tp = gmtime(&tt);
-	if (!tp)
-	{
-		BOX_ERROR("Failed to get current time");
-		THROW_EXCEPTION(HTTPException, Internal);
-	}
-	const char *dow[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
-	date << dow[tp->tm_wday] << ", ";
-	const char *month[] = {"Jan","Feb","Mar","Apr","May","Jun",
-		"Jul","Aug","Sep","Oct","Nov","Dec"};
-	date << std::internal << std::setfill('0') <<
-		std::setw(2) << tp->tm_mday << " " <<
-		month[tp->tm_mon] << " " <<
-		(tp->tm_year + 1900) << " ";
-	date << std::setw(2) << tp->tm_hour << ":" <<
-		std::setw(2) << tp->tm_min  << ":" <<
-		std::setw(2) << tp->tm_sec  << " GMT";
-	request.AddHeader("Date", date.str());
-
-	if (pStreamContentType)
-	{
-		request.AddHeader("Content-Type", pStreamContentType);
-	}
-	
-	std::string s3suffix = ".s3.amazonaws.com";
-	std::string bucket;
-	if (mHostName.size() > s3suffix.size())
-	{
-		std::string suffix = mHostName.substr(mHostName.size() -
-			s3suffix.size(), s3suffix.size());
-		if (suffix == s3suffix)
-		{
-			bucket = mHostName.substr(0, mHostName.size() -
-				s3suffix.size());
-		}
-	}
-	
-	std::ostringstream data;
-	data << request.GetVerb() << "\n";
-	data << "\n"; /* Content-MD5 */
-	data << request.GetContentType() << "\n";
-	data << date.str() << "\n";
-		
-	if (! bucket.empty())
-	{
-		data << "/" << bucket;
-	}
-	
-	data << request.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(),
-		mSecretKey.c_str(), mSecretKey.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 auth_code = "AWS " + mAccessKey + ":" +
-		encoder.encode(digest);
-
-	if (auth_code[auth_code.size() - 1] == '\n')
-	{
-		auth_code = auth_code.substr(0, auth_code.size() - 1);
-	}
-
-	request.AddHeader("Authorization", auth_code);
-	
-	if (mpSimulator)
-	{
-		if (pStreamToSend)
-		{
-			pStreamToSend->CopyStreamTo(request);
-		}
-
-		request.SetForReading();
-		CollectInBufferStream response_buffer;
-		HTTPResponse response(&response_buffer);
-	
-		mpSimulator->Handle(request, response);
-		return response;
-	}
-	else
-	{
-		try
-		{
-			if (!mapClientSocket.get())
-			{
-				mapClientSocket.reset(new SocketStream());
-				mapClientSocket->Open(Socket::TypeINET,
-					mHostName, mPort);
-			}
-			return SendRequest(request, pStreamToSend,
-				pStreamContentType);
-		}
-		catch (ConnectionException &ce)
-		{
-			if (ce.GetType() == ConnectionException::SocketWriteError)
-			{
-				// server may have disconnected us,
-				// try to reconnect, just once
-				mapClientSocket->Open(Socket::TypeINET,
-					mHostName, mPort);
-				return SendRequest(request, pStreamToSend,
-					pStreamContentType);
-			}
-			else
-			{
-				throw;
-			}
-		}
-	}
-}
-
-HTTPResponse S3Client::SendRequest(HTTPRequest& rRequest,
-	IOStream* pStreamToSend, const char* pStreamContentType)
-{		
-	HTTPResponse response;
-	
-	if (pStreamToSend)
-	{
-		rRequest.SendWithStream(*mapClientSocket,
-			30000 /* milliseconds */,
-			pStreamToSend, response);
-	}
-	else
-	{
-		rRequest.Send(*mapClientSocket, 30000 /* milliseconds */);
-		response.Receive(*mapClientSocket, 30000 /* milliseconds */);
-	}
-		
-	return response;
-}	
-
 int test(int argc, const char *argv[])
 {
 	if(argc >= 2 && ::strcmp(argv[1], "server") == 0)