summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/s3simulator/s3simulator.cpp32
-rwxr-xr-xinfrastructure/makebuildenv.pl.in2
-rw-r--r--lib/httpserver/HTTPRequest.cpp23
-rw-r--r--lib/httpserver/HTTPRequest.h18
-rw-r--r--lib/httpserver/HTTPServer.cpp36
-rw-r--r--lib/httpserver/HTTPServer.h10
-rw-r--r--lib/httpserver/S3Simulator.cpp308
-rw-r--r--lib/httpserver/S3Simulator.h40
-rw-r--r--modules.txt1
-rw-r--r--test/httpserver/testfiles/s3simulator.conf10
-rw-r--r--test/httpserver/testhttpserver.cpp238
11 files changed, 463 insertions, 255 deletions
diff --git a/bin/s3simulator/s3simulator.cpp b/bin/s3simulator/s3simulator.cpp
new file mode 100644
index 00000000..9a10635c
--- /dev/null
+++ b/bin/s3simulator/s3simulator.cpp
@@ -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;
+}
diff --git a/infrastructure/makebuildenv.pl.in b/infrastructure/makebuildenv.pl.in
index 5ed4a0ab..7705be0f 100755
--- a/infrastructure/makebuildenv.pl.in
+++ b/infrastructure/makebuildenv.pl.in
@@ -431,6 +431,8 @@ __E
echo Killing any running daemons...
kill_process bbackupd
kill_process bbstored
+kill_process httpserver
+kill_process s3simulator
__E
}
diff --git a/lib/httpserver/HTTPRequest.cpp b/lib/httpserver/HTTPRequest.cpp
index e146a0a3..4c5dc149 100644
--- a/lib/httpserver/HTTPRequest.cpp
+++ b/lib/httpserver/HTTPRequest.cpp
@@ -535,9 +535,11 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout)
{
++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 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout)
// 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 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout)
"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 @@ void HTTPRequest::ParseHeaders(IOStreamGetLine &rGetLine, int Timeout)
}
else
{
- std::string name = header.substr(0, p);
- mExtraHeaders.push_back(Header(name,
+ mExtraHeaders.push_back(Header(header_name,
h + dataStart));
}
diff --git a/lib/httpserver/HTTPRequest.h b/lib/httpserver/HTTPRequest.h
index 90215751..ca50e60f 100644
--- a/lib/httpserver/HTTPRequest.h
+++ b/lib/httpserver/HTTPRequest.h
@@ -97,16 +97,19 @@ public:
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 @@ public:
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 @@ private:
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
diff --git a/lib/httpserver/HTTPServer.cpp b/lib/httpserver/HTTPServer.cpp
index b8b02249..be1db687 100644
--- a/lib/httpserver/HTTPServer.cpp
+++ b/lib/httpserver/HTTPServer.cpp
@@ -153,21 +153,21 @@ void HTTPServer::Connection(SocketStream &rStream)
// 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 @@ void HTTPServer::Connection(SocketStream &rStream)
response.Send(request.GetMethod() == HTTPRequest::Method_HEAD);
}
- // Notify derived claases
+ // Notify derived classes
HTTPConnectionClosing();
}
@@ -194,12 +194,14 @@ void HTTPServer::Connection(SocketStream &rStream)
// --------------------------------------------------------------------------
//
// 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 @@ void HTTPServer::SendInternalErrorResponse(const char *Error, SocketStream &rStr
"</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);
}
diff --git a/lib/httpserver/HTTPServer.h b/lib/httpserver/HTTPServer.h
index 8009438d..d9f74949 100644
--- a/lib/httpserver/HTTPServer.h
+++ b/lib/httpserver/HTTPServer.h
@@ -52,15 +52,17 @@ public:
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
diff --git a/lib/httpserver/S3Simulator.cpp b/lib/httpserver/S3Simulator.cpp
new file mode 100644
index 00000000..62bf7344
--- /dev/null
+++ b/lib/httpserver/S3Simulator.cpp
@@ -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);
+}
diff --git a/lib/httpserver/S3Simulator.h b/lib/httpserver/S3Simulator.h
new file mode 100644
index 00000000..f80770ee
--- /dev/null
+++ b/lib/httpserver/S3Simulator.h
@@ -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
+
diff --git a/modules.txt b/modules.txt
index 8932f014..1be10efa 100644
--- a/modules.txt
+++ b/modules.txt
@@ -44,6 +44,7 @@ test/bbackupd bin/bbackupd bin/bbstored bin/bbstoreaccounts bin/bbackupquery bi
# HTTP server system
lib/httpserver lib/server
test/httpserver lib/httpserver
+bin/s3simulator lib/httpserver
# END_IF_DISTRIBUTION
diff --git a/test/httpserver/testfiles/s3simulator.conf b/test/httpserver/testfiles/s3simulator.conf
new file mode 100644
index 00000000..07921560
--- /dev/null
+++ b/test/httpserver/testfiles/s3simulator.conf
@@ -0,0 +1,10 @@
+AccessKey = 0PN5J17HBGZHT7JJ3X82
+SecretKey = uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o
+StoreDirectory = testfiles
+AddressPrefix = http://localhost:1080
+
+Server
+{
+ PidFile = testfiles/s3simulator.pid
+ ListenAddresses = inet:localhost:1080
+}
diff --git a/test/httpserver/testhttpserver.cpp b/test/httpserver/testhttpserver.cpp
index 6c0de9a9..bbefff31 100644
--- a/test/httpserver/testhttpserver.cpp
+++ b/test/httpserver/testhttpserver.cpp
@@ -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 @@ void TestWebServer::Handle(HTTPRequest &rRequest, HTTPResponse &rResponse)
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 @@ int test(int argc, const char *argv[])
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 @@ int test(int argc, const char *argv[])
// 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 @@ int test(int argc, const char *argv[])
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 @@ int test(int argc, const char *argv[])
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 @@ int test(int argc, const char *argv[])
HTTPResponse response(&response_buffer);
S3Simulator simulator;
+ simulator.Configure("testfiles/s3simulator.conf");
simulator.Handle(request, response);
TEST_EQUAL(200, response.GetResponseCode());
@@ -558,8 +362,8 @@ int test(int argc, const char *argv[])
}
// 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)
{