ROOT  6.06/09
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  toSign += "/" + fBucket + fObjectKey;
121 
122  unsigned char digest[SHA_DIGEST_LENGTH] = {0};
123 #if defined(MAC_OS_X_VERSION_10_7)
124  CCHmac(kCCHmacAlgSHA1, fSecretKey.Data(), fSecretKey.Length() , (unsigned char *)toSign.Data(), toSign.Length(), digest);
125 #else
126  unsigned int *sd = NULL;
127  HMAC(EVP_sha1(), fSecretKey.Data(), fSecretKey.Length() , (unsigned char *)toSign.Data(), toSign.Length(), digest, sd);
128 #endif
129 
130  return TBase64::Encode((const char *)digest, SHA_DIGEST_LENGTH);
131 }
132 
133 ////////////////////////////////////////////////////////////////////////////////
134 
136 {
137  switch (httpVerb) {
138  case kGET: return TString("GET");
139  case kPOST: return TString("POST");
140  case kPUT: return TString("PUT");
141  case kDELETE: return TString("DELETE");
142  case kHEAD: return TString("HEAD");
143  case kCOPY: return TString("COPY");
144  default: return TString("");
145  }
146 }
147 
148 ////////////////////////////////////////////////////////////////////////////////
149 /// Sets this request's time stamp according to:
150 /// http://code.google.com/apis/storage/docs/reference-headers.html#date
151 
153 {
154  time_t now = time(NULL);
155  char result[128];
156 #ifdef _REENTRANT
157  struct tm dateFormat;
158  strftime(result, sizeof(result), "%a, %d %b %Y %H:%M:%S GMT",
159  gmtime_r(&now, &dateFormat));
160 #else
161  strftime(result, sizeof(result), "%a, %d %b %Y %H:%M:%S GMT",
162  gmtime(&now));
163 #endif
164  fTimeStamp = result;
165  return *this;
166 }
167 
168 ////////////////////////////////////////////////////////////////////////////////
169 /// Returns the first line of a HTTP request for this object. Note that since
170 /// we don't use the virtual host syntax which is supported by Amazon, we
171 /// must include the bucket name in thr resource. For example, we don't use
172 /// http://mybucket.s3.amazonaws.com/path/to/my/file but instead
173 /// http://s3.amazonaws.com/mybucket/path/to/my/file so the HTTP request
174 /// will be of the form "GET /mybucket/path/to/my/file HTTP/1.1"
175 /// Also note that the path must include the leading '/'.
176 
178 {
179  return TString::Format("%s /%s%s HTTP/1.1",
180  (const char*)HTTPVerbToTString(httpVerb),
181  (const char*)fBucket,
182  (const char*)fObjectKey);
183 }
184 
185 ////////////////////////////////////////////////////////////////////////////////
186 /// Returns the 'Host' header to include in the HTTP request.
187 
189 {
190  return "Host: " + fHost;
191 }
192 
193 ////////////////////////////////////////////////////////////////////////////////
194 /// Returns the date header for this HTTP request
195 
197 {
198  return "Date: " + fTimeStamp;
199 }
200 
201 ////////////////////////////////////////////////////////////////////////////////
202 /// Returns the authentication prefix
203 
205 {
206  switch (fAuthType) {
207  case kNoAuth: return "";
208  case kGoogle: return "GOOG1";
209  case kAmazon:
210  default: return "AWS";
211  }
212 }
213 
214 ////////////////////////////////////////////////////////////////////////////////
215 /// Returns the authentication header for this HTTP request
216 
218 {
219  if (fAuthType == kNoAuth)
220  return "";
221 
222  return TString::Format("Authorization: %s %s:%s%s",
223  (const char*)MakeAuthPrefix(),
224  (const char*)fAccessKey,
225  (const char*)ComputeSignature(httpVerb),
226  (fAuthType == kGoogle) ? "\r\nx-goog-api-version: 1" : "");
227 }
228 
229 ////////////////////////////////////////////////////////////////////////////////
230 /// Returns the HTTP request ready to be sent to the server
231 
233 {
234  // Set time stamp before computing this request's signature. The signature
235  // includes the date.
236  SetTimeStamp();
237  TString request = TString::Format("%s\r\n%s\r\n%s\r\n",
238  (const char*)MakeRequestLine(httpVerb),
239  (const char*)MakeHostHeader(),
240  (const char*)MakeDateHeader());
241  TString authHeader = MakeAuthHeader(httpVerb);
242  if (!authHeader.IsNull())
243  request += authHeader + "\r\n";
244  if (appendCRLF)
245  request += "\r\n";
246  return request;
247 }
TString MakeRequestLine(TS3HTTPRequest::EHTTPVerb httpVerb) const
Returns the first line of a HTTP request for this object.
TS3HTTPRequest & SetTimeStamp()
Sets this request's time stamp according to: http://code.google.com/apis/storage/docs/reference-heade...
Ssiz_t Length() const
Definition: TString.h:390
TString MakeAuthHeader(TS3HTTPRequest::EHTTPVerb httpVerb) const
Returns the authentication header for this HTTP request.
TString fTimeStamp
Basic string class.
Definition: TString.h:137
bool Bool_t
Definition: RtypesCore.h:59
EAuthType fAuthType
TString fObjectKey
const char * Data() const
Definition: TString.h:349
static TString Encode(const char *data)
Transform data into a null terminated base64 string.
Definition: TBase64.cxx:113
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:2334
ClassImp(TS3HTTPRequest) TS3HTTPRequest
TString fSecretKey
ROOT::R::TRInterface & r
Definition: Object.C:4
TString MakeAuthPrefix() const
Returns the authentication prefix.
TString fAccessKey
TString GetRequest(TS3HTTPRequest::EHTTPVerb httpVerb, Bool_t appendCRLF=kTRUE)
Returns the HTTP request ready to be sent to the server.
TString MakeHostHeader() const
Returns the 'Host' header to include in the HTTP request.
Bool_t IsNull() const
Definition: TString.h:387
TString MakeDateHeader() const
Returns the date header for this HTTP request.
TString HTTPVerbToTString(EHTTPVerb httpVerb) const
Mother of all ROOT objects.
Definition: TObject.h:58
TString ComputeSignature(TS3HTTPRequest::EHTTPVerb httpVerb) const
Returns this request's signature.
#define NULL
Definition: Rtypes.h:82
double result[121]
EHTTPVerb fVerb