summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-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
6 files changed, 397 insertions, 38 deletions
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
+