#include "TWebFile.h"
#include "TROOT.h"
#include "TSocket.h"
#include "Bytes.h"
#include "TError.h"
#include "TSystem.h"
#include <errno.h>
#include <stdlib.h>
#ifdef WIN32
#define EADDRINUSE 10048
#define EISCONN 10056
#endif
static const char *gUserAgent = "User-Agent: ROOT-TWebFile/1.0";
class TWebSocket {
private:
TWebFile *fWebFile;
public:
TWebSocket(TWebFile *f);
~TWebSocket();
void ReOpen();
};
TWebSocket::TWebSocket(TWebFile *f)
{
fWebFile = f;
if (!f->fSocket)
ReOpen();
}
TWebSocket::~TWebSocket()
{
if (!fWebFile->fHTTP11) {
delete fWebFile->fSocket;
fWebFile->fSocket = 0;
}
}
void TWebSocket::ReOpen()
{
if (fWebFile->fSocket)
delete fWebFile->fSocket;
for (Int_t i = 0; i < 5; i++) {
fWebFile->fSocket = new TSocket(fWebFile->fUrl.GetHost(),
fWebFile->fUrl.GetPort());
if (!fWebFile->fSocket->IsValid()) {
delete fWebFile->fSocket;
fWebFile->fSocket = 0;
if (gSystem->GetErrno() == EADDRINUSE || gSystem->GetErrno() == EISCONN) {
gSystem->Sleep(i*10);
} else {
::Error("TWebSocket::ReOpen", "cannot connect to remote host %s (errno=%d)",
fWebFile->fUrl.GetHost(), gSystem->GetErrno());
return;
}
} else
return;
}
}
ClassImp(TWebFile)
TWebFile::TWebFile(const char *url) : TFile(url, "WEB")
{
Init(kFALSE);
}
TWebFile::TWebFile(TUrl url) : TFile(url.GetUrl(), "WEB")
{
Init(kFALSE);
}
TWebFile::~TWebFile()
{
delete fSocket;
}
void TWebFile::Init(Bool_t)
{
char buf[4];
int err;
fSocket = 0;
if ((err = GetHead()) < 0) {
if (err == -2)
Error("TWebFile", "%s does not exist", fUrl.GetUrl());
MakeZombie();
gDirectory = gROOT;
return;
}
if (fIsRootFile) {
Seek(0);
if (ReadBuffer(buf, 4)) {
MakeZombie();
gDirectory = gROOT;
return;
}
if (strncmp(buf, "root", 4) && strncmp(buf, "PK", 2)) {
Error("TWebFile", "%s is not a ROOT file", fUrl.GetUrl());
MakeZombie();
gDirectory = gROOT;
return;
}
}
TFile::Init(kFALSE);
fD = -2;
}
Bool_t TWebFile::IsOpen() const
{
return IsZombie() ? kFALSE : kTRUE;
}
Int_t TWebFile::ReOpen(Option_t *mode)
{
TString opt = mode;
opt.ToUpper();
if (opt != "READ" && opt != "UPDATE")
Error("ReOpen", "mode must be either READ or UPDATE, not %s", opt.Data());
if (opt == "UPDATE")
Error("ReOpen", "update mode not allowed for a TWebFile");
return 1;
}
Bool_t TWebFile::ReadBuffer(char *buf, Int_t len)
{
Int_t st;
if ((st = ReadBufferViaCache(buf, len))) {
if (st == 2)
return kTRUE;
return kFALSE;
}
if (!fHasModRoot)
return ReadBuffer10(buf, len);
TString msg = "GET ";
msg += fUrl.GetProtocol();
msg += "://";
msg += fUrl.GetHost();
msg += ":";
msg += fUrl.GetPort();
msg += "/";
msg += fUrl.GetFile();
msg += "?";
msg += fOffset;
msg += ":";
msg += len;
msg += "\r\n";
if (GetFromWeb(buf, len, msg) == -1)
return kTRUE;
fOffset += len;
return kFALSE;
}
Bool_t TWebFile::ReadBuffer10(char *buf, Int_t len)
{
TString msg = "GET ";
msg += fUrl.GetProtocol();
msg += "://";
msg += fUrl.GetHost();
msg += ":";
msg += fUrl.GetPort();
msg += "/";
msg += fUrl.GetFile();
if (fHTTP11)
msg += " HTTP/1.1";
else
msg += " HTTP/1.0";
msg += "\r\n";
if (fHTTP11) {
msg += "Host: ";
msg += fUrl.GetHost();
msg += "\r\n";
}
msg += gUserAgent;
msg += "\r\n";
msg += "Range: bytes=";
msg += fOffset;
msg += "-";
msg += fOffset+len-1;
msg += "\r\n\r\n";
Int_t n;
while ((n = GetFromWeb10(buf, len, msg)) == -2) { }
if (n == -1)
return kTRUE;
fOffset += len;
return kFALSE;
}
Bool_t TWebFile::ReadBuffers(char *buf, Long64_t *pos, Int_t *len, Int_t nbuf)
{
if (!fHasModRoot)
return ReadBuffers10(buf, pos, len, nbuf);
TString msgh = "GET ";
msgh += fUrl.GetProtocol();
msgh += "://";
msgh += fUrl.GetHost();
msgh += ":";
msgh += fUrl.GetPort();
msgh += "/";
msgh += fUrl.GetFile();
msgh += "?";
TString msg = msgh;
Int_t k = 0, n = 0;
for (Int_t i = 0; i < nbuf; i++) {
if (n) msg += ",";
msg += pos[i] + fArchiveOffset;
msg += ":";
msg += len[i];
n += len[i];
if (msg.Length() > 8000) {
msg += "\r\n";
if (GetFromWeb(&buf[k], n, msg) == -1)
return kTRUE;
msg = msgh;
k += n;
n = 0;
}
}
msg += "\r\n";
if (GetFromWeb(&buf[k], n, msg) == -1)
return kTRUE;
return kFALSE;
}
Bool_t TWebFile::ReadBuffers10(char *buf, Long64_t *pos, Int_t *len, Int_t nbuf)
{
TString msgh = "GET ";
msgh += fUrl.GetProtocol();
msgh += "://";
msgh += fUrl.GetHost();
msgh += ":";
msgh += fUrl.GetPort();
msgh += "/";
msgh += fUrl.GetFile();
if (fHTTP11)
msgh += " HTTP/1.1";
else
msgh += " HTTP/1.0";
msgh += "\r\n";
if (fHTTP11) {
msgh += "Host: ";
msgh += fUrl.GetHost();
msgh += "\r\n";
}
msgh += gUserAgent;
msgh += "\r\n";
msgh += "Range: bytes=";
TString msg = msgh;
Int_t k = 0, n = 0, r;
for (Int_t i = 0; i < nbuf; i++) {
if (n) msg += ",";
msg += pos[i] + fArchiveOffset;
msg += "-";
msg += pos[i] + fArchiveOffset + len[i] - 1;
n += len[i];
if (msg.Length() > 8000) {
msg += "\r\n\r\n";
while ((r = GetFromWeb10(&buf[k], n, msg)) == -2) { }
if (r == -1)
return kTRUE;
msg = msgh;
k += n;
n = 0;
}
}
msg += "\r\n\r\n";
while ((r = GetFromWeb10(&buf[k], n, msg)) == -2) { }
if (r == -1)
return kTRUE;
return kFALSE;
}
Int_t TWebFile::GetFromWeb(char *buf, Int_t len, const TString &msg)
{
if (!len) return 0;
TSocket s(fUrl.GetHost(), fUrl.GetPort());
if (!s.IsValid()) {
Error("GetFromWeb", "cannot connect to remote host %s", fUrl.GetHost());
return -1;
}
if (s.SendRaw(msg.Data(), msg.Length()) == -1) {
Error("GetFromWeb", "error sending command to remote host %s", fUrl.GetHost());
return -1;
}
if (s.RecvRaw(buf, len) == -1) {
Error("GetFromWeb", "error receiving data from remote host %s", fUrl.GetHost());
return -1;
}
fBytesRead += len;
SetFileBytesRead(GetFileBytesRead() + len);
return 0;
}
Int_t TWebFile::GetFromWeb10(char *buf, Int_t len, const TString &msg)
{
if (!len) return 0;
TWebSocket ws(this);
if (!fSocket || !fSocket->IsValid()) {
Error("GetFromWeb10", "cannot connect to remote host %s", fUrl.GetHost());
return -1;
}
if (fSocket->SendRaw(msg.Data(), msg.Length()) == -1) {
Error("GetFromWeb10", "error sending command to remote host %s", fUrl.GetHost());
return -1;
}
char line[1024];
Int_t n, ret = 0, nranges = 0, ltot = 0;
TString boundary, boundaryEnd;
Long64_t first = -1, last = -1, tot;
while ((n = GetLine(fSocket, line, 1024)) >= 0) {
if (n == 0) {
if (ret < 0)
return ret;
if (first >= 0) {
Int_t ll = Int_t(last - first) + 1;
if (fSocket->RecvRaw(&buf[ltot], ll) == -1) {
Error("GetFromWeb10", "error receiving data from remote host %s", fUrl.GetHost());
return -1;
}
ltot += ll;
fBytesRead += ll;
SetFileBytesRead(GetFileBytesRead() + ll);
first = -1;
if (boundary == "")
break;
}
continue;
}
if (gDebug > 0)
Info("GetFromWeb10", "header: %s", line);
if (boundaryEnd == line) {
if (gDebug > 0)
Info("GetFromWeb10", "got all headers");
break;
}
if (boundary == line) {
nranges++;
if (gDebug > 0)
Info("GetFromWeb10", "get new multipart byte range (%d)", nranges);
}
TString res = line;
if (res.BeginsWith("HTTP/1.")) {
TString scode = res(9, 3);
Int_t code = scode.Atoi();
if (code != 206) {
ret = -1;
TString mess = res(13, 1000);
Error("GetFromWeb10", "%s: %s (%d)", fUrl.GetUrl(), mess.Data(), code);
}
}
if (res.BeginsWith("Content-Type: multipart")) {
boundary = "--" + res(res.Index("boundary=")+9, 1000);
boundaryEnd = boundary + "--";
}
if (res.BeginsWith("Content-range:")) {
#ifdef R__WIN32
sscanf(res.Data(), "Content-range: bytes %I64d-%I64d/%I64d", &first, &last, &tot);
#else
sscanf(res.Data(), "Content-range: bytes %lld-%lld/%lld", &first, &last, &tot);
#endif
}
if (res.BeginsWith("Content-Range:")) {
#ifdef R__WIN32
sscanf(res.Data(), "Content-Range: bytes %I64d-%I64d/%I64d", &first, &last, &tot);
#else
sscanf(res.Data(), "Content-Range: bytes %lld-%lld/%lld", &first, &last, &tot);
#endif
}
}
if (n == -1 && fHTTP11) {
if (gDebug > 0)
Info("GetFromWeb10", "HTTP/1.1 socket closed, reopen");
ws.ReOpen();
return -2;
}
if (ltot != len) {
Error("GetFromWeb10", "error receiving expected amount of data (got %d, expected %d) from remote host %s",
ltot, len, fUrl.GetHost());
return -1;
}
return 0;
}
void TWebFile::Seek(Long64_t offset, ERelativeTo pos)
{
switch (pos) {
case kBeg:
fOffset = offset + fArchiveOffset;
break;
case kCur:
fOffset += offset;
break;
case kEnd:
if (fArchiveOffset)
Error("Seek", "seeking from end in archive is not (yet) supported");
fOffset = fEND - offset;
break;
}
}
Long64_t TWebFile::GetSize() const
{
if (!fHasModRoot || fSize >= 0)
return fSize;
Long64_t size;
char asize[64];
TString msg = "GET ";
msg += fUrl.GetProtocol();
msg += "://";
msg += fUrl.GetHost();
msg += ":";
msg += fUrl.GetPort();
msg += "/";
msg += fUrl.GetFile();
msg += "?";
msg += -1;
msg += "\r\n";
if (const_cast<TWebFile*>(this)->GetFromWeb(asize, 64, msg) == -1)
return kMaxInt;
#ifndef R__WIN32
size = atoll(asize);
#else
size = _atoi64(asize);
#endif
fSize = size;
return size;
}
Int_t TWebFile::GetHead()
{
fSize = -1;
fHasModRoot = kFALSE;
fHTTP11 = kFALSE;
TString msg = "HEAD ";
msg += fUrl.GetProtocol();
msg += "://";
msg += fUrl.GetHost();
msg += ":";
msg += fUrl.GetPort();
msg += "/";
msg += fUrl.GetFile();
msg += " HTTP/1.0";
msg += "\r\n";
msg += gUserAgent;
msg += "\r\n\r\n";
TSocket *s = 0;
for (Int_t i = 0; i < 5; i++) {
s = new TSocket(fUrl.GetHost(), fUrl.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 remote 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 remote host %s", fUrl.GetHost());
delete s;
return -1;
}
char line[1024];
Int_t n, ret = 0;
while ((n = GetLine(s, line, 1024)) >= 0) {
if (n == 0) {
if (gDebug > 0)
Info("GetHead", "got all headers");
delete s;
return ret;
}
if (gDebug > 0)
Info("GetHead", "header: %s", line);
TString res = line;
if (res.BeginsWith("HTTP/1.")) {
if (res.BeginsWith("HTTP/1.1"))
fHTTP11 = kTRUE;
TString scode = res(9, 3);
Int_t code = scode.Atoi();
if (code == 500)
fHasModRoot = kTRUE;
else if (code == 404)
ret = -2;
else if (code > 200) {
ret = -1;
TString mess = res(13, 1000);
Error("GetHead", "%s: %s (%d)", fUrl.GetUrl(), mess.Data(), code);
}
}
if (res.BeginsWith("Content-Length:")) {
TString slen = res(16, 1000);
fSize = slen.Atoll();
}
}
delete s;
return ret;
}
Int_t TWebFile::GetLine(TSocket *s, char *line, Int_t size)
{
char c;
Int_t err, n = 0;
while ((err = s->RecvRaw(&c, 1)) >= 0) {
if (n == size-1 || c == '\n' || err == 0) {
if (line[n-1] == '\r')
n--;
break;
}
line[n++] = c;
}
line[n] = '\0';
if (err < 0) {
if (!fHTTP11 || gDebug > 0)
Error("GetLine", "error receiving data from remote host %s", fUrl.GetHost());
return -1;
}
return n;
}
Last change: Thu Dec 18 09:31:39 2008
Last generated: 2008-12-18 09:31
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.