[Box Backup-commit] COMMIT r2442 - in box/trunk: lib/httpserver test/httpserver test/httpserver/testfiles test/httpserver/testfiles/photos

boxbackup-dev@boxbackup.org boxbackup-dev@boxbackup.org
Tue, 6 Jan 2009 13:18:08 +0000 (GMT)


Author: chris
Date: 2009-01-06 13:18:07 +0000 (Tue, 06 Jan 2009)
New Revision: 2442

Added:
   box/trunk/lib/httpserver/cdecode.cpp
   box/trunk/lib/httpserver/cdecode.h
   box/trunk/lib/httpserver/cencode.cpp
   box/trunk/lib/httpserver/cencode.h
   box/trunk/lib/httpserver/decode.h
   box/trunk/lib/httpserver/encode.h
   box/trunk/test/httpserver/testfiles/photos/
   box/trunk/test/httpserver/testfiles/photos/puppy.jpg
Modified:
   box/trunk/lib/httpserver/HTTPRequest.cpp
   box/trunk/lib/httpserver/HTTPRequest.h
   box/trunk/test/httpserver/testhttpserver.cpp
Log:
Add Amazon S3 signature checking to simulator.


Modified: box/trunk/lib/httpserver/HTTPRequest.cpp
===================================================================
--- box/trunk/lib/httpserver/HTTPRequest.cpp	2009-01-05 00:50:30 UTC (rev 2441)
+++ box/trunk/lib/httpserver/HTTPRequest.cpp	2009-01-06 13:18:07 UTC (rev 2442)
@@ -134,20 +134,20 @@
 	}
 	else
 	{
-		std::string method = requestLine.substr(0, p);
-		if (method == "GET")
+		mHttpVerb = requestLine.substr(0, p);
+		if (mHttpVerb == "GET")
 		{
 			mMethod = Method_GET;
 		}
-		else if (method == "HEAD")
+		else if (mHttpVerb == "HEAD")
 		{
 			mMethod = Method_HEAD;
 		}
-		else if (method == "POST")
+		else if (mHttpVerb == "POST")
 		{
 			mMethod = Method_POST;
 		}
-		else if (method == "PUT")
+		else if (mHttpVerb == "PUT")
 		{
 			mMethod = Method_PUT;
 		}

Modified: box/trunk/lib/httpserver/HTTPRequest.h
===================================================================
--- box/trunk/lib/httpserver/HTTPRequest.h	2009-01-05 00:50:30 UTC (rev 2441)
+++ box/trunk/lib/httpserver/HTTPRequest.h	2009-01-06 13:18:07 UTC (rev 2442)
@@ -86,7 +86,7 @@
 		mHostName = rHostName;
 	}
 
-	const int GetHostPort() const {return mHostPort;}  // into host name and port number
+	const int GetHostPort() const {return mHostPort;}
 	const std::string &GetQueryString() const {return mQueryString;}
 	int GetHTTPVersion() const {return mHTTPVersion;}
 	const Query_t &GetQuery() const {return mQuery;}
@@ -109,6 +109,7 @@
 		}
 		return false;
 	}
+	std::vector<Header> GetHeaders() { return mExtraHeaders; }
 
 	// --------------------------------------------------------------------------
 	//
@@ -130,6 +131,23 @@
 		mExtraHeaders.push_back(Header(rName, rValue));
 	}
 	bool IsExpectingContinue() const { return mExpectContinue; }
+	const char* GetVerb() const
+	{
+		if (!mHttpVerb.empty())
+		{
+			return mHttpVerb.c_str();
+		}
+		switch (mMethod)
+		{
+			case Method_UNINITIALISED: return "Uninitialized";
+			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";
+		}
+		return "Bad";
+	}
 	
 private:
 	void ParseHeaders(IOStreamGetLine &rGetLine, int Timeout);
@@ -149,6 +167,7 @@
 	std::vector<Header> mExtraHeaders;
 	bool mExpectContinue;
 	IOStream* mpStreamToReadFrom;
+	std::string mHttpVerb;
 };
 
 #endif // HTTPREQUEST__H

