Logo ROOT   6.08/07
Reference Guide
TS3HTTPRequest.cxx
Go to the documentation of this file.
1 // @(#)root/net:$Id$
2 // Author: Fabio Hernandez 30/01/2013
3 // based on an initial version by Marcelo Sousa (class THTTPMessage)
4 
5 /*************************************************************************
6  * Copyright (C) 1995-2011, Rene Brun and Fons Rademakers. *
7  * All rights reserved. *
8  * *
9  * For the licensing terms see $ROOTSYS/LICENSE. *
10  * For the list of contributors see $ROOTSYS/README/CREDITS. *
11  *************************************************************************/
12 
13 //////////////////////////////////////////////////////////////////////////
14 // //
15 // TS3HTTPRequest //
16 // //
17 // An object of this class represents an HTTP request extended to be //
18 // compatible with Amazon's S3 protocol. //
19 // Specifically, such a request contains an 'Authorization' header with //
20 // information used by the S3 server for authenticating this request. //
21 // The authentication information is computed based on a pair of access //
22 // key and secret key which are both provided to the user by the S3 //
23 // service provider (e.g. Amazon, Google, etc.). //
24 // The secret key is used to compute a signature of selected fields in //
25 // the request. The algorithm for computing the signature is documented //
26 // in: //
27 // //
28 // Google storage: //
29 // http://code.google.com/apis/storage/docs/reference/v1/developer-guidev1.html#authentication
30 // //
31 // Amazon: //
32 // http://docs.aws.amazon.com/AmazonS3/latest/dev/S3_Authentication2.html
33 // //
34 //////////////////////////////////////////////////////////////////////////
35 
36 #include "TS3HTTPRequest.h"
37 #include "TBase64.h"
38 #if defined(MAC_OS_X_VERSION_10_7)
39 #include <CommonCrypto/CommonHMAC.h>
40 #define SHA_DIGEST_LENGTH 20
41 #else
42 #include <openssl/sha.h>
43 #include <openssl/hmac.h>
44 #include <openssl/evp.h>
45 #include <openssl/bio.h>
46 #include <openssl/buffer.h>
47 #endif
48 
49 #include <stdio.h>
50 #include <time.h>
51 #include <string.h>
52 
54 
55 ////////////////////////////////////////////////////////////////////////////////
56 
58  : fAuthType(kNoAuth), fHost("NoHost")
59 {
60 }
61 
62 ////////////////////////////////////////////////////////////////////////////////
63 /// Default constructor
64 
66  const TString& bucket, const TString& objectKey, EAuthType authType,
67  const TString& accessKey, const TString& secretKey)
68 {
69  fVerb = httpVerb;
70  fHost = host;
71  fBucket = bucket;
72  fObjectKey = objectKey;
73  fAuthType = authType;
74  fAccessKey = accessKey;
75  fSecretKey = secretKey;
76 }
77 
78 ////////////////////////////////////////////////////////////////////////////////
79 /// Copy constructor
80 
82  : TObject(r)
83 {
84  fVerb = r.fVerb;
85  fHost = r.fHost;
86  fBucket = r.fBucket;
88  fAuthType = r.fAuthType;
92 }
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 /// Returns this request's signature
96 
98 {
99  // Please note, the order of the fields used for computing
100  // the signature is important. Make sure that the changes you
101  // make are compatible with the reference documentation.
102  //
103  // Refs:
104  // AMAZON http://awsdocs.s3.amazonaws.com/S3/latest/s3-qrc.pdf
105  // GOOGLE: http://code.google.com/apis/storage/docs/reference/v1/developer-guidev1.html#authentication
106 
107  TString toSign = TString::Format("%s\n\n\n%s\n", // empty Content-MD5 and Content-Type
108  (const char*)HTTPVerbToTString(httpVerb),
109  (const char*)fTimeStamp);
110  if (fAuthType == kGoogle) {
111  // Must use API version 1. Google Storage API v2 only
112  // accepts OAuth authentication.
113  // This header is not strictly needed but if used for computing
114  // the signature, the request must contain it as a header
115  // (see method MakeAuthHeader)
116  // Ref: https://developers.google.com/storage/docs/reference/v1/apiversion1
117  toSign += "x-goog-api-version:1\n"; // Lowercase, no spaces around ':'
118  }
119 
120  if (fAuthType == kAmazon) {
121  if (!fSessionToken.IsNull()) {
122  toSign += "x-amz-security-token:" + fSessionToken + "\n";
123  }
124  }
125 
126  toSign += "/" + fBucket + fObjectKey;
127 
128  unsigned char digest[SHA_DIGEST_LENGTH] = {0};
129 #if defined(MAC_OS_X_VERSION_10_7)
130  CCHmac(kCCHmacAlgSHA1, fSecretKey.Data(), fSecretKey.Length() , (unsigned char *)toSign.Data(), toSign.Length(), digest);
131 #else
132  unsigned int *sd = NULL;
133  HMAC(EVP_sha1(), fSecretKey.Data(), fSecretKey.Length() , (unsigned char *)toSign.Data(), toSign.Length(), digest, sd);
134 #endif
135 
136  return TBase64::Encode((const char *)digest, SHA_DIGEST_LENGTH);
137 }
138 
139 ////////////////////////////////////////////////////////////////////////////////
140 
142 {
143  switch (httpVerb) {
144  case kGET: return TString("GET");
145  case kPOST: return TString("POST");
146  case kPUT: return TString("PUT");
147  case kDELETE: return TString("DELETE");
148  case kHEAD: return TString("HEAD");
149  case kCOPY: return TString("COPY");
150  default: return TString("");
151  }
152 }
153 
154 ////////////////////////////////////////////////////////////////////////////////
155 /// Sets this request's time stamp according to:
156 /// http://code.google.com/apis/storage/docs/reference-headers.html#date
157 
159 {
160  time_t now = time(NULL);
161  char result[128];
162 #ifdef _REENTRANT
163  struct tm dateFormat;
164  strftime(result, sizeof(result), "%a, %d %b %Y %H:%M:%S GMT",
165  gmtime_r(&now, &dateFormat));
166 #else
167  strftime(result, sizeof(result), "%a, %d %b %Y %H:%M:%S GMT",
168  gmtime(&now));
169 #endif
170  fTimeStamp = result;
171  return *this;
172 }
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 /// Returns the first line of a HTTP request for this object. Note that since
176 /// we don't use the virtual host syntax which is supported by Amazon, we
177 /// must include the bucket name in thr resource. For example, we don't use
178 /// http://mybucket.s3.amazonaws.com/path/to/my/file but instead
179 /// http://s3.amazonaws.com/mybucket/path/to/my/file so the HTTP request
180 /// will be of the form "GET /mybucket/path/to/my/file HTTP/1.1"
181 /// Also note that the path must include the leading '/'.
182 
184 {
185  return TString::Format("%s /%s%s HTTP/1.1",
186  (const char*)HTTPVerbToTString(httpVerb),
187  (const char*)fBucket,
188  (const char*)fObjectKey);
189 }
190 
191 ////////////////////////////////////////////////////////////////////////////////
192 /// Returns the 'Host' header to include in the HTTP request.
193 
195 {
196  return "Host: " + fHost;
197 }
198 
199 ////////////////////////////////////////////////////////////////////////////////
200 /// Returns the date header for this HTTP request
201 
203 {
204  return "Date: " + fTimeStamp;
205 }
206 
207 ////////////////////////////////////////////////////////////////////////////////
208 /// Returns the session security token header for this HTTP request
209 
211 {
212  if (fAuthType != kAmazon)
213  return "";
214 
215  if (fSessionToken.IsNull())
216  return "";
217 
218  return TString::Format("x-amz-security-token: %s",
219  (const char*) fSessionToken.Data());
220 }
221 
222 ////////////////////////////////////////////////////////////////////////////////
223 /// Returns the authentication prefix
224 
226 {
227  switch (fAuthType) {
228  case kNoAuth: return "";
229  case kGoogle: return "GOOG1";
230  case kAmazon:
231  default: return "AWS";
232  }
233 }
234 
235 ////////////////////////////////////////////////////////////////////////////////
236 /// Returns the authentication header for this HTTP request
237 
239 {
240  if (fAuthType == kNoAuth)
241  return "";
242 
243  return TString::Format("Authorization: %s %s:%s%s",
244  (const char*)MakeAuthPrefix(),
245  (const char*)fAccessKey,
246  (const char*)ComputeSignature(httpVerb),
247  (fAuthType == kGoogle) ? "\r\nx-goog-api-version: 1" : "");
248 }
249 
250 ////////////////////////////////////////////////////////////////////////////////
251 /// Returns the HTTP request ready to be sent to the server
252 
254 {
255  // Set time stamp before computing this request's signature. The signature
256  // includes the date.
257  SetTimeStamp();
258  TString request = TString::Format("%s\r\n%s\r\n%s\r\n",
259  (const char*)MakeRequestLine(httpVerb),
260  (const char*)MakeHostHeader(),
261  (const char*)MakeDateHeader());
262  TString tokenHeader = MakeTokenHeader();
263  if (!tokenHeader.IsNull())
264  request += tokenHeader + "\r\n";
265  TString authHeader = MakeAuthHeader(httpVerb);
266  if (!authHeader.IsNull())
267  request += authHeader + "\r\n";
268  if (appendCRLF)
269  request += "\r\n";
270  return request;
271 }
TS3HTTPRequest & SetTimeStamp()
Sets this request&#39;s time stamp according to: http://code.google.com/apis/storage/docs/reference-heade...
TString fTimeStamp
TString MakeTokenHeader() const
Returns the session security token header for this HTTP request.
TString HTTPVerbToTString(EHTTPVerb httpVerb) const
TString MakeAuthPrefix() const
Returns the authentication prefix.
Basic string class.
Definition: TString.h:137
bool Bool_t
Definition: RtypesCore.h:59
EAuthType fAuthType
TString fObjectKey
static TString Encode(const char *data)
Transform data into a null terminated base64 string.
Definition: TBase64.cxx:115
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString...
Definition: TString.cxx:2335
TString fSecretKey
TString ComputeSignature(TS3HTTPRequest::EHTTPVerb httpVerb) const
Returns this request&#39;s signature.
TRandom2 r(17)
TString fAccessKey
TString GetRequest(TS3HTTPRequest::EHTTPVerb httpVerb, Bool_t appendCRLF=kTRUE)
Returns the HTTP request ready to be sent to the server.
TString fSessionToken
Ssiz_t Length() const
Definition: TString.h:390
#define ClassImp(name)
Definition: Rtypes.h:279
TString MakeDateHeader() const
Returns the date header for this HTTP request.
Bool_t IsNull() const
Definition: TString.h:387
Mother of all ROOT objects.
Definition: TObject.h:37
TString MakeRequestLine(TS3HTTPRequest::EHTTPVerb httpVerb) const
Returns the first line of a HTTP request for this object.
#define NULL
Definition: Rtypes.h:82
double result[121]
TString MakeAuthHeader(TS3HTTPRequest::EHTTPVerb httpVerb) const
Returns the authentication header for this HTTP request.
TString MakeHostHeader() const
Returns the &#39;Host&#39; header to include in the HTTP request.
EHTTPVerb fVerb
const char * Data() const
Definition: TString.h:349