source: box/trunk/lib/httpserver/S3Simulator.cpp @ 2507

Revision 2507, 8.5 KB checked in by martin, 3 years ago (diff)

Fixes for gcc 4.4.

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-c++src
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    S3Client.cpp
5//              Purpose: Amazon S3 client helper implementation class
6//              Created: 09/01/2009
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <algorithm>
13#include <cstring>
14
15// #include <cstdio>
16// #include <ctime>
17
18#include <openssl/hmac.h>
19
20#include "HTTPRequest.h"
21#include "HTTPResponse.h"
22#include "autogen_HTTPException.h"
23#include "IOStream.h"
24#include "Logging.h"
25#include "S3Simulator.h"
26#include "decode.h"
27#include "encode.h"
28
29#include "MemLeakFindOn.h"
30
31// --------------------------------------------------------------------------
32//
33// Function
34//              Name:    HTTPServer::GetConfigVerify()
35//              Purpose: Returns additional configuration options for the
36//                       S3 simulator. Currently the access key, secret key
37//                       and store directory can be configured.
38//              Created: 09/01/09
39//
40// --------------------------------------------------------------------------
41const ConfigurationVerify* S3Simulator::GetConfigVerify() const
42{
43        static ConfigurationVerifyKey verifyserverkeys[] = 
44        {
45                HTTPSERVER_VERIFY_SERVER_KEYS(ConfigurationVerifyKey::NoDefaultValue) // no default addresses
46        };
47
48        static ConfigurationVerify verifyserver[] = 
49        {
50                {
51                        "Server",
52                        0,
53                        verifyserverkeys,
54                        ConfigTest_Exists | ConfigTest_LastEntry,
55                        0
56                }
57        };
58       
59        static ConfigurationVerifyKey verifyrootkeys[] = 
60        {
61                ConfigurationVerifyKey("AccessKey", ConfigTest_Exists),
62                ConfigurationVerifyKey("SecretKey", ConfigTest_Exists),
63                ConfigurationVerifyKey("StoreDirectory", ConfigTest_Exists),
64                HTTPSERVER_VERIFY_ROOT_KEYS
65        };
66
67        static ConfigurationVerify verify =
68        {
69                "root",
70                verifyserver,
71                verifyrootkeys,
72                ConfigTest_Exists | ConfigTest_LastEntry,
73                0
74        };
75
76        return &verify;
77}
78
79// --------------------------------------------------------------------------
80//
81// Function
82//              Name:    S3Simulator::Handle(HTTPRequest &rRequest,
83//                       HTTPResponse &rResponse)
84//              Purpose: Handles any incoming S3 request, by checking
85//                       authorization and then dispatching to one of the
86//                       private Handle* methods.
87//              Created: 09/01/09
88//
89// --------------------------------------------------------------------------
90
91void S3Simulator::Handle(HTTPRequest &rRequest, HTTPResponse &rResponse)
92{
93        // if anything goes wrong, return a 500 error
94        rResponse.SetResponseCode(HTTPResponse::Code_InternalServerError);
95        rResponse.SetContentType("text/plain");
96
97        try
98        {
99                const Configuration& rConfig(GetConfiguration());
100                std::string access_key = rConfig.GetKeyValue("AccessKey");
101                std::string secret_key = rConfig.GetKeyValue("SecretKey");
102               
103                std::string md5, date, bucket;
104                rRequest.GetHeader("content-md5", &md5);
105                rRequest.GetHeader("date", &date);
106               
107                std::string host = rRequest.GetHostName();
108                std::string s3suffix = ".s3.amazonaws.com";
109                if (host.size() > s3suffix.size())
110                {
111                        std::string suffix = host.substr(host.size() -
112                                s3suffix.size(), s3suffix.size());
113                        if (suffix == s3suffix)
114                        {
115                                bucket = host.substr(0, host.size() -
116                                        s3suffix.size());
117                        }
118                }
119               
120                std::ostringstream data;
121                data << rRequest.GetVerb() << "\n";
122                data << md5 << "\n";
123                data << rRequest.GetContentType() << "\n";
124                data << date << "\n";
125
126                // header names are already in lower case, i.e. canonical form
127
128                std::vector<HTTPRequest::Header> headers = rRequest.GetHeaders();
129                std::sort(headers.begin(), headers.end());
130               
131                for (std::vector<HTTPRequest::Header>::iterator
132                        i = headers.begin(); i != headers.end(); i++)
133                {
134                        if (i->first.substr(0, 5) == "x-amz")
135                        {
136                                data << i->first << ":" << i->second << "\n";
137                        }
138                }               
139               
140                if (! bucket.empty())
141                {
142                        data << "/" << bucket;
143                }
144               
145                data << rRequest.GetRequestURI();
146                std::string data_string = data.str();
147
148                unsigned char digest_buffer[EVP_MAX_MD_SIZE];
149                unsigned int digest_size = sizeof(digest_buffer);
150                /* unsigned char* mac = */ HMAC(EVP_sha1(),
151                        secret_key.c_str(), secret_key.size(),
152                        (const unsigned char*)data_string.c_str(),
153                        data_string.size(), digest_buffer, &digest_size);
154                std::string digest((const char *)digest_buffer, digest_size);
155               
156                base64::encoder encoder;
157                std::string expectedAuth = "AWS " + access_key + ":" +
158                        encoder.encode(digest);
159               
160                if (expectedAuth[expectedAuth.size() - 1] == '\n')
161                {
162                        expectedAuth = expectedAuth.substr(0,
163                                expectedAuth.size() - 1);
164                }
165               
166                std::string actualAuth;
167                if (!rRequest.GetHeader("authorization", &actualAuth) ||
168                        actualAuth != expectedAuth)
169                {
170                        rResponse.SetResponseCode(HTTPResponse::Code_Unauthorized);
171                        SendInternalErrorResponse("Authentication Failed",
172                                rResponse);
173                }       
174                else if (rRequest.GetMethod() == HTTPRequest::Method_GET)
175                {
176                        HandleGet(rRequest, rResponse);
177                }
178                else if (rRequest.GetMethod() == HTTPRequest::Method_PUT)
179                {
180                        HandlePut(rRequest, rResponse);
181                }
182                else
183                {
184                        rResponse.SetResponseCode(HTTPResponse::Code_MethodNotAllowed);
185                        SendInternalErrorResponse("Unsupported Method",
186                                rResponse);
187                }
188        }
189        catch (CommonException &ce)
190        {
191                SendInternalErrorResponse(ce.what(), rResponse);
192        }
193        catch (std::exception &e)
194        {
195                SendInternalErrorResponse(e.what(), rResponse);
196        }
197        catch (...)
198        {
199                SendInternalErrorResponse("Unknown exception", rResponse);
200        }
201       
202        if (rResponse.GetResponseCode() != 200 &&
203                rResponse.GetSize() == 0)
204        {
205                // no error message written, provide a default
206                std::ostringstream s;
207                s << rResponse.GetResponseCode();
208                SendInternalErrorResponse(s.str().c_str(), rResponse);
209        }
210       
211        return;
212}
213
214// --------------------------------------------------------------------------
215//
216// Function
217//              Name:    S3Simulator::HandleGet(HTTPRequest &rRequest,
218//                       HTTPResponse &rResponse)
219//              Purpose: Handles an S3 GET request, i.e. downloading an
220//                       existing object.
221//              Created: 09/01/09
222//
223// --------------------------------------------------------------------------
224
225void S3Simulator::HandleGet(HTTPRequest &rRequest, HTTPResponse &rResponse)
226{
227        std::string path = GetConfiguration().GetKeyValue("StoreDirectory");
228        path += rRequest.GetRequestURI();
229        std::auto_ptr<FileStream> apFile;
230
231        try
232        {
233                apFile.reset(new FileStream(path));
234        }
235        catch (CommonException &ce)
236        {
237                if (ce.GetSubType() == CommonException::OSFileOpenError)
238                {
239                        rResponse.SetResponseCode(HTTPResponse::Code_NotFound);
240                }
241                else if (ce.GetSubType() == CommonException::AccessDenied)
242                {
243                        rResponse.SetResponseCode(HTTPResponse::Code_Forbidden);
244                }
245                throw;
246        }
247
248        // http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingRESTOperations.html
249        apFile->CopyStreamTo(rResponse);
250        rResponse.AddHeader("x-amz-id-2", "qBmKRcEWBBhH6XAqsKU/eg24V3jf/kWKN9dJip1L/FpbYr9FDy7wWFurfdQOEMcY");
251        rResponse.AddHeader("x-amz-request-id", "F2A8CCCA26B4B26D");
252        rResponse.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
253        rResponse.AddHeader("Last-Modified", "Sun, 1 Jan 2006 12:00:00 GMT");
254        rResponse.AddHeader("ETag", "\"828ef3fdfa96f00ad9f27c383fc9ac7f\"");
255        rResponse.AddHeader("Server", "AmazonS3");
256        rResponse.SetResponseCode(HTTPResponse::Code_OK);
257}
258
259// --------------------------------------------------------------------------
260//
261// Function
262//              Name:    S3Simulator::HandlePut(HTTPRequest &rRequest,
263//                       HTTPResponse &rResponse)
264//              Purpose: Handles an S3 PUT request, i.e. uploading data to
265//                       create or replace an object.
266//              Created: 09/01/09
267//
268// --------------------------------------------------------------------------
269
270void S3Simulator::HandlePut(HTTPRequest &rRequest, HTTPResponse &rResponse)
271{
272        std::string path = GetConfiguration().GetKeyValue("StoreDirectory");
273        path += rRequest.GetRequestURI();
274        std::auto_ptr<FileStream> apFile;
275
276        try
277        {
278                apFile.reset(new FileStream(path, O_CREAT | O_WRONLY));
279        }
280        catch (CommonException &ce)
281        {
282                if (ce.GetSubType() == CommonException::OSFileOpenError)
283                {
284                        rResponse.SetResponseCode(HTTPResponse::Code_NotFound);
285                }
286                else if (ce.GetSubType() == CommonException::AccessDenied)
287                {
288                        rResponse.SetResponseCode(HTTPResponse::Code_Forbidden);
289                }
290                throw;
291        }
292
293        if (rRequest.IsExpectingContinue())
294        {
295                rResponse.SendContinue();
296        }
297
298        rRequest.ReadContent(*apFile);
299
300        // http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTObjectPUT.html
301        rResponse.AddHeader("x-amz-id-2", "LriYPLdmOdAiIfgSm/F1YsViT1LW94/xUQxMsF7xiEb1a0wiIOIxl+zbwZ163pt7");
302        rResponse.AddHeader("x-amz-request-id", "F2A8CCCA26B4B26D");
303        rResponse.AddHeader("Date", "Wed, 01 Mar  2006 12:00:00 GMT");
304        rResponse.AddHeader("Last-Modified", "Sun, 1 Jan 2006 12:00:00 GMT");
305        rResponse.AddHeader("ETag", "\"828ef3fdfa96f00ad9f27c383fc9ac7f\"");
306        rResponse.SetContentType("");
307        rResponse.AddHeader("Server", "AmazonS3");
308        rResponse.SetResponseCode(HTTPResponse::Code_OK);
309}
Note: See TracBrowser for help on using the repository browser.