Added: box/trunk/lib/httpserver/cdecode.cpp
===================================================================
--- box/trunk/lib/httpserver/cdecode.cpp	                        (rev 0)
+++ box/trunk/lib/httpserver/cdecode.cpp	2009-01-06 13:18:07 UTC (rev 2442)
@@ -0,0 +1,92 @@
+/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+extern "C"
+{
+
+#include "cdecode.h"
+
+int base64_decode_value(char value_in)
+{
+	static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+	static const char decoding_size = sizeof(decoding);
+	value_in -= 43;
+	if (value_in < 0 || value_in > decoding_size) return -1;
+	return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(base64_decodestate* state_in)
+{
+	state_in->step = step_a;
+	state_in->plainchar = 0;
+}
+
+int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
+{
+	const char* codechar = code_in;
+	char* plainchar = plaintext_out;
+	char fragment;
+	
+	*plainchar = state_in->plainchar;
+	
+	switch (state_in->step)
+	{
+		while (1)
+		{
+	case step_a:
+			do {
+				if (codechar == code_in+length_in)
+				{
+					state_in->step = step_a;
+					state_in->plainchar = *plainchar;
+					return plainchar - plaintext_out;
+				}
+				fragment = (char)base64_decode_value(*codechar++);
+			} while (fragment < 0);
+			*plainchar    = (fragment & 0x03f) << 2;
+	case step_b:
+			do {
+				if (codechar == code_in+length_in)
+				{
+					state_in->step = step_b;
+					state_in->plainchar = *plainchar;
+					return plainchar - plaintext_out;
+				}
+				fragment = (char)base64_decode_value(*codechar++);
+			} while (fragment < 0);
+			*plainchar++ |= (fragment & 0x030) >> 4;
+			*plainchar    = (fragment & 0x00f) << 4;
+	case step_c:
+			do {
+				if (codechar == code_in+length_in)
+				{
+					state_in->step = step_c;
+					state_in->plainchar = *plainchar;
+					return plainchar - plaintext_out;
+				}
+				fragment = (char)base64_decode_value(*codechar++);
+			} while (fragment < 0);
+			*plainchar++ |= (fragment & 0x03c) >> 2;
+			*plainchar    = (fragment & 0x003) << 6;
+	case step_d:
+			do {
+				if (codechar == code_in+length_in)
+				{
+					state_in->step = step_d;
+					state_in->plainchar = *plainchar;
+					return plainchar - plaintext_out;
+				}
+				fragment = (char)base64_decode_value(*codechar++);
+			} while (fragment < 0);
+			*plainchar++   |= (fragment & 0x03f);
+		}
+	}
+	/* control should not reach here */
+	return plainchar - plaintext_out;
+}
+
+}


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

Added: box/trunk/lib/httpserver/cdecode.h
===================================================================
--- box/trunk/lib/httpserver/cdecode.h	                        (rev 0)
+++ box/trunk/lib/httpserver/cdecode.h	2009-01-06 13:18:07 UTC (rev 2442)
@@ -0,0 +1,28 @@
+/*
+cdecode.h - c header for a base64 decoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CDECODE_H
+#define BASE64_CDECODE_H
+
+typedef enum
+{
+	step_a, step_b, step_c, step_d
+} base64_decodestep;
+
+typedef struct
+{
+	base64_decodestep step;
+	char plainchar;
+} base64_decodestate;
+
+void base64_init_decodestate(base64_decodestate* state_in);
+
+int base64_decode_value(char value_in);
+
+int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
+
+#endif /* BASE64_CDECODE_H */


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

Added: box/trunk/lib/httpserver/cencode.cpp
===================================================================
--- box/trunk/lib/httpserver/cencode.cpp	                        (rev 0)
+++ box/trunk/lib/httpserver/cencode.cpp	2009-01-06 13:18:07 UTC (rev 2442)
@@ -0,0 +1,113 @@
+/*
+cencoder.c - c source to a base64 encoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+extern "C"
+{
+
+#include "cencode.h"
+
+const int CHARS_PER_LINE = 72;
+
+void base64_init_encodestate(base64_encodestate* state_in)
+{
+	state_in->step = step_A;
+	state_in->result = 0;
+	state_in->stepcount = 0;
+}
+
+char base64_encode_value(char value_in)
+{
+	static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	if (value_in > 63) return '=';
+	return encoding[(int)value_in];
+}
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
+{
+	const char* plainchar = plaintext_in;
+	const char* const plaintextend = plaintext_in + length_in;
+	char* codechar = code_out;
+	char result;
+	char fragment;
+	
+	result = state_in->result;
+	
+	switch (state_in->step)
+	{
+		while (1)
+		{
+	case step_A:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_A;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result = (fragment & 0x0fc) >> 2;
+			*codechar++ = base64_encode_value(result);
+			result = (fragment & 0x003) << 4;
+	case step_B:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_B;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result |= (fragment & 0x0f0) >> 4;
+			*codechar++ = base64_encode_value(result);
+			result = (fragment & 0x00f) << 2;
+	case step_C:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_C;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result |= (fragment & 0x0c0) >> 6;
+			*codechar++ = base64_encode_value(result);
+			result  = (fragment & 0x03f) >> 0;
+			*codechar++ = base64_encode_value(result);
+			
+			++(state_in->stepcount);
+			if (state_in->stepcount == CHARS_PER_LINE/4)
+			{
+				*codechar++ = '\n';
+				state_in->stepcount = 0;
+			}
+		}
+	}
+	/* control should not reach here */
+	return codechar - code_out;
+}
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
+{
+	char* codechar = code_out;
+	
+	switch (state_in->step)
+	{
+	case step_B:
+		*codechar++ = base64_encode_value(state_in->result);
+		*codechar++ = '=';
+		*codechar++ = '=';
+		break;
+	case step_C:
+		*codechar++ = base64_encode_value(state_in->result);
+		*codechar++ = '=';
+		break;
+	case step_A:
+		break;
+	}
+	*codechar++ = '\n';
+	
+	return codechar - code_out;
+}
+
+}


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

