Skip to content

Commit

Permalink
Refactor S3Simulator to better support merge of SimpleDB support
Browse files Browse the repository at this point in the history
(cherry picked from commit 35b581b)
  • Loading branch information
qris committed Jul 23, 2017
1 parent af8bc40 commit 5d2a395
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 54 deletions.
141 changes: 88 additions & 53 deletions lib/httpserver/S3Simulator.cpp
Expand Up @@ -94,94 +94,116 @@ void S3Simulator::Handle(HTTPRequest &rRequest, HTTPResponse &rResponse)
rResponse.SetResponseCode(HTTPResponse::Code_InternalServerError);
rResponse.SetContentType("text/plain");

std::string bucket_name;

try
{
const Configuration& rConfig(GetConfiguration());
std::string access_key = rConfig.GetKeyValue("AccessKey");
std::string secret_key = rConfig.GetKeyValue("SecretKey");
std::ostringstream buffer_to_sign;
buffer_to_sign << rRequest.GetMethodName() << "\n";

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())
if(true)
{
std::string suffix = host.substr(host.size() -
s3suffix.size(), s3suffix.size());
if (suffix == s3suffix)
std::string md5, date;
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())
{
bucket = host.substr(0, host.size() -
std::string suffix = host.substr(host.size() - s3suffix.size(),
s3suffix.size());

if (suffix == s3suffix)
{
bucket_name = host.substr(0,
host.size() - s3suffix.size());
}
}
}

std::ostringstream data;
data << rRequest.GetMethodName() << "\n";
data << md5 << "\n";
data << rRequest.GetContentType() << "\n";
data << date << "\n";
buffer_to_sign << md5 << "\n";
buffer_to_sign << rRequest.GetContentType() << "\n";
buffer_to_sign << date << "\n";

// header names are already in lower case, i.e. canonical form
// header names are already in lower case, i.e. canonical form
std::vector<HTTPRequest::Header> headers =
rRequest.GetHeaders().GetExtraHeaders();
std::sort(headers.begin(), headers.end());

std::vector<HTTPRequest::Header> headers = rRequest.GetHeaders().GetExtraHeaders();
std::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")
{
buffer_to_sign << i->first << ":" <<
i->second << "\n";
}
}

for (std::vector<HTTPRequest::Header>::iterator
i = headers.begin(); i != headers.end(); i++)
{
if (i->first.substr(0, 5) == "x-amz")
if (! bucket_name.empty())
{
data << i->first << ":" << i->second << "\n";
buffer_to_sign << "/" << bucket_name;
}
}

if (! bucket.empty())
{
data << "/" << bucket;
buffer_to_sign << rRequest.GetRequestURI();
}

data << rRequest.GetRequestURI();
std::string data_string = data.str();
std::string string_to_sign = buffer_to_sign.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);
(const unsigned char*)string_to_sign.c_str(),
string_to_sign.size(), digest_buffer, &digest_size);

std::string digest((const char *)digest_buffer, digest_size);
std::string expected_auth, actual_auth;
base64::encoder encoder;
std::string expectedAuth = "AWS " + access_key + ":" +
encoder.encode(digest);

if (expectedAuth[expectedAuth.size() - 1] == '\n')
if(true)
{
expectedAuth = expectedAuth.substr(0,
expectedAuth.size() - 1);
expected_auth = "AWS " + access_key + ":" +
encoder.encode(digest);

if(!rRequest.GetHeader("authorization", &actual_auth))
{
THROW_EXCEPTION_MESSAGE(HTTPException,
AuthenticationFailed, "Missing Authorization header");
}
}

std::string actualAuth;
if (!rRequest.GetHeader("authorization", &actualAuth) ||
actualAuth != expectedAuth)
// The Base64 encoder tends to add a newline onto the end of the encoded
// string, which we don't want, so remove it here.
if(expected_auth[expected_auth.size() - 1] == '\n')
{
THROW_EXCEPTION_MESSAGE(HTTPException, AuthenticationFailed,
"Authentication code mismatch");
expected_auth = expected_auth.substr(0, expected_auth.size() - 1);
}
else if (rRequest.GetMethod() == HTTPRequest::Method_GET)

if(actual_auth != expected_auth)
{
THROW_EXCEPTION_MESSAGE(HTTPException,
AuthenticationFailed, "Authentication code mismatch: " <<
"expected " << expected_auth << " but received " <<
actual_auth);
}

if(rRequest.GetMethod() == HTTPRequest::Method_GET)
{
HandleGet(rRequest, rResponse);
}
else if (rRequest.GetMethod() == HTTPRequest::Method_PUT)
else if(rRequest.GetMethod() == HTTPRequest::Method_PUT)
{
HandlePut(rRequest, rResponse);
}
else
{
THROW_EXCEPTION_MESSAGE(HTTPException, BadRequest,
"Unsupported Amazon S3 Method");
"Unsupported Amazon S3 Method: " <<
rRequest.GetMethodName());
}
}
catch (BoxException &ce)
Expand Down Expand Up @@ -215,10 +237,13 @@ void S3Simulator::Handle(HTTPRequest &rRequest, HTTPResponse &rResponse)
SendInternalErrorResponse("Unknown exception", rResponse);
}

if (rResponse.GetResponseCode() != 200 &&
if (rResponse.GetResponseCode() != HTTPResponse::Code_OK &&
rResponse.GetResponseCode() != HTTPResponse::Code_NotModified &&
rResponse.GetResponseCode() != HTTPResponse::Code_NoContent &&
rResponse.GetSize() == 0)
{
// no error message written, provide a default
// Looks like an error response with no error message specified,
// so write a default one.
std::ostringstream s;
s << rResponse.GetResponseCode();
SendInternalErrorResponse(s.str().c_str(), rResponse);
Expand All @@ -230,6 +255,7 @@ void S3Simulator::Handle(HTTPRequest &rRequest, HTTPResponse &rResponse)
return;
}


// --------------------------------------------------------------------------
//
// Function
Expand All @@ -246,20 +272,29 @@ void S3Simulator::HandleGet(HTTPRequest &rRequest, HTTPResponse &rResponse)
std::string path = GetConfiguration().GetKeyValue("StoreDirectory");
path += rRequest.GetRequestURI();
std::auto_ptr<FileStream> apFile;

apFile.reset(new FileStream(path));

rResponse.SetResponseCode(HTTPResponse::Code_OK);

if(true)
{
apFile->CopyStreamTo(rResponse);
// We allow the HTTPResponse to set the response size itself in this case,
// but we must add the ETag header. TODO: proper support for streaming
// responses will require us to set the content-length here, because we
// know it but the HTTPResponse does not.
rResponse.AddHeader("ETag", "\"828ef3fdfa96f00ad9f27c383fc9ac7f\"");
}

// 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
Expand Down
4 changes: 3 additions & 1 deletion test/httpserver/testhttpserver.cpp
Expand Up @@ -520,7 +520,9 @@ bool test_httpserver()
"<h1>Internal Server Error</h1>\n"
"<p>An error occurred while processing the request:</p>\n"
"<pre>HTTPException(AuthenticationFailed): "
"Authentication code mismatch</pre>\n"
"Authentication code mismatch: expected AWS 0PN5J17HBGZHT7JJ3X82"
":xXjDGYUmKxnwqr5KXNPGldn5LbA= but received AWS "
"0PN5J17HBGZHT7JJ3X82:xXjDGYUmKxnwqr5KXNPGldn5LbB=</pre>\n"
"<p>Please try again later.</p></body>\n"
"</html>\n", response_data);
}
Expand Down

0 comments on commit 5d2a395

Please sign in to comment.