summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris+github@qwirx.com>2009-01-09 11:50:10 +0000
committerChris Wilson <chris+github@qwirx.com>2009-01-09 11:50:10 +0000
commitf44cc2b954fcacb662317f22da1dc1b08b96e2f6 (patch)
treee892cb656f87482e9fe16ef3b18caf47adb171d8
parent0b999ceba3550774ef00dc0170c6995b353bb791 (diff)
Move S3Client class into its own files for public access.
-rw-r--r--lib/httpserver/S3Client.cpp243
-rw-r--r--lib/httpserver/S3Client.h72
-rw-r--r--test/httpserver/testhttpserver.cpp198
3 files changed, 316 insertions, 197 deletions
diff --git a/lib/httpserver/S3Client.cpp b/lib/httpserver/S3Client.cpp
new file mode 100644
index 00000000..cd5988d5
--- /dev/null
+++ b/lib/httpserver/S3Client.cpp
@@ -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;
+}
diff --git a/lib/httpserver/S3Client.h b/lib/httpserver/S3Client.h
new file mode 100644
index 00000000..3c4126ac
--- /dev/null
+++ b/lib/httpserver/S3Client.h
@@ -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
+
diff --git a/test/httpserver/testhttpserver.cpp b/test/httpserver/testhttpserver.cpp
index 5b608591..421c9626 100644
--- a/test/httpserver/testhttpserver.cpp
+++ b/test/httpserver/testhttpserver.cpp
@@ -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 @@ void S3Simulator::HandlePut(HTTPRequest &rRequest, HTTPResponse &rResponse)
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)