Added: box/trunk/lib/httpserver/cencode.h
===================================================================
--- box/trunk/lib/httpserver/cencode.h	                        (rev 0)
+++ box/trunk/lib/httpserver/cencode.h	2009-01-06 13:18:07 UTC (rev 2442)
@@ -0,0 +1,32 @@
+/*
+cencode.h - c header for a base64 encoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CENCODE_H
+#define BASE64_CENCODE_H
+
+typedef enum
+{
+	step_A, step_B, step_C
+} base64_encodestep;
+
+typedef struct
+{
+	base64_encodestep step;
+	char result;
+	int stepcount;
+} base64_encodestate;
+
+void base64_init_encodestate(base64_encodestate* state_in);
+
+char base64_encode_value(char value_in);
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
+
+#endif /* BASE64_CENCODE_H */
+


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

Added: box/trunk/lib/httpserver/decode.h
===================================================================
--- box/trunk/lib/httpserver/decode.h	                        (rev 0)
+++ box/trunk/lib/httpserver/decode.h	2009-01-06 13:18:07 UTC (rev 2442)
@@ -0,0 +1,77 @@
+// :mode=c++:
+/*
+decode.h - c++ wrapper for a base64 decoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_DECODE_H
+#define BASE64_DECODE_H
+
+#include <iostream>
+
+namespace base64
+{
+	
+	extern "C"
+	{
+		#include "cdecode.h"
+	}
+	
+	struct decoder
+	{
+		base64_decodestate _state;
+		int _buffersize;
+		
+		decoder(int buffersize_in = 4096)
+		: _buffersize(buffersize_in)
+		{}
+		int decode(char value_in)
+		{
+			return base64_decode_value(value_in);
+		}
+		int decode(const char* code_in, const int length_in, char* plaintext_out)
+		{
+			return base64_decode_block(code_in, length_in, plaintext_out, &_state);
+		}
+		std::string decode(const std::string& input)
+		{
+			base64_init_decodestate(&_state);
+			char* output = new char[2*input.size()];
+			int outlength = decode(input.c_str(), input.size(),
+				output);
+			std::string output_string(output, outlength);
+			base64_init_decodestate(&_state);
+			delete [] output;
+			return output_string;
+		}
+		void decode(std::istream& istream_in, std::ostream& ostream_in)
+		{
+			base64_init_decodestate(&_state);
+			//
+			const int N = _buffersize;
+			char* code = new char[N];
+			char* plaintext = new char[N];
+			int codelength;
+			int plainlength;
+			
+			do
+			{
+				istream_in.read((char*)code, N);
+				codelength = istream_in.gcount();
+				plainlength = decode(code, codelength, plaintext);
+				ostream_in.write((const char*)plaintext, plainlength);
+			}
+			while (istream_in.good() && codelength > 0);
+			//
+			base64_init_decodestate(&_state);
+			
+			delete [] code;
+			delete [] plaintext;
+		}
+	};
+	
+} // namespace base64
+
+#endif // BASE64_DECODE_H


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

Added: box/trunk/lib/httpserver/encode.h
===================================================================
--- box/trunk/lib/httpserver/encode.h	                        (rev 0)
+++ box/trunk/lib/httpserver/encode.h	2009-01-06 13:18:07 UTC (rev 2442)
@@ -0,0 +1,87 @@
+// :mode=c++:
+/*
+encode.h - c++ wrapper for a base64 encoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_ENCODE_H
+#define BASE64_ENCODE_H
+
+#include <iostream>
+
+namespace base64
+{
+	
+	extern "C" 
+	{
+		#include "cencode.h"
+	}
+	
+	struct encoder
+	{
+		base64_encodestate _state;
+		int _buffersize;
+		
+		encoder(int buffersize_in = 4096)
+		: _buffersize(buffersize_in)
+		{}
+		int encode(char value_in)
+		{
+			return base64_encode_value(value_in);
+		}
+		int encode(const char* code_in, const int length_in, char* plaintext_out)
+		{
+			return base64_encode_block(code_in, length_in, plaintext_out, &_state);
+		}
+		int encode_end(char* plaintext_out)
+		{
+			return base64_encode_blockend(plaintext_out, &_state);
+		}
+		std::string encode(const std::string& input)
+		{
+			base64_init_encodestate(&_state);
+			char* output = new char[2*input.size()];
+			int outlength = encode(input.c_str(), input.size(),
+				output);
+			outlength += encode_end(output + outlength);
+			std::string output_string(output, outlength);
+			base64_init_encodestate(&_state);
+			delete [] output;
+			return output_string;
+		}
+		void encode(std::istream& istream_in, std::ostream& ostream_in)
+		{
+			base64_init_encodestate(&_state);
+			//
+			const int N = _buffersize;
+			char* plaintext = new char[N];
+			char* code = new char[2*N];
+			int plainlength;
+			int codelength;
+			
+			do
+			{
+				istream_in.read(plaintext, N);
+				plainlength = istream_in.gcount();
+				//
+				codelength = encode(plaintext, plainlength, code);
+				ostream_in.write(code, codelength);
+			}
+			while (istream_in.good() && plainlength > 0);
+			
+			codelength = encode_end(code);
+			ostream_in.write(code, codelength);
+			//
+			base64_init_encodestate(&_state);
+			
+			delete [] code;
+			delete [] plaintext;
+		}
+	};
+	
+} // namespace base64
+
+#endif // BASE64_ENCODE_H
+


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

Added: box/trunk/test/httpserver/testfiles/photos/puppy.jpg
===================================================================
(Binary files differ)


Property changes on: box/trunk/test/httpserver/testfiles/photos/puppy.jpg
___________________________________________________________________
Added: svn:mime-type
   + image/jpeg

Modified: box/trunk/test/httpserver/testhttpserver.cpp
===================================================================
--- box/trunk/test/httpserver/testhttpserver.cpp	2009-01-05 00:50:30 UTC (rev 2441)
+++ box/trunk/test/httpserver/testhttpserver.cpp	2009-01-06 13:18:07 UTC (rev 2442)
@@ -12,12 +12,16 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "Test.h"
-#include "HTTPServer.h"
+#include <openssl/hmac.h>
+
 #include "HTTPRequest.h"
 #include "HTTPResponse.h"
+#include "HTTPServer.h"
 #include "IOStreamGetLine.h"
 #include "ServerControl.h"
+#include "Test.h"
+#include "decode.h"
+#include "encode.h"
 
 #include "MemLeakFindOn.h"
 
@@ -131,9 +135,93 @@
 	rResponse.SetContentType("text/plain");
 
 	try
-	{		
-		if (rRequest.GetMethod() == HTTPRequest::Method_GET)
+	{
+		// 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)
@@ -348,6 +436,97 @@
 	TEST_THAT(KillServer(pid));
 	TestRemoteProcessMemLeaks("generic-httpserver.memleaks");
 
+	{
+		// 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",
+			"AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbA=");
+		
+		S3Simulator simulator;
+		
+		CollectInBufferStream response_buffer;
+		HTTPResponse response(&response_buffer);
+		
+		simulator.Handle(request, response);
+		TEST_EQUAL(200, response.GetResponseCode());
+		
+		std::string response_data((const char *)response.GetBuffer(),
+			response.GetSize());
+		TEST_EQUAL("omgpuppies!\n", response_data);
+	}
+
+	{
+		// 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",
+			"AWS 0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbB=");
+		
+		S3Simulator simulator;
+		
+		CollectInBufferStream response_buffer;
+		HTTPResponse response(&response_buffer);
+		
+		simulator.Handle(request, response);
+		TEST_EQUAL(401, response.GetResponseCode());
+		
+		std::string response_data((const char *)response.GetBuffer(),
+			response.GetSize());
+		TEST_EQUAL("", response_data);
+	}
+
+	{
+		HTTPRequest request(HTTPRequest::Method_GET, "/nonexist");
+		request.SetHostName("quotes.s3.amazonaws.com");
+		request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
+		request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:0cSX/YPdtXua1aFFpYmH1tc0ajA=");
+
+		S3Simulator simulator;
+		
+		CollectInBufferStream response_buffer;
+		HTTPResponse response(&response_buffer);
+		
+		simulator.Handle(request, response);
+		TEST_EQUAL(404, response.GetResponseCode());
+	}
+
+	{
+		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("Content-Type", "text/plain");
+		
+		FileStream fs("testfiles/testrequests.pl");
+		fs.CopyStreamTo(request);
+		request.SetForReading();
+
+		CollectInBufferStream response_buffer;
+		HTTPResponse response(&response_buffer);
+		
+		S3Simulator simulator;
+		simulator.Handle(request, response);
+		
+		TEST_EQUAL(200, response.GetResponseCode());
+		TEST_EQUAL("LriYPLdmOdAiIfgSm/F1YsViT1LW94/xUQxMsF7xiEb1a0wiIOIxl+zbwZ163pt7", response.GetHeaderValue("x-amz-id-2"));
+		TEST_EQUAL("F2A8CCCA26B4B26D", response.GetHeaderValue("x-amz-request-id"));
+		TEST_EQUAL("Wed, 01 Mar  2006 12:00:00 GMT", response.GetHeaderValue("Date"));
+		TEST_EQUAL("Sun, 1 Jan 2006 12:00:00 GMT", response.GetHeaderValue("Last-Modified"));
+		TEST_EQUAL("\"828ef3fdfa96f00ad9f27c383fc9ac7f\"", response.GetHeaderValue("ETag"));
+		TEST_EQUAL("", response.GetContentType());
+		TEST_EQUAL("AmazonS3", response.GetHeaderValue("Server"));
+		TEST_EQUAL(0, response.GetSize());
+
+		FileStream f1("testfiles/testrequests.pl");
+		FileStream f2("testfiles/newfile");
+		TEST_THAT(f1.CompareWith(f2));
+		TEST_EQUAL(0, ::unlink("testfiles/newfile"));
+	}
+
 	// Start the S3Simulator server
 	pid = LaunchServer("./test s3server testfiles/httpserver.conf",
 		"testfiles/httpserver.pid");
@@ -364,7 +543,7 @@
 		HTTPRequest request(HTTPRequest::Method_GET, "/nonexist");
 		request.SetHostName("quotes.s3.amazonaws.com");
 		request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
-		request.AddHeader("Authorization", "AWS 15B4D3461F177624206A:xQE0diMbLRepdf3YB+FIEXAMPLE=");
+		request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:0cSX/YPdtXua1aFFpYmH1tc0ajA=");
 		request.SetClientKeepAliveRequested(true);
 		request.Send(sock, IOStream::TimeOutInfinite);
 
@@ -380,7 +559,7 @@
 			"/testrequests.pl");
 		request.SetHostName("quotes.s3.amazonaws.com");
 		request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
-		request.AddHeader("Authorization", "AWS 15B4D3461F177624206A:xQE0diMbLRepdf3YB+FIEXAMPLE=");
+		request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:qc1e8u8TVl2BpIxwZwsursIb8U8=");
 		request.SetClientKeepAliveRequested(true);
 		request.Send(sock, IOStream::TimeOutInfinite);
 
@@ -396,7 +575,7 @@
 			"/testrequests.pl");
 		request.SetHostName("quotes.s3.amazonaws.com");
 		request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
-		request.AddHeader("Authorization", "AWS 15B4D3461F177624206A:xQE0diMbLRepdf3YB+FIEXAMPLE=");
+		request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:qc1e8u8TVl2BpIxwZwsursIb8U8=");
 		request.SetClientKeepAliveRequested(true);
 		request.Send(sock, IOStream::TimeOutInfinite);
 
@@ -421,7 +600,7 @@
 			"/newfile");
 		request.SetHostName("quotes.s3.amazonaws.com");
 		request.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
-		request.AddHeader("Authorization", "AWS 15B4D3461F177624206A:xQE0diMbLRepdf3YB+FIEXAMPLE=");
+		request.AddHeader("Authorization", "AWS 0PN5J17HBGZHT7JJ3X82:kfY1m6V3zTufRy2kj92FpQGKz4M=");
 		request.AddHeader("Content-Type", "text/plain");
 		FileStream fs("testfiles/testrequests.pl");
 		HTTPResponse response;