ROOT logo
// @(#)root/net:$Id: TGSFile.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.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGSFile                                                              //
//                                                                      //
// A TGSFile is a normal TWebFile but it reads data from the            //
// Google Storage server. As a derived TWebFile class TGSFile it is     //
// a read only file. The HTTP requests are generated by THTTPMessage    //
// objects with the auth_prefix set as GOOG1. The user id and secret    //
// pass required to sign the requests are passed through the            //
// environment variables GT_ACCESS_ID and GT_ACCESS_KEY.                //
// For more information check:                                          //
//   http://code.google.com/apis/storage/docs/getting-started.html      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

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

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

ClassImp(TGSFile)

//______________________________________________________________________________
TGSFile::TGSFile(const char *path, Option_t *) : TWebFile(path, "IO")
{
   // For TGSFile to properly work you need to set up
   // environment variables GT_ACCESS_ID and GT_ACCESS_KEY.
   // The format of the path is: server/bucket/file, e.g.
   // f = new TGSFile("gs://commondatastorage.googleapis.com/roots3/hsimple.root")

   TString tpath = path;

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

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

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

      if (slash == kNPOS) {
         Error("TGSFile","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 = "GOOG1";
   fAccessId   = gSystem->Getenv("GT_ACCESS_ID");
   fAccessKey  = gSystem->Getenv("GT_ACCESS_KEY");
   if (fAccessId == "" || fAccessKey == "") {
      if (fAccessId == "")  Error("TGSFile", "shell variable GT_ACCESS_ID not set");
      if (fAccessKey == "") Error("TGSFile", "shell variable GT_ACCESS_KEY not set");
      goto zombie;
   }

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

//______________________________________________________________________________
Int_t TGSFile::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;
}

#if 0
// Currently not supported on the Google Storage cloud side because
// the chunked responses are not "standard"
//______________________________________________________________________________
Bool_t  TGSFile::ReadBuffers10(char *buf, Long64_t *pos, Int_t *len, Int_t nbuf)
{
   // Read specified byte range from Google Storage.
   // This routine connects to the Google Storage server, sends the
   // request created by THTTPMessage and returns the buffer.
   // Returns kTRUE in case of error.
	
   THTTPMessage gsget = THTTPMessage(kGET, fRealName, GetBucket(),
                                     GetUrl().GetHost(), GetAuthPrefix(),
                                     GetAccessId(), GetAccessKey(),
                                     fOffset, pos, len, nbuf);
   TString msg = gsget.GetRequest();
   Int_t size = gsget.GetLength();
   Int_t curbuf = gsget.GetCurrentBuffer();
   
   //printf("Num of buffers %d, current buffer %d size %d\n",nbuf,curbuf,size);
   
   Int_t n = GetFromWeb10(buf, size, 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 += size;

   if (nbuf == curbuf) {
      return kFALSE;         
   } else {
      return ReadBuffers10(&buf[size], &pos[curbuf], &len[curbuf], nbuf-curbuf);
   }   
}
#endif

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

//______________________________________________________________________________
Bool_t TGSFile::ReadBuffer10(char *buf, Int_t len)
{
   // Read specified byte range from Google Storage.
   // This routine connects to the Google Storage 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 gsget = THTTPMessage(kGET, fRealName, GetBucket(),
                                     GetUrl().GetHost(), GetAuthPrefix(),
                                     GetAccessId(), GetAccessKey(),
                                     0, pos, lens, nbuf);
   TString msg = gsget.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;
}

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