ROOT logo
// @(#)root/net:$Id: TAS3File.cxx 41754 2011-11-03 16:21:13Z rdm $
// Author: Marcelo Sousa   23/08/2011

/*************************************************************************
 * Copyright (C) 1995-2011, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TAS3File                                                             //
//                                                                      //
// A TAS3File is a normal TWebFile but it reads data from the           //
// Amazon S3 server. As a derived TWebFile class TAS3File it is a       //
// read only file. The HTTP requests are generated by THTTPMessage      //
// objects with the auth_prefix set as AWS. The user id and secret pass //
// required to sign the requests are passed through the environment     //
// variables S3_ACCESS_ID and S3_ACCESS_KEY.                            //
// For more information check:                                          //
//   http://aws.amazon.com/documentation/s3/                            //
//   http://awsdocs.s3.amazonaws.com/S3/latest/s3-qrc.pdf               //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TAS3File.h"
#include "THTTPMessage.h"
#include "TSocket.h"
#include "TROOT.h"

#include <errno.h>
#include <stdlib.h>

ClassImp(TAS3File)

//______________________________________________________________________________
TAS3File::TAS3File(const char *path, Option_t *) : TWebFile(path, "IO")
{
   // For TAS3File to properly work you need to set up
   // environment variables S3_ACCESS_ID and S3_ACCESS_KEY
   // The format of the path is: server/bucket/file
   // Example: f = new TAS3File("as3://s3-eu-west-1.amazonaws.com/roots3/hsimple.root")
   
   TString tpath = path;

   Int_t begPath = 6, slash = 0, i = 0;

   if (tpath.BeginsWith("as3://") == kFALSE) {
      Error("TAS3File", "invalid path %s", path);
      goto zombie;
   }

   while (i < 2 && begPath < tpath.Length()) {
      slash = tpath.Index('/', begPath);

      if (slash == kNPOS) {
         Error("TAS3File","invalid path %s", path);
         goto zombie;
      }

      switch(i){
         case 0:
            fServer = TUrl(TString(tpath(begPath,slash)));
            break;
         case 1:
            fBucket = tpath(begPath,slash-begPath);
            fRealName = "/" + tpath(slash+1, tpath.Length()-(slash+1));
      }
      i++;
      begPath = slash+1;
   }

   fAuthPrefix = "AWS";
   fAccessId   = gSystem->Getenv("S3_ACCESS_ID");
   fAccessKey  = gSystem->Getenv("S3_ACCESS_KEY");
   if (fAccessId == "" || fAccessKey == "") {
      if (fAccessId == "")  Error("TAS3File", "shell variable S3_ACCESS_ID not set");
      if (fAccessKey == "") Error("TAS3File", "shell variable S3_ACCESS_KEY not set");
      goto zombie;
   }

   Init(kFALSE);
   return;
   
zombie:
   MakeZombie();
   gDirectory = gROOT;
}

//______________________________________________________________________________
Int_t TAS3File::GetHead()
{
   // Clone of TWebFile::GetHead except it uses THTTPMessage to generate
   // the HTTP request.

   THTTPMessage s3head = THTTPMessage(kHEAD, fRealName, GetBucket(),
                                      GetUrl().GetHost(), GetAuthPrefix(),
                                      GetAccessId(), GetAccessKey());

   TString msg = s3head.GetRequest();

   TUrl connurl;

   fUrl = fServer;

   if (fProxy.IsValid())
      connurl = fProxy;
   else
      connurl = fUrl;

   TSocket *s = 0;
   for (Int_t i = 0; i < 5; i++) {
      s = new TSocket(connurl.GetHost(), connurl.GetPort());
      if (!s->IsValid()) {
         delete s;
         if (gSystem->GetErrno() == EADDRINUSE || gSystem->GetErrno() == EISCONN) {
            s = 0;
            gSystem->Sleep(i*10);
         } else {
            Error("GetHead", "cannot connect to host %s (errno=%d)", fUrl.GetHost(),
                  gSystem->GetErrno());
            return -1;
         }
      } else
         break;
   }
   if (!s)
      return -1;

   if (s->SendRaw(msg.Data(), msg.Length()) == -1) {
      Error("GetHead", "error sending command to host %s", fUrl.GetHost());
      delete s;
      return -1;
   }

   char line[8192];
   Int_t n, ret = 0, redirect = 0;

   while ((n = GetLine(s, line, sizeof(line))) >= 0) {
      if (n == 0) {
         if (gDebug > 0)
            Info("GetHead", "got all headers");
         delete s;
         if (fBasicUrlOrg != "" && !redirect) {
            // set back to original url in case of temp redirect
            SetMsgReadBuffer10();
            fMsgGetHead = "";
         }
         if (ret < 0)
            return ret;
         if (redirect)
            return GetHead();
         return 0;
      }

      if (gDebug > 0)
         Info("GetHead", "header: %s", line);

      TString res = line;
      if (res.BeginsWith("HTTP/1.")) {
         if (res.BeginsWith("HTTP/1.1")) {
            if (!fHTTP11) {
               fMsgGetHead = "";
               fMsgReadBuffer10 = "";
            }
            fHTTP11 = kTRUE;
         }
         TString scode = res(9, 3);
         Int_t code = scode.Atoi();
         if (code >= 500) {
            if (code == 500)
               fHasModRoot = kTRUE;
            else {
               ret = -1;
               TString mess = res(13, 1000);
               Error("GetHead", "%s: %s (%d)", fBasicUrl.Data(), mess.Data(), code);
            }
         } else if (code >= 400) {
            if (code == 400)
               ret = -3;   // command not supported
            else if (code == 404)
               ret = -2;   // file does not exist
            else {
               ret = -1;
               TString mess = res(13, 1000);
               Error("GetHead", "%s: %s (%d)", fBasicUrl.Data(), mess.Data(), code);
            }
         } else if (code >= 300) {
            if (code == 301 || code == 303)
               redirect = 1;   // permanent redirect
            else if (code == 302 || code == 307)
               redirect = 2;   // temp redirect
            else {
               ret = -1;
               TString mess = res(13, 1000);
               Error("GetHead", "%s: %s (%d)", fBasicUrl.Data(), mess.Data(), code);
            }
         } else if (code > 200) {
            ret = -1;
            TString mess = res(13, 1000);
            Error("GetHead", "%s: %s (%d)", fBasicUrl.Data(), mess.Data(), code);
         }
      } else if (res.BeginsWith("Content-Length:")) {
         TString slen = res(16, 1000);
         fSize = slen.Atoll();
      } else if (res.BeginsWith("Location:") && redirect) {
         TString redir = res(10, 1000);
         if (redirect == 2)   // temp redirect
            SetMsgReadBuffer10(redir, kTRUE);
         else               // permanent redirect
            SetMsgReadBuffer10(redir, kFALSE);
         fMsgGetHead = "";
      }
   }

   delete s;
   return ret;
}

//______________________________________________________________________________
Bool_t TAS3File::ReadBuffer(char *buf, Int_t len)
{
   return ReadBuffer10(buf,len);
}

//______________________________________________________________________________
Bool_t TAS3File::ReadBuffer10(char *buf, Int_t len)
{
   // Read specified byte range from Amazon S3.
   // This routine connects to the Amazon S3 server, sends the
   // request created by THTTPMessage and returns the buffer.
   // Returns kTRUE in case of error.

   const Int_t nbuf = 1;
   Long64_t pos[nbuf];
   pos[nbuf-1] = fOffset; 

   Int_t lens[nbuf];
   lens[nbuf-1] = len;
 
   THTTPMessage s3get = THTTPMessage(kGET, fRealName, GetBucket(),
                                     GetUrl().GetHost(), GetAuthPrefix(),
                                     GetAccessId(), GetAccessKey(),
                                     0, pos, lens, nbuf);
   TString msg = s3get.GetRequest();

   Int_t n = GetFromWeb10(buf, len, msg);
   if (n == -1)
      return kTRUE;
   // The -2 error condition typically only happens when
   // GetHead() failed because not implemented, in the first call to
   // ReadBuffer() in Init(), it is not checked in ReadBuffers10().
   if (n == -2) {
      Error("ReadBuffer10", "%s does not exist", fBasicUrl.Data());
      MakeZombie();
      gDirectory = gROOT;
      return kTRUE;
   }

   fOffset += len;

   return kFALSE;
}
 TAS3File.cxx:1
 TAS3File.cxx:2
 TAS3File.cxx:3
 TAS3File.cxx:4
 TAS3File.cxx:5
 TAS3File.cxx:6
 TAS3File.cxx:7
 TAS3File.cxx:8
 TAS3File.cxx:9
 TAS3File.cxx:10
 TAS3File.cxx:11
 TAS3File.cxx:12
 TAS3File.cxx:13
 TAS3File.cxx:14
 TAS3File.cxx:15
 TAS3File.cxx:16
 TAS3File.cxx:17
 TAS3File.cxx:18
 TAS3File.cxx:19
 TAS3File.cxx:20
 TAS3File.cxx:21
 TAS3File.cxx:22
 TAS3File.cxx:23
 TAS3File.cxx:24
 TAS3File.cxx:25
 TAS3File.cxx:26
 TAS3File.cxx:27
 TAS3File.cxx:28
 TAS3File.cxx:29
 TAS3File.cxx:30
 TAS3File.cxx:31
 TAS3File.cxx:32
 TAS3File.cxx:33
 TAS3File.cxx:34
 TAS3File.cxx:35
 TAS3File.cxx:36
 TAS3File.cxx:37
 TAS3File.cxx:38
 TAS3File.cxx:39
 TAS3File.cxx:40
 TAS3File.cxx:41
 TAS3File.cxx:42
 TAS3File.cxx:43
 TAS3File.cxx:44
 TAS3File.cxx:45
 TAS3File.cxx:46
 TAS3File.cxx:47
 TAS3File.cxx:48
 TAS3File.cxx:49
 TAS3File.cxx:50
 TAS3File.cxx:51
 TAS3File.cxx:52
 TAS3File.cxx:53
 TAS3File.cxx:54
 TAS3File.cxx:55
 TAS3File.cxx:56
 TAS3File.cxx:57
 TAS3File.cxx:58
 TAS3File.cxx:59
 TAS3File.cxx:60
 TAS3File.cxx:61
 TAS3File.cxx:62
 TAS3File.cxx:63
 TAS3File.cxx:64
 TAS3File.cxx:65
 TAS3File.cxx:66
 TAS3File.cxx:67
 TAS3File.cxx:68
 TAS3File.cxx:69
 TAS3File.cxx:70
 TAS3File.cxx:71
 TAS3File.cxx:72
 TAS3File.cxx:73
 TAS3File.cxx:74
 TAS3File.cxx:75
 TAS3File.cxx:76
 TAS3File.cxx:77
 TAS3File.cxx:78
 TAS3File.cxx:79
 TAS3File.cxx:80
 TAS3File.cxx:81
 TAS3File.cxx:82
 TAS3File.cxx:83
 TAS3File.cxx:84
 TAS3File.cxx:85
 TAS3File.cxx:86
 TAS3File.cxx:87
 TAS3File.cxx:88
 TAS3File.cxx:89
 TAS3File.cxx:90
 TAS3File.cxx:91
 TAS3File.cxx:92
 TAS3File.cxx:93
 TAS3File.cxx:94
 TAS3File.cxx:95
 TAS3File.cxx:96
 TAS3File.cxx:97
 TAS3File.cxx:98
 TAS3File.cxx:99
 TAS3File.cxx:100
 TAS3File.cxx:101
 TAS3File.cxx:102
 TAS3File.cxx:103
 TAS3File.cxx:104
 TAS3File.cxx:105
 TAS3File.cxx:106
 TAS3File.cxx:107
 TAS3File.cxx:108
 TAS3File.cxx:109
 TAS3File.cxx:110
 TAS3File.cxx:111
 TAS3File.cxx:112
 TAS3File.cxx:113
 TAS3File.cxx:114
 TAS3File.cxx:115
 TAS3File.cxx:116
 TAS3File.cxx:117
 TAS3File.cxx:118
 TAS3File.cxx:119
 TAS3File.cxx:120
 TAS3File.cxx:121
 TAS3File.cxx:122
 TAS3File.cxx:123
 TAS3File.cxx:124
 TAS3File.cxx:125
 TAS3File.cxx:126
 TAS3File.cxx:127
 TAS3File.cxx:128
 TAS3File.cxx:129
 TAS3File.cxx:130
 TAS3File.cxx:131
 TAS3File.cxx:132
 TAS3File.cxx:133
 TAS3File.cxx:134
 TAS3File.cxx:135
 TAS3File.cxx:136
 TAS3File.cxx:137
 TAS3File.cxx:138
 TAS3File.cxx:139
 TAS3File.cxx:140
 TAS3File.cxx:141
 TAS3File.cxx:142
 TAS3File.cxx:143
 TAS3File.cxx:144
 TAS3File.cxx:145
 TAS3File.cxx:146
 TAS3File.cxx:147
 TAS3File.cxx:148
 TAS3File.cxx:149
 TAS3File.cxx:150
 TAS3File.cxx:151
 TAS3File.cxx:152
 TAS3File.cxx:153
 TAS3File.cxx:154
 TAS3File.cxx:155
 TAS3File.cxx:156
 TAS3File.cxx:157
 TAS3File.cxx:158
 TAS3File.cxx:159
 TAS3File.cxx:160
 TAS3File.cxx:161
 TAS3File.cxx:162
 TAS3File.cxx:163
 TAS3File.cxx:164
 TAS3File.cxx:165
 TAS3File.cxx:166
 TAS3File.cxx:167
 TAS3File.cxx:168
 TAS3File.cxx:169
 TAS3File.cxx:170
 TAS3File.cxx:171
 TAS3File.cxx:172
 TAS3File.cxx:173
 TAS3File.cxx:174
 TAS3File.cxx:175
 TAS3File.cxx:176
 TAS3File.cxx:177
 TAS3File.cxx:178
 TAS3File.cxx:179
 TAS3File.cxx:180
 TAS3File.cxx:181
 TAS3File.cxx:182
 TAS3File.cxx:183
 TAS3File.cxx:184
 TAS3File.cxx:185
 TAS3File.cxx:186
 TAS3File.cxx:187
 TAS3File.cxx:188
 TAS3File.cxx:189
 TAS3File.cxx:190
 TAS3File.cxx:191
 TAS3File.cxx:192
 TAS3File.cxx:193
 TAS3File.cxx:194
 TAS3File.cxx:195
 TAS3File.cxx:196
 TAS3File.cxx:197
 TAS3File.cxx:198
 TAS3File.cxx:199
 TAS3File.cxx:200
 TAS3File.cxx:201
 TAS3File.cxx:202
 TAS3File.cxx:203
 TAS3File.cxx:204
 TAS3File.cxx:205
 TAS3File.cxx:206
 TAS3File.cxx:207
 TAS3File.cxx:208
 TAS3File.cxx:209
 TAS3File.cxx:210
 TAS3File.cxx:211
 TAS3File.cxx:212
 TAS3File.cxx:213
 TAS3File.cxx:214
 TAS3File.cxx:215
 TAS3File.cxx:216
 TAS3File.cxx:217
 TAS3File.cxx:218
 TAS3File.cxx:219
 TAS3File.cxx:220
 TAS3File.cxx:221
 TAS3File.cxx:222
 TAS3File.cxx:223
 TAS3File.cxx:224
 TAS3File.cxx:225
 TAS3File.cxx:226
 TAS3File.cxx:227
 TAS3File.cxx:228
 TAS3File.cxx:229
 TAS3File.cxx:230
 TAS3File.cxx:231
 TAS3File.cxx:232
 TAS3File.cxx:233
 TAS3File.cxx:234
 TAS3File.cxx:235
 TAS3File.cxx:236
 TAS3File.cxx:237
 TAS3File.cxx:238
 TAS3File.cxx:239
 TAS3File.cxx:240
 TAS3File.cxx:241
 TAS3File.cxx:242
 TAS3File.cxx:243
 TAS3File.cxx:244
 TAS3File.cxx:245
 TAS3File.cxx:246
 TAS3File.cxx:247
 TAS3File.cxx:248
 TAS3File.cxx:249
 TAS3File.cxx:250
 TAS3File.cxx:251
 TAS3File.cxx:252
 TAS3File.cxx:253
 TAS3File.cxx:254
 TAS3File.cxx:255
 TAS3File.cxx:256
 TAS3File.cxx:257
 TAS3File.cxx:258
 TAS3File.cxx:259
 TAS3File.cxx:260
 TAS3File.cxx:261
 TAS3File.cxx:262
 TAS3File.cxx:263
 TAS3File.cxx:264
 TAS3File.cxx:265