XRootD
XrdHttpReq Class Reference

#include <XrdHttpReq.hh>

+ Inheritance diagram for XrdHttpReq:
+ Collaboration diagram for XrdHttpReq:

Public Types

enum  ReqType : int {
  rtUnset = -1 ,
  rtUnknown = 0 ,
  rtMalformed ,
  rtGET ,
  rtHEAD ,
  rtPUT ,
  rtOPTIONS ,
  rtPATCH ,
  rtDELETE ,
  rtPROPFIND ,
  rtMKCOL ,
  rtMOVE ,
  rtPOST ,
  rtCOPY ,
  rtCount
}
 These are the HTTP/DAV requests that we support. More...
 

Public Member Functions

 XrdHttpReq (XrdHttpProtocol *protinstance, const XrdHttpReadRangeHandler::Configuration &rcfg)
 
virtual ~XrdHttpReq ()
 
void addCgi (const std::string &key, const std::string &value)
 
void appendOpaque (XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
 
std::string buildPartialHdr (long long bytestart, long long byteend, long long filesize, char *token)
 Build a partial header for a multipart response. More...
 
std::string buildPartialHdrEnd (char *token)
 Build the closing part for a multipart response. More...
 
virtual bool Data (XrdXrootd::Bridge::Context &info, const struct iovec *iovP, int iovN, int iovL, bool final)
 
virtual bool Done (XrdXrootd::Bridge::Context &info)
 the result context More...
 
virtual bool Error (XrdXrootd::Bridge::Context &info, int ecode, const char *etext)
 
virtual int File (XrdXrootd::Bridge::Context &info, int dlen)
 
int getHttpStatusCode ()
 
int getInitialStatusCode ()
 
int parseBody (char *body, long long len)
 Parse the body of a request, assuming that it's XML and that it's entirely in memory. More...
 
int parseFirstLine (char *line, int len)
 Parse the first line of the header. More...
 
int parseLine (char *line, int len)
 Parse the header. More...
 
int ProcessHTTPReq ()
 
virtual bool Redir (XrdXrootd::Bridge::Context &info, int port, const char *hname)
 
int ReqReadV (const XrdHttpIOList &cl)
 Prepare the buffers for sending a readv request. More...
 
virtual void reset ()
 
void setHttpStatusCode (int code)
 
void setTransferStatusHeader (std::string &header)
 
const std::string & userAgent () const
 
- Public Member Functions inherited from XrdXrootd::Bridge::Result
 Result ()
 Constructor & Destructor. More...
 
virtual ~Result ()
 
virtual void Free (Bridge::Context &info, char *buffP, int buffL)
 
virtual bool Wait (Bridge::Context &info, int wtime, const char *wtext)
 
virtual Bridge::ResultWaitResp (Bridge::Context &info, int wtime, const char *wtext)
 

Public Attributes

std::map< std::string, std::string > allheaders
 
bool closeAfterError
 
int depth
 
std::string destination
 The destination field specified in the req. More...
 
long long etagval
 
std::string etext
 
char fhandle [4]
 
long filectime
 
long fileflags
 
long filemodtime
 
long long filesize
 
bool final
 true -> final result More...
 
bool fopened
 
std::string hdr2cgistr
 Additional opaque info that may come from the hdr2cgi directive. More...
 
bool headerok
 Tells if we have finished reading the header. More...
 
std::string host
 The host field specified in the req. More...
 
int iovL
 byte count More...
 
int iovN
 array count More...
 
const struct iovec * iovP
 The latest data chunks got from the xrd layer. These are valid only inside the callbacks! More...
 
bool keepalive
 
ssize_t length
 
bool length_seen {false}
 
bool m_appended_asize {false}
 Track whether we already appended the oss.asize argument for PUTs. More...
 
bool m_appended_hdr2cgistr
 
std::string m_digest_header
 The computed digest for the HTTP response header. More...
 
std::string m_origin
 
std::map< std::string, std::string > m_repr_digest
 Repr-Digest map where the key is the digest name and the value is the base64 encoded digest value. More...
 
XrdHttpChecksumHandler::XrdHttpChecksumRawPtr m_req_cksum = nullptr
 The checksum that was ran for this request. More...
 
XrdOucString m_resource_with_digest
 
std::string m_want_digest
 The requested digest type. More...
 
std::map< std::string, uint8_t > m_want_repr_digest
 
XrdHttpMonState monState
 
int mScitag
 
XrdOucEnvopaque
 The opaque data, after parsing. More...
 
std::vector< readahead_listralist
 
bool readClosing
 
XrdHttpReadRangeHandler readRangeHandler
 Tracking the next ranges of data to read during GET. More...
 
XrdOucString redirdest
 
int reqstate
 State machine to talk to the bridge. More...
 
ReqType request
 The request we got. More...
 
std::string requestverb
 
XrdOucString resource
 The resource specified by the request, stripped of opaque data. More...
 
XrdOucString resourceplusopaque
 The resource specified by the request, including all the opaque data. More...
 
unsigned int rwOpDone
 To coordinate multipart responses across multiple calls. More...
 
unsigned int rwOpPartialDone
 
bool sendcontinue
 
std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::time_point::min()
 
std::string stringresp
 If we want to give a string as a response, we compose it here. More...
 
long long writtenbytes
 In a long write, we track where we have arrived. More...
 
XErrorCode xrderrcode
 
ClientRequest xrdreq
 The last issued xrd request, often pending. More...
 
XResponseType xrdresp
 The last response data we got. More...
 

Detailed Description

Definition at line 66 of file XrdHttpReq.hh.

Member Enumeration Documentation

◆ ReqType

enum XrdHttpReq::ReqType : int

These are the HTTP/DAV requests that we support.

Enumerator
rtUnset 
rtUnknown 
rtMalformed 
rtGET 
rtHEAD 
rtPUT 
rtOPTIONS 
rtPATCH 
rtDELETE 
rtPROPFIND 
rtMKCOL 
rtMOVE 
rtPOST 
rtCOPY 
rtCount 

Definition at line 77 of file XrdHttpReq.hh.

77  : int {
78  rtUnset = -1,
79  rtUnknown = 0,
81  rtGET,
82  rtHEAD,
83  rtPUT,
84  rtOPTIONS,
85  rtPATCH,
86  rtDELETE,
87  rtPROPFIND,
88  rtMKCOL,
89  rtMOVE,
90  rtPOST,
91  rtCOPY,
92  rtCount
93  };

Constructor & Destructor Documentation

◆ XrdHttpReq()

XrdHttpReq::XrdHttpReq ( XrdHttpProtocol protinstance,
const XrdHttpReadRangeHandler::Configuration rcfg 
)
inline

Definition at line 208 of file XrdHttpReq.hh.

208  :
209  readRangeHandler(rcfg), closeAfterError(false), keepalive(true) {
210 
211  prot = protinstance;
212  length = 0;
213  //xmlbody = 0;
214  depth = 0;
215  opaque = 0;
216  writtenbytes = 0;
217  fopened = false;
218  headerok = false;
219  mScitag = -1;
220  };
bool keepalive
Definition: XrdHttpReq.hh:295
bool headerok
Tells if we have finished reading the header.
Definition: XrdHttpReq.hh:285
ssize_t length
Definition: XrdHttpReq.hh:296
bool closeAfterError
Definition: XrdHttpReq.hh:293
long long writtenbytes
In a long write, we track where we have arrived.
Definition: XrdHttpReq.hh:364
XrdOucEnv * opaque
The opaque data, after parsing.
Definition: XrdHttpReq.hh:279
bool fopened
Definition: XrdHttpReq.hh:355
XrdHttpReadRangeHandler readRangeHandler
Tracking the next ranges of data to read during GET.
Definition: XrdHttpReq.hh:288

References depth, fopened, headerok, length, mScitag, opaque, and writtenbytes.

◆ ~XrdHttpReq()

XrdHttpReq::~XrdHttpReq ( )
virtual

Definition at line 112 of file XrdHttpReq.cc.

112  {
113  //if (xmlbody) xmlFreeDoc(xmlbody);
114 
115  reset();
116 }
virtual void reset()
Definition: XrdHttpReq.cc:2771

References reset().

+ Here is the call graph for this function:

Member Function Documentation

◆ addCgi()

void XrdHttpReq::addCgi ( const std::string &  key,
const std::string &  value 
)

Definition at line 819 of file XrdHttpReq.cc.

819  {
820  if (hdr2cgistr.length() > 0) {
821  hdr2cgistr.append("&");
822  }
823  hdr2cgistr.append(key);
824  hdr2cgistr.append("=");
825  hdr2cgistr.append(value);
826 }
std::string hdr2cgistr
Additional opaque info that may come from the hdr2cgi directive.
Definition: XrdHttpReq.hh:320

References hdr2cgistr.

Referenced by parseLine().

+ Here is the caller graph for this function:

◆ appendOpaque()

void XrdHttpReq::appendOpaque ( XrdOucString s,
XrdSecEntity secent,
char *  hash,
time_t  tnow 
)

Definition at line 711 of file XrdHttpReq.cc.

711  {
712 
713  int l = 0;
714  char * p = 0;
715  if (opaque)
716  p = opaque->Env(l);
717 
718  if (hdr2cgistr.empty() && (l < 2) && !hash) return;
719 
720  // this works in most cases, except if the url already contains the xrdhttp tokens
721  s = s + "?";
722  if (!hdr2cgistr.empty()) {
723  s += encode_opaque(hdr2cgistr).c_str();
724  }
725  if (p && (l > 1)) {
726  if (!hdr2cgistr.empty()) {
727  s = s + "&";
728  }
729  s = s + encode_opaque(p + 1).c_str();
730  }
731 
732  if (hash) {
733  if (l > 1) s += "&";
734  s += "xrdhttptk=";
735  s += hash;
736 
737  s += "&xrdhttptime=";
738  char buf[256];
739  sprintf(buf, "%lld", (long long) tnow);
740  s += buf;
741 
742  if (secent) {
743  if (secent->name) {
744  s += "&xrdhttpname=";
745  s += encode_str(secent->name).c_str();
746  }
747  }
748 
749  if (secent->vorg) {
750  s += "&xrdhttpvorg=";
751  s += encode_str(secent->vorg).c_str();
752  }
753 
754  if (secent->host) {
755  s += "&xrdhttphost=";
756  s += encode_str(secent->host).c_str();
757  }
758 
759  if (secent->moninfo) {
760  s += "&xrdhttpdn=";
761  s += encode_str(secent->moninfo).c_str();
762  }
763 
764  if (secent->role) {
765  s += "&xrdhttprole=";
766  s += encode_str(secent->role).c_str();
767  }
768 
769  if (secent->grps) {
770  s += "&xrdhttpgrps=";
771  s += encode_str(secent->grps).c_str();
772  }
773 
774  if (secent->endorsements) {
775  s += "&xrdhttpendorsements=";
776  s += encode_str(secent->endorsements).c_str();
777  }
778 
779  if (secent->credslen) {
780  s += "&xrdhttpcredslen=";
781  char buf[16];
782  sprintf(buf, "%d", secent->credslen);
783  s += encode_str(buf).c_str();
784  }
785 
786  if (secent->credslen) {
787  if (secent->creds) {
788  s += "&xrdhttpcreds=";
789  // Apparently this string might be not 0-terminated (!)
790  char *zerocreds = strndup(secent->creds, secent->credslen);
791  if (zerocreds) {
792  s += encode_str(zerocreds).c_str();
793  free(zerocreds);
794  }
795  }
796  }
797  }
798  }
std::string encode_opaque(const std::string &opaque)
std::string encode_str(const std::string &str)
char * Env(int &envlen)
Definition: XrdOucEnv.hh:48
char * vorg
Entity's virtual organization(s)
Definition: XrdSecEntity.hh:71
int credslen
Length of the 'creds' data.
Definition: XrdSecEntity.hh:78
char * creds
Raw entity credentials or cert.
Definition: XrdSecEntity.hh:77
char * grps
Entity's group name(s)
Definition: XrdSecEntity.hh:73
char * name
Entity's name.
Definition: XrdSecEntity.hh:69
char * role
Entity's role(s)
Definition: XrdSecEntity.hh:72
char * endorsements
Protocol specific endorsements.
Definition: XrdSecEntity.hh:75
char * moninfo
Information for monitoring.
Definition: XrdSecEntity.hh:76
char * host
Entity's host name dnr dependent.
Definition: XrdSecEntity.hh:70

References XrdSecEntity::creds, XrdSecEntity::credslen, encode_opaque(), encode_str(), XrdSecEntity::endorsements, XrdOucEnv::Env(), XrdSecEntity::grps, hdr2cgistr, XrdSecEntity::host, XrdSecEntity::moninfo, XrdSecEntity::name, opaque, XrdSecEntity::role, and XrdSecEntity::vorg.

Referenced by XrdHttpProtocol::Process(), ProcessHTTPReq(), and Redir().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ buildPartialHdr()

std::string XrdHttpReq::buildPartialHdr ( long long  bytestart,
long long  byteend,
long long  filesize,
char *  token 
)

Build a partial header for a multipart response.

Definition at line 488 of file XrdHttpReq.cc.

488  {
489  std::ostringstream s;
490 
491  s << "\r\n--" << token << "\r\n";
492  s << "Content-type: text/plain; charset=UTF-8\r\n";
493  s << "Content-range: bytes " << bytestart << "-" << byteend << "/" << fsz << "\r\n\r\n";
494 
495  return s.str();
496 }

◆ buildPartialHdrEnd()

std::string XrdHttpReq::buildPartialHdrEnd ( char *  token)

Build the closing part for a multipart response.

Definition at line 498 of file XrdHttpReq.cc.

498  {
499  std::ostringstream s;
500 
501  s << "\r\n--" << token << "--\r\n";
502 
503  return s.str();
504 }

◆ Data()

bool XrdHttpReq::Data ( XrdXrootd::Bridge::Context info,
const struct iovec *  iovP,
int  iovN,
int  iovL,
bool  final 
)
virtual

Effect a client data response.

The Data() method is called when Run() resulted in a successful data response. The method should rewrite the data and send it to the client using the associated XrdLink object. As an example, 1) Result::Data(info, iovP, iovN, iovL) is called. 2) Inspect iovP, rewrite the data. 3) Send the response: info->linkP->Send(new_iovP, new_iovN, new_iovL); 4) Handle send errors and cleanup(e.g. deallocate storage). 5) Return, the exchange is now complete.

Parameters
infothe context associated with the result.
iovPa pointer to the iovec structure containing the xrootd data response about to be sent to the client. The request header is not included in the iovec structure. The elements of this structure must not be modified by the method.
iovNthe number of elements in the iovec structure array.
iovLtotal number of data bytes that would be sent to the client. This is simply the sum of all the lengths in the iovec.
finalTrue is this is the final result. Otherwise, this is a partial result (i.e. kXR_oksofar) and more data will result causing additional callbacks.
Returns
true continue normal processing. false terminate the bridge and close the link.
Parameters
infothe result context
iovPpointer to data array
iovNarray count
iovLbyte count
finaltrue -> final result

Implements XrdXrootd::Bridge::Result.

Definition at line 506 of file XrdHttpReq.cc.

512  {
513 
514  TRACE(REQ, " XrdHttpReq::Data! final=" << final);
515 
516  this->xrdresp = kXR_ok;
517  this->iovP = iovP_;
518  this->iovN = iovN_;
519  this->iovL = iovL_;
520  this->final = final_;
521 
522  if (PostProcessHTTPReq(final_)) reset();
523 
524  return true;
525 
526 };
@ kXR_ok
Definition: XProtocol.hh:941
#define TRACE(act, x)
Definition: XrdTrace.hh:63
XResponseType xrdresp
The last response data we got.
Definition: XrdHttpReq.hh:337
int iovL
byte count
Definition: XrdHttpReq.hh:345
const struct iovec * iovP
The latest data chunks got from the xrd layer. These are valid only inside the callbacks!
Definition: XrdHttpReq.hh:343
int iovN
array count
Definition: XrdHttpReq.hh:344

References iovL, iovN, iovP, kXR_ok, reset(), TRACE, and xrdresp.

+ Here is the call graph for this function:

◆ Done()

bool XrdHttpReq::Done ( XrdXrootd::Bridge::Context info)
virtual

the result context

Effect a client acknowledgement.

The Done() method is called when Run() resulted in success and there is no associated data for the client (equivalent to a simple kXR_ok response).

Parameters
infothe context associated with the result.
Returns
true continue normal processing. false terminate the bridge and close the link.

Implements XrdXrootd::Bridge::Result.

Definition at line 552 of file XrdHttpReq.cc.

552  {
553 
554  TRACE(REQ, " XrdHttpReq::Done");
555 
556  xrdresp = kXR_ok;
557 
558  this->iovN = 0;
559 
560  int r = PostProcessHTTPReq(true);
561  // Beware, we don't have to reset() if the result is 0
562  if (r) reset();
563  if (r < 0) return false;
564 
565 
566  return true;
567 };

References iovN, kXR_ok, reset(), TRACE, and xrdresp.

+ Here is the call graph for this function:

◆ Error()

bool XrdHttpReq::Error ( XrdXrootd::Bridge::Context info,
int  ecode,
const char *  etext 
)
virtual

Effect a client error response.

The Error() method is called when an error was encountered while processing the Run() request. The error should be reflected to the client.

Parameters
infothe context associated with the result.
ecodethe "kXR" error code describing the nature of the error. The code is in host byte format.
etexta null terminated string describing the error in human terms
Returns
true continue normal processing. false terminate the bridge and close the link.
Parameters
infothe result context
ecodethe "kXR" error code
etextassociated error message

Implements XrdXrootd::Bridge::Result.

Definition at line 569 of file XrdHttpReq.cc.

572  {
573 
574  TRACE(REQ, " XrdHttpReq::Error");
575 
576  xrdresp = kXR_error;
577  xrderrcode = (XErrorCode) ecode;
578 
579  if (etext_) {
580  char *s = escapeXML(etext_);
581  this->etext = s;
582  free(s);
583  }
584 
585  auto rc = PostProcessHTTPReq();
586  if (rc) {
587  reset();
588  }
589 
590  // If we are servicing a GET on a directory, it'll generate an error for the default
591  // OSS (we don't assume this is always true). Catch and suppress the error so we can instead
592  // generate a directory listing (if configured).
593  if ((request == rtGET) && (xrdreq.header.requestid == ntohs(kXR_open)) && (xrderrcode == kXR_isDirectory))
594  return true;
595 
596  return rc == 0;
597 };
XErrorCode
Definition: XProtocol.hh:1031
@ kXR_isDirectory
Definition: XProtocol.hh:1048
@ kXR_error
Definition: XProtocol.hh:945
struct ClientRequestHdr header
Definition: XProtocol.hh:887
kXR_unt16 requestid
Definition: XProtocol.hh:159
@ kXR_open
Definition: XProtocol.hh:123
char * escapeXML(const char *str)
std::string etext
Definition: XrdHttpReq.hh:339
ReqType request
The request we got.
Definition: XrdHttpReq.hh:269
XErrorCode xrderrcode
Definition: XrdHttpReq.hh:338
ClientRequest xrdreq
The last issued xrd request, often pending.
Definition: XrdHttpReq.hh:334

References escapeXML(), etext, ClientRequest::header, kXR_error, kXR_isDirectory, kXR_open, request, ClientRequestHdr::requestid, reset(), rtGET, TRACE, xrderrcode, xrdreq, and xrdresp.

+ Here is the call graph for this function:

◆ File()

int XrdHttpReq::File ( XrdXrootd::Bridge::Context info,
int  dlen 
)
virtual

Notify callback that a sendfile() request is pending.

The File() method is called when Run() resulted in a sendfile response (i.e. sendfile() would have been used to send data to the client). This allows the callback to reframe the sendfile() data using the Send() method in the passed context object (see class Context above).

Parameters
infothe context associated with the result.
dlentotal number of data bytes that would be sent to the client.
Returns
true continue normal processing. false terminate the bridge and close the link.
Parameters
infothe result context
dlenbyte count

Implements XrdXrootd::Bridge::Result.

Definition at line 528 of file XrdHttpReq.cc.

530  {
531 
532  // sendfile about to be sent by bridge for fetching data for GET:
533  // no https, no chunked+trailer, no multirange
534 
535  //prot->SendSimpleResp(200, NULL, NULL, NULL, dlen);
536  int rc = info.Send(0, 0, 0, 0);
537  TRACE(REQ, " XrdHttpReq::File dlen:" << dlen << " send rc:" << rc);
538  bool start, finish;
539  // short read will be classed as error
540  if (rc) {
542  return false;
543  }
544 
545  if (readRangeHandler.NotifyReadResult(dlen, nullptr, start, finish) < 0)
546  return false;
547 
548 
549  return true;
550 };
void NotifyError()
Force handler to enter error state.
int NotifyReadResult(const ssize_t ret, const UserRange **const urp, bool &start, bool &allend)
Advance internal counters concerning received bytes.
virtual int Send(const struct iovec *headP, int headN, const struct iovec *tailP, int tailN)

References XrdHttpReadRangeHandler::NotifyError(), XrdHttpReadRangeHandler::NotifyReadResult(), readRangeHandler, XrdXrootd::Bridge::Context::Send(), and TRACE.

+ Here is the call graph for this function:

◆ getHttpStatusCode()

int XrdHttpReq::getHttpStatusCode ( )
inline

Definition at line 227 of file XrdHttpReq.hh.

227 { return httpStatusCode;}

◆ getInitialStatusCode()

int XrdHttpReq::getInitialStatusCode ( )
inline

Definition at line 226 of file XrdHttpReq.hh.

226 { return initialStatusCode;}

◆ parseBody()

int XrdHttpReq::parseBody ( char *  body,
long long  len 
)

Parse the body of a request, assuming that it's XML and that it's entirely in memory.

Definition at line 96 of file XrdHttpReq.cc.

96  {
97  /*
98  * The document being in memory, it has no base per RFC 2396,
99  * and the "noname.xml" argument will serve as its base.
100  */
101  //xmlbody = xmlReadMemory(body, len, "noname.xml", NULL, 0);
102  //if (xmlbody == NULL) {
103  // fprintf(stderr, "Failed to parse document\n");
104  // return 1;
105  //}
106 
107 
108 
109  return 1;
110 }

Referenced by ProcessHTTPReq().

+ Here is the caller graph for this function:

◆ parseFirstLine()

int XrdHttpReq::parseFirstLine ( char *  line,
int  len 
)

Parse the first line of the header.

Definition at line 319 of file XrdHttpReq.cc.

319  {
320 
321  char *key = line;
322 
323  int pos;
324 
325  // Do the naive parsing
326  if (!line) return -1;
327 
328  // Look for the first space-delimited token
329  char *p = strchr((char *) line, (int) ' ');
330  if (!p) {
332  return -1;
333  }
334 
335 
336  pos = p - line;
337  // The first token cannot be too long
338  if (pos > MAX_TK_LEN - 1) {
340  return -2;
341  }
342 
343  // The first space-delimited char cannot be the first one
344  // this allows to deal with the case when a client sends a first line that starts with a space " GET / HTTP/1.1"
345  if(pos == 0) {
347  return -4;
348  }
349 
350  // the first token must be non empty
351  if (pos > 0) {
352  line[pos] = 0;
353  char *val = line + pos + 1;
354 
355  // Here we are supposed to initialize whatever flag or variable that is needed
356  // by looking at the first token of the line
357 
358  // The token is key
359  // The remainder is val, look for the resource
360  p = strchr((char *) val, (int) ' ');
361 
362  if (!p) {
364  line[pos] = ' ';
365  return -3;
366  }
367 
368  *p = '\0';
369  parseResource(val);
370 
371  *p = ' ';
372 
373  // Xlate the known header lines
374  if (!strcmp(key, "GET")) {
375  request = rtGET;
376  } else if (!strcmp(key, "HEAD")) {
377  request = rtHEAD;
378  } else if (!strcmp(key, "PUT")) {
379  request = rtPUT;
380  } else if (!strcmp(key, "POST")) {
381  request = rtPOST;
382  } else if (!strcmp(key, "PATCH")) {
383  request = rtPATCH;
384  } else if (!strcmp(key, "OPTIONS")) {
385  request = rtOPTIONS;
386  } else if (!strcmp(key, "DELETE")) {
387  request = rtDELETE;
388  } else if (!strcmp(key, "PROPFIND")) {
390  } else if (!strcmp(key, "MKCOL")) {
391  request = rtMKCOL;
392  } else if (!strcmp(key, "MOVE")) {
393  request = rtMOVE;
394  } else if (!strcmp(key, "COPY")) {
395  request = rtCOPY;
396  } else {
397  request = rtUnknown;
398  }
399 
400  requestverb = key;
401 
402  // The last token should be the protocol. If it is HTTP/1.0, then
403  // keepalive is disabled by default.
404  if (!strcmp(p+1, "HTTP/1.0\r\n")) {
405  keepalive = false;
406  }
407  line[pos] = ' ';
408  }
409 
410  return 0;
411 }
#define MAX_TK_LEN
Definition: XrdHttpReq.cc:67
std::string requestverb
Definition: XrdHttpReq.hh:270

References keepalive, MAX_TK_LEN, request, requestverb, rtCOPY, rtDELETE, rtGET, rtHEAD, rtMalformed, rtMKCOL, rtMOVE, rtOPTIONS, rtPATCH, rtPOST, rtPROPFIND, rtPUT, and rtUnknown.

Referenced by XrdHttpProtocol::Process().

+ Here is the caller graph for this function:

◆ parseLine()

int XrdHttpReq::parseLine ( char *  line,
int  len 
)

Parse the header.

Definition at line 118 of file XrdHttpReq.cc.

118  {
119 
120  char *key = line;
121  int pos;
122 
123  // Do the parsing
124  if (!line) return -1;
125 
126 
127  char *p = strchr((char *) line, (int) ':');
128  if (!p) {
129 
131  return -1;
132  }
133 
134  pos = (p - line);
135  if (pos > (MAX_TK_LEN - 1)) {
136 
138  return -2;
139  }
140 
141  if (pos > 0) {
142  line[pos] = 0;
143  char *val = line + pos + 1;
144 
145  // Trim left
146  while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
147 
148  // We memorize the headers also as a string
149  // because external plugins may need to process it differently
150  std::string ss = val;
151  if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) != "\r\n") {
153  return -3;
154  }
155  trim(ss);
156  allheaders[key] = ss;
157 
158  // Here we are supposed to initialize whatever flag or variable that is needed
159  // by looking at the first token of the line
160  // The token is key
161  // The value is val
162 
163  // Screen out the needed header lines
164  if (!strcasecmp(key, "connection")) {
165 
166  if (!strcasecmp(val, "Keep-Alive\r\n")) {
167  keepalive = true;
168  } else if (!strcasecmp(val, "close\r\n")) {
169  keepalive = false;
170  }
171 
172  } else if (!strcasecmp(key, "host")) {
173  parseHost(val);
174  } else if (!strcasecmp(key, "range")) {
175  // (rfc2616 14.35.1) says if Range header contains any range
176  // which is syntactically invalid the Range header should be ignored.
177  // Therefore no need for the range handler to report an error.
179  } else if (!strcasecmp(key, "content-length")) {
180  // Parse and validate the Content-Length value (one-or-more digits,
181  // no sign, no embedded garbage, no overflow). Anything malformed
182  // gives the server an ambiguous body length and is an HTTP request
183  // smuggling primitive — reject with HTTP 400.
184  // Reference: RFC 9112 §6.2, RFC 7230 §3.3.3 rule 4.
185  ssize_t parsed = XrdHttpHeaderUtils::parseContentLength(val);
186  if (parsed < 0) {
188  return -6;
189  }
190  if (m_transfer_encoding_chunked) {
191  // A request that already declared Transfer-Encoding: chunked and
192  // now also sends Content-Length is the classic smuggling vector
193  // (the frontend and backend may disagree on which header wins).
194  // Reference: RFC 9112 §6.1.
196  return -8;
197  }
198  if (length_seen && parsed != length) {
199  // Two Content-Length headers with different values. The body
200  // length is ambiguous; reject.
201  // Reference: RFC 7230 §3.3.3 rule 4.
203  return -7;
204  }
205  length = parsed;
206  length_seen = true;
207 
208  } else if (!strcasecmp(key, "destination")) {
209  destination.assign(val, line+len-val);
210  trim(destination);
211  } else if (!strcasecmp(key, "want-digest")) {
212  // Discard Want-Repr-Digest in favor of Want-Digest
213  m_want_repr_digest.clear();
214  m_want_digest.assign(val, line + len - val);
216  //Transform the user requests' want-digest to lowercase
217  std::transform(m_want_digest.begin(), m_want_digest.end(), m_want_digest.begin(), ::tolower);
218  } else if (!strcasecmp(key, "depth")) {
219  depth = -1;
220  if (strcmp(val, "infinity"))
221  depth = atoll(val);
222 
223  } else if (!strcasecmp(key, "expect") && strstr(val, "100-continue")) {
224  sendcontinue = true;
225  } else if (!strcasecmp(key, "te") && strstr(val, "trailers")) {
226  m_trailer_headers = true;
227  } else if (!strcasecmp(key, "transfer-encoding")) {
228  // Tokenize the Transfer-Encoding list and verify that "chunked"
229  // is present AND is the final encoding. Anything else (substring
230  // matches like "chunkedX", a non-final "chunked", or only unknown
231  // codings) is rejected so a frontend proxy cannot disagree with us
232  // about how the body is framed.
233  // Reference: RFC 9112 §6.1, RFC 7230 §3.3.1.
236  return -4;
237  }
238  if (length_seen) {
239  // Content-Length was already accepted and now Transfer-Encoding:
240  // chunked arrives. Reject (see the matching check in the
241  // Content-Length branch above).
242  // Reference: RFC 9112 §6.1.
244  return -8;
245  }
246  m_transfer_encoding_chunked = true;
247  } else if (!strcasecmp(key, "x-transfer-status") && strstr(val, "true")) {
248  m_transfer_encoding_chunked = true;
249  m_status_trailer = true;
250  } else if (!strcasecmp(key, "scitag")) {
251  if(prot->pmarkHandle != nullptr) {
252  parseScitag(val);
253  }
254  } else if (!strcasecmp(key, "user-agent")) {
255  m_user_agent = val;
256  trim(m_user_agent);
257  } else if (!strcasecmp(key,"origin")) {
258  m_origin = val;
259  trim(m_origin);
260  } else if (!strcasecmp(key,"repr-digest")) {
262  } else if (!strcasecmp(key,"want-repr-digest")) {
263  if(m_want_digest.empty()) {
264  // If Want-Digest was set, don't parse want-repr-digest
266  }
267  } else {
268  // Some headers need to be translated into "local" cgi info.
269  auto it = std::find_if(prot->hdr2cgimap.begin(), prot->hdr2cgimap.end(),[key](const auto & item) {
270  return !strcasecmp(key,item.first.c_str());
271  });
272  if (it != prot->hdr2cgimap.end() && (opaque ? (0 == opaque->Get(it->second.c_str())) : true)) {
273  std::string s;
274  s.assign(val, line+len-val);
275  trim(s);
276  addCgi(it->second,s);
277  }
278  }
279 
280 
281  line[pos] = ':';
282  }
283 
284  return 0;
285 }
void trim(std::string &str)
Definition: XrdHttpReq.cc:78
static int parseTransferEncoding(const std::string &value)
static void parseWantReprDigest(const std::string &value, std::map< std::string, uint8_t > &output)
static void parseReprDigest(const std::string &value, std::map< std::string, std::string > &output)
static ssize_t parseContentLength(const std::string &value)
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
void ParseContentRange(const char *const line)
parse the line after a "Range: " http request header
std::string destination
The destination field specified in the req.
Definition: XrdHttpReq.hh:304
std::map< std::string, uint8_t > m_want_repr_digest
Definition: XrdHttpReq.hh:377
std::map< std::string, std::string > m_repr_digest
Repr-Digest map where the key is the digest name and the value is the base64 encoded digest value.
Definition: XrdHttpReq.hh:373
bool length_seen
Definition: XrdHttpReq.hh:297
std::string m_origin
Definition: XrdHttpReq.hh:368
std::string m_want_digest
The requested digest type.
Definition: XrdHttpReq.hh:307
std::map< std::string, std::string > allheaders
Definition: XrdHttpReq.hh:274
void addCgi(const std::string &key, const std::string &value)
Definition: XrdHttpReq.cc:819
bool sendcontinue
Definition: XrdHttpReq.hh:299
char * Get(const char *varname)
Definition: XrdOucEnv.hh:69

References addCgi(), allheaders, depth, destination, XrdOucEnv::Get(), XrdHttpProtocol::hdr2cgimap, keepalive, length, length_seen, m_origin, m_repr_digest, m_want_digest, m_want_repr_digest, MAX_TK_LEN, opaque, XrdHttpHeaderUtils::parseContentLength(), XrdHttpReadRangeHandler::ParseContentRange(), XrdHttpHeaderUtils::parseReprDigest(), XrdHttpHeaderUtils::parseTransferEncoding(), XrdHttpHeaderUtils::parseWantReprDigest(), XrdHttpProtocol::pmarkHandle, readRangeHandler, request, rtMalformed, sendcontinue, and trim().

Referenced by XrdHttpProtocol::Process().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ProcessHTTPReq()

int XrdHttpReq::ProcessHTTPReq ( )

Crunch an http request. Return values: 0->call Process again 1->request processed -1->error

If we have to add extra header information, add it here.

Definition at line 940 of file XrdHttpReq.cc.

940  {
941 
942  kXR_int32 l;
943  if (startTime == std::chrono::steady_clock::time_point::min()) startTime = std::chrono::steady_clock::now();
944 
945  // State variable for tracking the query parameter search
946  // - 0: Indicates we've not yet searched the URL for '?'
947  // - 1: Indicates we have a '?' and hence query parameters
948  // - 2: Indicates we do *not* have '?' present -- no query parameters
949  int query_param_status = 0;
950  if (!m_appended_asize) {
951  m_appended_asize = true;
952  if (request == rtPUT && length) {
953  if (query_param_status == 0) {
954  query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
955  }
956  resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
957  query_param_status = 1;
958  auto length_str = std::to_string(length);
959  resourceplusopaque.append("oss.asize=");
960  resourceplusopaque.append(length_str.c_str());
961  if (!opaque) {
962  opaque = new XrdOucEnv();
963  }
964  opaque->Put("oss.asize", length_str.c_str());
965  }
966  }
967 
969  if (!m_appended_hdr2cgistr && !hdr2cgistr.empty()) {
970  if (query_param_status == 0) {
971  query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
972  }
973  resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
974 
975  std::string hdr2cgistrEncoded = encode_opaque(hdr2cgistr);
976  resourceplusopaque.append(hdr2cgistrEncoded.c_str());
977  if (TRACING(TRACE_DEBUG)) {
978  // The obfuscation of "authz" will only be done if the server http.header2cgi config contains something that maps a header to this "authz" cgi.
979  // Unfortunately the obfuscation code will be called no matter what is configured in http.header2cgi.
980  std::string header2cgistrObf = obfuscateAuth(hdr2cgistr);
981 
982  TRACEI(DEBUG, "Appended header fields to opaque info: '"
983  << header2cgistrObf.c_str() << "'");
984 
985  }
986 
987  m_appended_hdr2cgistr = true;
988  }
989 
990  // Verify if we have an external handler for this request
991  if (reqstate == 0) {
992  XrdHttpExtHandler *exthandler = prot->FindMatchingExtHandler(*this);
993  if (exthandler) {
994  XrdHttpExtReq xreq(this, prot);
995  int r = exthandler->ProcessReq(xreq);
996  reset();
997  if (!r) return 1; // All went fine, response sent
998  if (r < 0) return -1; // There was a hard error... close the connection
999 
1000  return 1; // There was an error and a response was sent
1001  }
1002  }
1003 
1004  //
1005  // Here we process the request locally
1006  //
1007 
1008  switch (request) {
1009  case XrdHttpReq::rtUnset:
1010  return -1;
1011  case XrdHttpReq::rtUnknown:
1012  case XrdHttpReq::rtMalformed: {
1013  generateWebdavErrMsg();
1014  prot->SendSimpleResp(400, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1015  reset();
1016  return -1;
1017  }
1018  case XrdHttpReq::rtHEAD:
1019  {
1020  if (reqstate == 0) {
1021  // Always start with Stat; in the case of a checksum request, we'll have a follow-up query
1022  if (prot->doStat((char *) resourceplusopaque.c_str())) {
1023  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1024  return -1;
1025  }
1026  return 0;
1027  } else {
1028  // Note that doChksum requires that the memory stays alive until the callback is invoked.
1029  int prepareCksum = prepareChecksumQuery(m_req_cksum, m_resource_with_digest);
1030  if(prepareCksum < 0) {
1031  return -1;
1032  }
1033  if (prot->doChksum(m_resource_with_digest) < 0) {
1034  // In this case, the Want-Digest header was set and PostProcess gave the go-ahead to do a checksum.
1035  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to create initial checksum request.", 0, false);
1036  return -1;
1037  }
1038  return 1;
1039  }
1040  }
1041  case XrdHttpReq::rtGET:
1042  {
1043  int retval = keepalive ? 1 : -1; // reset() clears keepalive
1044 
1045  if (resource.beginswith("/static/")) {
1046 
1047  // This is a request for a /static resource
1048  // If we have to use the embedded ones then we return the ones in memory as constants
1049 
1050  // The sysadmin can always redirect the request to another host that
1051  // contains his static resources
1052 
1053  // We also allow xrootd to preread from the local disk all the files
1054  // that have to be served as static resources.
1055 
1056  if (prot->embeddedstatic) {
1057 
1058  // Default case: the icon and the css of the HTML rendering of XrdHttp
1059  if (resource == "/static/css/xrdhttp.css") {
1060  prot->SendSimpleResp(200, NULL, NULL, (char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len, keepalive);
1061  reset();
1062  return retval;
1063  }
1064  if (resource == "/static/icons/xrdhttp.ico") {
1065  prot->SendSimpleResp(200, NULL, NULL, (char *) favicon_ico, favicon_ico_len, keepalive);
1066  reset();
1067  return retval;
1068  }
1069 
1070  }
1071 
1072  // If we are here then none of the embedded resources match (or they are disabled)
1073  // We may have to redirect to a host that is supposed to serve the static resources
1074  if (prot->staticredir) {
1075 
1076  XrdOucString s = "Location: ";
1077  s.append(prot->staticredir);
1078 
1079  if (s.endswith('/'))
1080  s.erasefromend(1);
1081 
1082  s.append(encode_str(std::string(resource.c_str())).c_str());
1083  appendOpaque(s, 0, 0, 0);
1084 
1085  prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1086  return -1;
1087 
1088 
1089  } else {
1090 
1091  // We lookup the requested path in a hash containing the preread files
1092  if (prot->staticpreload) {
1094  if (mydata) {
1095  prot->SendSimpleResp(200, NULL, NULL, (char *) mydata->data, mydata->len, keepalive);
1096  reset();
1097  return retval;
1098  }
1099  }
1100 
1101  }
1102 
1103 
1104  }
1105 
1106  // The reqstate parameter basically moves us through a simple state machine.
1107  // To optimize things, we start off by opening the file; if it turns out to be a directory, then
1108  // we close the file handle and switch to doing a HTML-based rendering of the directory. This
1109  // avoids needing to always to do "stat" first to determine the next step (since the file-open also
1110  // does a "stat").
1111  // - 0: Perform an open on the resource
1112  // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
1113  // - 2: Perform a close (for dirlist only)
1114  // - 3: Perform a dirlist.
1115  // - 4+: Reads from file; if at end, perform a close.
1116  switch (reqstate) {
1117  case 0: // Open the path for reading.
1118  {
1119  memset(&xrdreq, 0, sizeof (ClientRequest));
1120  xrdreq.open.requestid = htons(kXR_open);
1121  l = resourceplusopaque.length() + 1;
1122  xrdreq.open.dlen = htonl(l);
1123  xrdreq.open.mode = 0;
1125 
1126  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1127  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1128  return -1;
1129  }
1130 
1131  // Prepare to chunk up the request
1132  writtenbytes = 0;
1133 
1134  // We want to be invoked again after this request is finished
1135  return 0;
1136  }
1137  case 1: // Checksum request
1138  if (!(fileflags & kXR_isDir) && (!m_want_digest.empty() || !m_want_repr_digest.empty())) {
1139  // In this case, the Want-Digest or then Want-Repr-Digest header was set.
1140  int prepareCksum = prepareChecksumQuery(m_req_cksum, m_resource_with_digest);
1141  if(prepareCksum < 0) {
1142  return -1;
1143  }
1144  if (prot->doChksum(m_resource_with_digest) < 0) {
1145  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to start internal checksum request to satisfy Want-Digest or Want-Repr-Digest header.", 0, false);
1146  return -1;
1147  }
1148  return 0;
1149  } else {
1150  TRACEI(DEBUG, "No checksum requested; skipping to request state 2");
1151  reqstate += 1;
1152  }
1153  // fallthrough
1154  case 2: // Close file handle for directory
1155  if ((fileflags & kXR_isDir) && fopened) {
1156  memset(&xrdreq, 0, sizeof (ClientRequest));
1157  xrdreq.close.requestid = htons(kXR_close);
1158  memcpy(xrdreq.close.fhandle, fhandle, 4);
1159 
1160  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1161  generateWebdavErrMsg();
1162  return sendFooterError("Could not run close request on the bridge");
1163  }
1164  return 0;
1165  } else {
1166  reqstate += 1;
1167  }
1168  // fallthrough
1169  case 3: // List directory
1170  if (fileflags & kXR_isDir) {
1171  if (prot->listdeny) {
1172  // Return 403 as the administrator forbid the directory listing
1173  prot->SendSimpleResp(403, NULL, NULL, (char *) "Listings are disabled.", 0, false);
1174  return -1;
1175  }
1176 
1177  if (prot->listredir) {
1178  XrdOucString s = "Location: ";
1179  s.append(prot->listredir);
1180 
1181  if (s.endswith('/'))
1182  s.erasefromend(1);
1183 
1184  s.append(encode_str(std::string(resource.c_str())).c_str());
1185  appendOpaque(s, 0, 0, 0);
1186 
1187  prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1188  return -1;
1189  }
1190 
1191  std::string res;
1192  res = resourceplusopaque.c_str();
1193 
1194  // --------- DIRLIST
1195  memset(&xrdreq, 0, sizeof (ClientRequest));
1198  l = res.length() + 1;
1199  xrdreq.dirlist.dlen = htonl(l);
1200 
1201  if (!prot->Bridge->Run((char *) &xrdreq, (char *) res.c_str(), l)) {
1202  generateWebdavErrMsg();
1203  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1204  sendFooterError("Could not run listing request on the bridge");
1205  return -1;
1206  }
1207 
1208  // We don't want to be invoked again after this request is finished
1209  return 1;
1210  }
1211  else {
1212  reqstate += 1;
1213  }
1214  // fallthrough
1215  case 4:
1216  {
1217  auto retval = ReturnGetHeaders();
1218  if (retval) {
1219  return retval;
1220  }
1221  }
1222  // fallthrough
1223  default: // Read() or Close(); reqstate is 4+
1224  {
1225  const XrdHttpIOList &readChunkList = readRangeHandler.NextReadList();
1226 
1227  // Close() if we have finished, otherwise read the next chunk
1228 
1229  // --------- CLOSE
1230  if ( closeAfterError || readChunkList.empty() )
1231  {
1232 
1233  memset(&xrdreq, 0, sizeof (ClientRequest));
1234  xrdreq.close.requestid = htons(kXR_close);
1235  memcpy(xrdreq.close.fhandle, fhandle, 4);
1236 
1237  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1238  TRACEI(REQ, " Failed to run close request on the bridge.");
1239  // Note: we have already completed the request and sent the data to the client.
1240  // Hence, there's no need to send an error. However, since the bridge is potentially
1241  // in a bad state, we close the TCP socket to force the client to reconnect.
1242  return -1;
1243  }
1244 
1245  // We have finished
1246  readClosing = true;
1247  return 1;
1248 
1249  }
1250  // --------- READ or READV
1251 
1252  if ( readChunkList.size() == 1 ) {
1253  // Use a read request for single range
1254 
1255  long l;
1256  long long offs;
1257 
1258  // --------- READ
1259  memset(&xrdreq, 0, sizeof (xrdreq));
1260  xrdreq.read.requestid = htons(kXR_read);
1261  memcpy(xrdreq.read.fhandle, fhandle, 4);
1262  xrdreq.read.dlen = 0;
1263 
1264  offs = readChunkList[0].offset;
1265  l = readChunkList[0].size;
1266 
1267  xrdreq.read.offset = htonll(offs);
1268  xrdreq.read.rlen = htonl(l);
1269 
1270  // If we are using HTTPS or if the client requested trailers, or if the
1271  // read concerns a multirange reponse, disable sendfile
1272  // (in the latter two cases, the extra framing is only done in PostProcessHTTPReq)
1273  if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1275  if (!prot->Bridge->setSF((kXR_char *) fhandle, false)) {
1276  TRACE(REQ, " XrdBridge::SetSF(false) failed.");
1277 
1278  }
1279  }
1280 
1281 
1282 
1283  if (l <= 0) {
1284  if (l < 0) {
1285  TRACE(ALL, " Data sizes mismatch.");
1286  return -1;
1287  }
1288  else {
1289  TRACE(ALL, " No more bytes to send.");
1290  reset();
1291  return 1;
1292  }
1293  }
1294 
1295  if ((offs >= filesize) || (offs+l > filesize)) {
1296  httpStatusCode = 416;
1297  httpErrorBody = "Range Not Satisfiable";
1298  std::stringstream ss;
1299  ss << "Requested range " << l << "@" << offs << " is past the end of file (" << filesize << ")";
1300  return sendFooterError(ss.str());
1301  }
1302 
1303  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1304  generateWebdavErrMsg();
1305  return sendFooterError("Could not run read request on the bridge");
1306  }
1307  } else {
1308  // --------- READV
1309 
1310  length = ReqReadV(readChunkList);
1311 
1312  if (!prot->Bridge->Run((char *) &xrdreq, (char *) &ralist[0], length)) {
1313  generateWebdavErrMsg();
1314  return sendFooterError("Could not run ReadV request on the bridge");
1315  }
1316 
1317  }
1318 
1319  // We want to be invoked again after this request is finished
1320  return 0;
1321  } // case 3+
1322 
1323  } // switch (reqstate)
1324 
1325 
1326  } // case XrdHttpReq::rtGET
1327 
1328  case XrdHttpReq::rtPUT:
1329  {
1330  //if (prot->ishttps) {
1331  //prot->SendSimpleResp(501, NULL, NULL, (char *) "HTTPS not supported yet for direct writing. Sorry.", 0);
1332  //return -1;
1333  //}
1334 
1335  if (!fopened) {
1336 
1337  // --------- OPEN for write!
1338  memset(&xrdreq, 0, sizeof (ClientRequest));
1339  xrdreq.open.requestid = htons(kXR_open);
1340  l = resourceplusopaque.length() + 1;
1341  xrdreq.open.dlen = htonl(l);
1342  xrdreq.open.mode = htons(kXR_ur | kXR_uw | kXR_gw | kXR_gr | kXR_or);
1343  if (! XrdHttpProtocol::usingEC)
1345  else
1347 
1348  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1349  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, keepalive);
1350  return -1;
1351  }
1352 
1353 
1354  // We want to be invoked again after this request is finished
1355  // Only if there is data to fetch from the socket or there will
1356  // never be more data
1357  if (prot->BuffUsed() > 0 || (length == 0 && !sendcontinue))
1358  return 0;
1359 
1360  return 1;
1361 
1362  } else {
1363 
1364  if (m_transfer_encoding_chunked) {
1365  if (m_current_chunk_size == m_current_chunk_offset) {
1366  // Chunk has been consumed; we now must process the CRLF.
1367  // Note that we don't support trailer headers.
1368  if (prot->BuffUsed() < 2) return 1;
1369  if (prot->myBuffStart[0] != '\r' || prot->myBuffStart[1] != '\n') {
1370  prot->SendSimpleResp(400, NULL, NULL, (char *) "Invalid trailing chunk encoding.", 0, keepalive);
1371  return -1;
1372  }
1373  prot->BuffConsume(2);
1374  if (m_current_chunk_size == 0) {
1375  // All data has been sent. Turn off chunk processing and
1376  // set the bytes written and length appropriately; on next callback,
1377  // we will hit the close() block below.
1378  m_transfer_encoding_chunked = false;
1379  length = writtenbytes;
1380  return ProcessHTTPReq();
1381  }
1382  m_current_chunk_size = -1;
1383  m_current_chunk_offset = 0;
1384  // If there is more data, we try to process the next chunk; otherwise, return
1385  if (!prot->BuffUsed()) return 1;
1386  }
1387  if (-1 == m_current_chunk_size) {
1388 
1389  // Parse out the next chunk size.
1390  long long idx = 0;
1391  bool found_newline = false;
1392  // Set a maximum size of chunk we will allow
1393  // Nginx sets this to "NGX_MAX_OFF_T_VALUE", which is 9223372036854775807 (a some crazy number)
1394  // We set it to 1TB, which is 1099511627776
1395  // This is to prevent a malicious client from sending a very large chunk size
1396  // or a malformed chunk request.
1397  // 1TB in base-16 is 0x40000000000, so only allow 11 characters, plus the CRLF
1398  long long max_chunk_size_chars = std::min(static_cast<long long>(prot->BuffUsed()), static_cast<long long>(13));
1399  for (; idx < max_chunk_size_chars; idx++) {
1400  if (prot->myBuffStart[idx] == '\n') {
1401  found_newline = true;
1402  break;
1403  }
1404  }
1405  // If we found a new line, but it is the first character in the buffer (no chunk length)
1406  // or if the previous character is not a CR.
1407  if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] != '\r')) {
1408  prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1409  TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1410  return -1;
1411  }
1412  if (found_newline) {
1413  char *endptr = NULL;
1414  std::string line_contents(prot->myBuffStart, idx);
1415  long long chunk_contents = strtoll(line_contents.c_str(), &endptr, 16);
1416  // Chunk sizes can be followed by trailer information or CRLF
1417  if (*endptr != ';' && *endptr != '\r') {
1418  prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1419  TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1420  return -1;
1421  }
1422  m_current_chunk_size = chunk_contents;
1423  m_current_chunk_offset = 0;
1424  prot->BuffConsume(idx + 1);
1425  TRACE(REQ, "XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size << " bytes");
1426  } else {
1427  // Need more data!
1428  return 1;
1429  }
1430  }
1431 
1432  if (m_current_chunk_size == 0) {
1433  // All data has been sent. Invoke this routine again immediately to process CRLF
1434  return ProcessHTTPReq();
1435  } else {
1436  // At this point, we have a chunk size defined and should consume payload data
1437  memset(&xrdreq, 0, sizeof (xrdreq));
1438  xrdreq.write.requestid = htons(kXR_write);
1439  memcpy(xrdreq.write.fhandle, fhandle, 4);
1440 
1441  long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1442  long long bytes_to_write = std::min(static_cast<long long>(prot->BuffUsed()),
1443  chunk_bytes_remaining);
1444 
1445  xrdreq.write.offset = htonll(writtenbytes);
1446  xrdreq.write.dlen = htonl(bytes_to_write);
1447 
1448  TRACEI(REQ, "XrdHTTP PUT: Writing chunk of size " << bytes_to_write << " starting with '" << *(prot->myBuffStart) << "'" << " with " << chunk_bytes_remaining << " bytes remaining in the chunk");
1449  if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_write)) {
1450  generateWebdavErrMsg();
1451  return sendFooterError("Could not run write request on the bridge");
1452  }
1453  // If there are more bytes in the buffer, then immediately call us after the
1454  // write is finished; otherwise, wait for data.
1455  return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1456  }
1457  } else if (writtenbytes < length) {
1458 
1459 
1460  // --------- WRITE
1461  memset(&xrdreq, 0, sizeof (xrdreq));
1462  xrdreq.write.requestid = htons(kXR_write);
1463  memcpy(xrdreq.write.fhandle, fhandle, 4);
1464 
1465  long long bytes_to_read = std::min(static_cast<long long>(prot->BuffUsed()),
1466  length - writtenbytes);
1467 
1468  xrdreq.write.offset = htonll(writtenbytes);
1469  xrdreq.write.dlen = htonl(bytes_to_read);
1470 
1471  TRACEI(REQ, "Writing " << bytes_to_read);
1472  if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_read)) {
1473  generateWebdavErrMsg();
1474  return sendFooterError("Could not run write request on the bridge");
1475  }
1476 
1477  if (writtenbytes + prot->BuffUsed() >= length)
1478  // Trigger an immediate recall after this request has finished
1479  return 0;
1480  else
1481  // We want to be invoked again after this request is finished
1482  // only if there is pending data
1483  return 1;
1484 
1485 
1486 
1487  } else {
1488 
1489  // --------- CLOSE
1490  memset(&xrdreq, 0, sizeof (ClientRequest));
1491  xrdreq.close.requestid = htons(kXR_close);
1492  memcpy(xrdreq.close.fhandle, fhandle, 4);
1493 
1494 
1495  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1496  generateWebdavErrMsg();
1497  return sendFooterError("Could not run close request on the bridge");
1498  }
1499 
1500  // We have finished
1501  return 1;
1502 
1503  }
1504 
1505  }
1506 
1507  break;
1508 
1509  }
1510  case XrdHttpReq::rtOPTIONS:
1511  {
1512  prot->SendSimpleResp(200, NULL, (char *) "DAV: 1\r\nDAV: <http://apache.org/dav/propset/fs/1>\r\nAllow: HEAD,GET,PUT,PROPFIND,DELETE,OPTIONS", NULL, 0, keepalive);
1513  bool ret_keepalive = keepalive; // reset() clears keepalive
1514  reset();
1515  return ret_keepalive ? 1 : -1;
1516  }
1517  case XrdHttpReq::rtDELETE:
1518  {
1519 
1520 
1521  switch (reqstate) {
1522 
1523  case 0: // Stat()
1524  {
1525 
1526 
1527  // --------- STAT is always the first step
1528  memset(&xrdreq, 0, sizeof (ClientRequest));
1529  xrdreq.stat.requestid = htons(kXR_stat);
1530  std::string s = resourceplusopaque.c_str();
1531 
1532 
1533  l = resourceplusopaque.length() + 1;
1534  xrdreq.stat.dlen = htonl(l);
1535 
1536  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1537  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1538  return -1;
1539  }
1540 
1541  // We need to be invoked again to complete the request
1542  return 0;
1543  }
1544  default:
1545 
1546  if (fileflags & kXR_isDir) {
1547  // --------- RMDIR
1548  memset(&xrdreq, 0, sizeof (ClientRequest));
1549  xrdreq.rmdir.requestid = htons(kXR_rmdir);
1550 
1551  std::string s = resourceplusopaque.c_str();
1552 
1553  l = s.length() + 1;
1554  xrdreq.rmdir.dlen = htonl(l);
1555 
1556  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1557  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rmdir request.", 0, false);
1558  return -1;
1559  }
1560  } else {
1561  // --------- DELETE
1562  memset(&xrdreq, 0, sizeof (ClientRequest));
1563  xrdreq.rm.requestid = htons(kXR_rm);
1564 
1565  std::string s = resourceplusopaque.c_str();
1566 
1567  l = s.length() + 1;
1568  xrdreq.rm.dlen = htonl(l);
1569 
1570  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1571  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rm request.", 0, false);
1572  return -1;
1573  }
1574  }
1575 
1576 
1577  // We don't want to be invoked again after this request is finished
1578  return 1;
1579 
1580  }
1581 
1582 
1583 
1584  }
1585  case XrdHttpReq::rtPATCH:
1586  {
1587  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported yet.", 0, false);
1588 
1589  return -1;
1590  }
1592  {
1593 
1594 
1595 
1596  switch (reqstate) {
1597 
1598  case 0: // Stat() and add the current item to the list of the things to send
1599  {
1600 
1601  if (length > 0) {
1602  TRACE(REQ, "Reading request body " << length << " bytes.");
1603  char *p = 0;
1604  // We have to specifically read all the request body
1605 
1606  if (prot->BuffgetData(length, &p, true) < length) {
1607  prot->SendSimpleResp(501, NULL, NULL, (char *) "Error in getting the PROPFIND request body.", 0, false);
1608  return -1;
1609  }
1610 
1611  if ((depth > 1) || (depth < 0)) {
1612  prot->SendSimpleResp(501, NULL, NULL, (char *) "Invalid depth value.", 0, false);
1613  return -1;
1614  }
1615 
1616 
1617  parseBody(p, length);
1618  }
1619 
1620 
1621  // --------- STAT is always the first step
1622  memset(&xrdreq, 0, sizeof (ClientRequest));
1623  xrdreq.stat.requestid = htons(kXR_stat);
1624  std::string s = resourceplusopaque.c_str();
1625 
1626 
1627  l = resourceplusopaque.length() + 1;
1628  xrdreq.stat.dlen = htonl(l);
1629 
1630  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1631  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1632  return -1;
1633  }
1634 
1635 
1636  if (depth == 0) {
1637  // We don't need to be invoked again
1638  return 1;
1639  } else
1640  // We need to be invoked again to complete the request
1641  return 0;
1642 
1643 
1644 
1645  break;
1646  }
1647 
1648  default: // Dirlist()
1649  {
1650 
1651  // --------- DIRLIST
1652  memset(&xrdreq, 0, sizeof (ClientRequest));
1654 
1655  std::string s = resourceplusopaque.c_str();
1657  //s += "?xrd.dirstat=1";
1658 
1659  l = s.length() + 1;
1660  xrdreq.dirlist.dlen = htonl(l);
1661 
1662  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1663  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1664  return -1;
1665  }
1666 
1667  // We don't want to be invoked again after this request is finished
1668  return 1;
1669  }
1670  }
1671 
1672 
1673  break;
1674  }
1675  case XrdHttpReq::rtMKCOL:
1676  {
1677 
1678  // --------- MKDIR
1679  memset(&xrdreq, 0, sizeof (ClientRequest));
1680  xrdreq.mkdir.requestid = htons(kXR_mkdir);
1681 
1682  std::string s = resourceplusopaque.c_str();
1684 
1685  l = s.length() + 1;
1686  xrdreq.mkdir.dlen = htonl(l);
1687 
1688  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1689  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1690  return -1;
1691  }
1692 
1693  // We don't want to be invoked again after this request is finished
1694  return 1;
1695  }
1696  case XrdHttpReq::rtMOVE:
1697  {
1698  // Skip the protocol part of destination URL
1699  size_t skip = destination.find("://");
1700  skip = (skip == std::string::npos) ? 0 : skip + 3;
1701 
1702  // If we have a manager role, enforce source and destination are on the same host
1703  if (prot->myRole == kXR_isManager && destination.compare(skip, host.size(), host) != 0) {
1704  prot->SendSimpleResp(501, NULL, NULL, (char *) "Only in-place renaming is supported for MOVE.", 0, false);
1705  return -1;
1706  }
1707 
1708  // If needed, append opaque info from source onto destination
1709  int pos = resourceplusopaque.find("?");
1710  if (pos != STR_NPOS) {
1711  destination.append((destination.find("?") == std::string::npos) ? "?" : "&");
1712  destination.append(resourceplusopaque.c_str() + pos + 1);
1713  }
1714 
1715  size_t path_pos = destination.find('/', skip + 1);
1716 
1717  if (path_pos == std::string::npos) {
1718  prot->SendSimpleResp(400, NULL, NULL, (char *) "Cannot determine destination path", 0, false);
1719  return -1;
1720  }
1721 
1722  // Construct args to kXR_mv request (i.e. <src> + " " + <dst>)
1723  std::string mv_args = std::string(resourceplusopaque.c_str()) + " " + destination.substr(path_pos);
1724 
1725  l = mv_args.length() + 1;
1726 
1727  // Prepare and run kXR_mv request
1728  memset(&xrdreq, 0, sizeof (ClientRequest));
1729  xrdreq.mv.requestid = htons(kXR_mv);
1731  xrdreq.mv.dlen = htonl(l);
1732 
1733  if (!prot->Bridge->Run((char *) &xrdreq, (char *) mv_args.c_str(), l)) {
1734  prot->SendSimpleResp(500, NULL, NULL, (char *) "Could not run request.", 0, false);
1735  return -1;
1736  }
1737 
1738  // We don't want to be invoked again after this request is finished
1739  return 1;
1740  }
1741  default:
1742  {
1743  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported.", 0, false);
1744  return -1;
1745  }
1746 
1747  }
1748 
1749  return 1;
1750 }
kXR_unt16 requestid
Definition: XProtocol.hh:511
kXR_char options[1]
Definition: XProtocol.hh:278
kXR_int16 arg1len
Definition: XProtocol.hh:460
#define kXR_isManager
Definition: XProtocol.hh:1198
kXR_unt16 requestid
Definition: XProtocol.hh:847
struct ClientCloseRequest close
Definition: XProtocol.hh:893
kXR_char fhandle[4]
Definition: XProtocol.hh:848
struct ClientMkdirRequest mkdir
Definition: XProtocol.hh:900
kXR_int32 dlen
Definition: XProtocol.hh:461
kXR_int64 offset
Definition: XProtocol.hh:682
kXR_unt16 requestid
Definition: XProtocol.hh:680
kXR_unt16 options
Definition: XProtocol.hh:513
struct ClientDirlistRequest dirlist
Definition: XProtocol.hh:894
kXR_unt16 requestid
Definition: XProtocol.hh:257
@ kXR_open_wrto
Definition: XProtocol.hh:499
@ kXR_delete
Definition: XProtocol.hh:483
@ kXR_open_read
Definition: XProtocol.hh:486
@ kXR_mkpath
Definition: XProtocol.hh:490
@ kXR_seqio
Definition: XProtocol.hh:498
@ kXR_new
Definition: XProtocol.hh:485
@ kXR_retstat
Definition: XProtocol.hh:493
struct ClientOpenRequest open
Definition: XProtocol.hh:902
@ kXR_dstat
Definition: XProtocol.hh:269
kXR_unt16 requestid
Definition: XProtocol.hh:458
kXR_char fhandle[4]
Definition: XProtocol.hh:681
kXR_char fhandle[4]
Definition: XProtocol.hh:258
@ kXR_read
Definition: XProtocol.hh:126
@ kXR_mkdir
Definition: XProtocol.hh:121
@ kXR_dirlist
Definition: XProtocol.hh:117
@ kXR_rm
Definition: XProtocol.hh:127
@ kXR_write
Definition: XProtocol.hh:132
@ kXR_rmdir
Definition: XProtocol.hh:128
@ kXR_mv
Definition: XProtocol.hh:122
@ kXR_stat
Definition: XProtocol.hh:130
@ kXR_close
Definition: XProtocol.hh:116
kXR_int32 dlen
Definition: XProtocol.hh:735
struct ClientRmRequest rm
Definition: XProtocol.hh:911
kXR_int32 dlen
Definition: XProtocol.hh:684
struct ClientReadRequest read
Definition: XProtocol.hh:909
struct ClientMvRequest mv
Definition: XProtocol.hh:901
kXR_unt16 requestid
Definition: XProtocol.hh:808
kXR_int32 dlen
Definition: XProtocol.hh:517
struct ClientRmdirRequest rmdir
Definition: XProtocol.hh:912
kXR_unt16 requestid
Definition: XProtocol.hh:445
kXR_unt16 mode
Definition: XProtocol.hh:512
kXR_char options[1]
Definition: XProtocol.hh:446
kXR_unt16 requestid
Definition: XProtocol.hh:733
@ kXR_mkdirpath
Definition: XProtocol.hh:440
struct ClientStatRequest stat
Definition: XProtocol.hh:915
kXR_int64 offset
Definition: XProtocol.hh:849
struct ClientWriteRequest write
Definition: XProtocol.hh:918
kXR_int32 dlen
Definition: XProtocol.hh:813
kXR_int32 rlen
Definition: XProtocol.hh:683
@ kXR_gw
Definition: XProtocol.hh:474
@ kXR_ur
Definition: XProtocol.hh:470
@ kXR_uw
Definition: XProtocol.hh:471
@ kXR_gr
Definition: XProtocol.hh:473
@ kXR_or
Definition: XProtocol.hh:476
@ kXR_isDir
Definition: XProtocol.hh:1263
kXR_unt16 requestid
Definition: XProtocol.hh:744
int kXR_int32
Definition: XPtypes.hh:89
unsigned char kXR_char
Definition: XPtypes.hh:65
#define DEBUG(x)
Definition: XrdBwmTrace.hh:54
std::vector< XrdOucIOVec2 > XrdHttpIOList
std::string obfuscateAuth(const std::string &input)
#define STR_NPOS
#define TRACE_DEBUG
Definition: XrdTrace.hh:36
#define TRACING(x)
Definition: XrdTrace.hh:70
#define TRACEI(act, x)
Definition: XrdTrace.hh:66
virtual int ProcessReq(XrdHttpExtReq &)=0
static kXR_int32 myRole
Our role.
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
int doStat(char *fname)
Perform a Stat request.
static char * listredir
Url to redirect to in the case a listing is requested.
static bool listdeny
If true, any form of listing is denied.
static bool embeddedstatic
If true, use the embedded css and icons.
const XrdHttpIOList & NextReadList()
return XrdHttpIOList for sending to read or readv
size_t getMaxRanges() const
return the maximum number of ranges that may be requested
bool isSingleRange()
indicates a single range (implied whole file, or single range) or empty file
int reqstate
State machine to talk to the bridge.
Definition: XrdHttpReq.hh:361
char fhandle[4]
Definition: XrdHttpReq.hh:354
int ReqReadV(const XrdHttpIOList &cl)
Prepare the buffers for sending a readv request.
Definition: XrdHttpReq.cc:451
int parseBody(char *body, long long len)
Parse the body of a request, assuming that it's XML and that it's entirely in memory.
Definition: XrdHttpReq.cc:96
std::vector< readahead_list > ralist
Definition: XrdHttpReq.hh:247
XrdOucString resource
The resource specified by the request, stripped of opaque data.
Definition: XrdHttpReq.hh:277
int ProcessHTTPReq()
Definition: XrdHttpReq.cc:940
long fileflags
Definition: XrdHttpReq.hh:351
XrdOucString resourceplusopaque
The resource specified by the request, including all the opaque data.
Definition: XrdHttpReq.hh:281
std::string host
The host field specified in the req.
Definition: XrdHttpReq.hh:302
XrdHttpChecksumHandler::XrdHttpChecksumRawPtr m_req_cksum
The checksum that was ran for this request.
Definition: XrdHttpReq.hh:310
bool m_appended_hdr2cgistr
Definition: XrdHttpReq.hh:321
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
Definition: XrdHttpReq.cc:711
bool m_appended_asize
Track whether we already appended the oss.asize argument for PUTs.
Definition: XrdHttpReq.hh:323
XrdOucString m_resource_with_digest
Definition: XrdHttpReq.hh:315
long long filesize
Definition: XrdHttpReq.hh:350
bool readClosing
Definition: XrdHttpReq.hh:289
std::chrono::steady_clock::time_point startTime
Definition: XrdHttpReq.hh:370
void Put(const char *varname, const char *value)
Definition: XrdOucEnv.hh:85
const char * c_str() const
int erasefromend(int sz=0)
bool endswith(char c)
bool beginswith(char c)
int find(const char c, int start=0, bool forward=1)
int length() const
void append(const int i)
virtual int setSF(kXR_char *fhandle, bool seton=false)=0
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0

References XrdOucString::append(), appendOpaque(), ClientMvRequest::arg1len, XrdOucString::beginswith(), XrdHttpProtocol::Bridge, XrdOucString::c_str(), ClientRequest::close, closeAfterError, XrdHttpProtocol::StaticPreloadInfo::data, DEBUG, depth, destination, ClientRequest::dirlist, ClientDirlistRequest::dlen, ClientMkdirRequest::dlen, ClientMvRequest::dlen, ClientOpenRequest::dlen, ClientReadRequest::dlen, ClientRmRequest::dlen, ClientRmdirRequest::dlen, ClientStatRequest::dlen, ClientWriteRequest::dlen, XrdHttpProtocol::doChksum(), XrdHttpProtocol::doStat(), XrdHttpProtocol::embeddedstatic, encode_opaque(), encode_str(), XrdOucString::endswith(), XrdOucString::erasefromend(), ClientCloseRequest::fhandle, ClientReadRequest::fhandle, ClientWriteRequest::fhandle, fhandle, fileflags, filesize, XrdOucString::find(), fopened, XrdHttpReadRangeHandler::getMaxRanges(), hdr2cgistr, host, XrdHttpReadRangeHandler::isSingleRange(), keepalive, kXR_close, kXR_delete, kXR_dirlist, kXR_dstat, kXR_gr, kXR_gw, kXR_isDir, kXR_isManager, kXR_mkdir, kXR_mkdirpath, kXR_mkpath, kXR_mv, kXR_new, kXR_open, kXR_open_read, kXR_open_wrto, kXR_or, kXR_read, kXR_retstat, kXR_rm, kXR_rmdir, kXR_seqio, kXR_stat, kXR_ur, kXR_uw, kXR_write, XrdHttpProtocol::StaticPreloadInfo::len, length, XrdOucString::length(), XrdHttpProtocol::listdeny, XrdHttpProtocol::listredir, m_appended_asize, m_appended_hdr2cgistr, m_req_cksum, m_resource_with_digest, m_want_digest, m_want_repr_digest, ClientRequest::mkdir, ClientOpenRequest::mode, ClientRequest::mv, XrdHttpProtocol::myRole, XrdHttpReadRangeHandler::NextReadList(), obfuscateAuth(), ClientReadRequest::offset, ClientWriteRequest::offset, opaque, ClientRequest::open, ClientDirlistRequest::options, ClientMkdirRequest::options, ClientOpenRequest::options, parseBody(), XrdHttpExtHandler::ProcessReq(), XrdOucEnv::Put(), ralist, ClientRequest::read, readClosing, readRangeHandler, ReqReadV(), reqstate, request, ClientCloseRequest::requestid, ClientDirlistRequest::requestid, ClientMkdirRequest::requestid, ClientMvRequest::requestid, ClientOpenRequest::requestid, ClientReadRequest::requestid, ClientRmRequest::requestid, ClientRmdirRequest::requestid, ClientStatRequest::requestid, ClientWriteRequest::requestid, reset(), resource, resourceplusopaque, ClientReadRequest::rlen, ClientRequest::rm, ClientRequest::rmdir, rtDELETE, rtGET, rtHEAD, rtMalformed, rtMKCOL, rtMOVE, rtOPTIONS, rtPATCH, rtPROPFIND, rtPUT, rtUnknown, rtUnset, XrdXrootd::Bridge::Run(), sendcontinue, XrdXrootd::Bridge::setSF(), startTime, ClientRequest::stat, XrdHttpProtocol::staticpreload, XrdHttpProtocol::staticredir, STR_NPOS, TRACE, TRACE_DEBUG, TRACEI, TRACING, ClientRequest::write, writtenbytes, and xrdreq.

Referenced by XrdHttpProtocol::Process().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ Redir()

bool XrdHttpReq::Redir ( XrdXrootd::Bridge::Context info,
int  port,
const char *  hname 
)
virtual

Redirect the client to another host:port.

The Redir() method is called when the client must be redirected to another host.

Parameters
infothe context associated with the result.
portthe port number in host byte format.
hnamethe DNS name of the host or IP address is IPV4 or IPV6 format (i.e. "n.n.n.n" or "[ipv6_addr]").
Returns
true continue normal processing. false terminate the bridge and close the link.
Parameters
infothe result context
portthe port number
hnamethe destination host

Implements XrdXrootd::Bridge::Result.

Definition at line 599 of file XrdHttpReq.cc.

602  {
603 
604 
605 
606  char buf[512];
607  char hash[512];
608  hash[0] = '\0';
609 
610  bool invalid_host = false;
611 
612  if (!hname) {
613  invalid_host = true;
614  } else {
615  for (const char *c = hname; *c; ++c)
616  if (*c == '\r' || *c == '\n')
617  invalid_host = true;
618  }
619 
620  if (invalid_host) {
621  prot->SendSimpleResp(502, nullptr, nullptr, "Invalid redirect host", 0, false);
622  return keepalive;
623  }
624 
625  if (prot->isdesthttps)
626  redirdest = "Location: https://";
627  else
628  redirdest = "Location: http://";
629 
630  // port < 0 signals switch to full URL
631  if (port < 0)
632  {
633  if (strncmp(hname, "file://", 7) == 0)
634  {
635  TRACE(REQ, " XrdHttpReq::Redir Switching to file:// ");
636  redirdest = "Location: "; // "file://" already contained in hname
637  }
638  }
639  // Beware, certain Ofs implementations (e.g. EOS) add opaque data directly to the host name
640  // This must be correctly treated here and appended to the opaque info
641  // that we may already have
642  char *pp = strchr((char *)hname, '?');
643  char *vardata = 0;
644  if (pp) {
645  *pp = '\0';
646  redirdest += hname;
647  vardata = pp+1;
648  int varlen = strlen(vardata);
649 
650  //Now extract the remaining, vardata points to it
651  while(*vardata == '&' && varlen) {vardata++; varlen--;}
652 
653  // Put the question mark back where it was
654  *pp = '?';
655  }
656  else
657  redirdest += hname;
658 
659  if (port > 0) {
660  sprintf(buf, ":%d", port);
661  redirdest += buf;
662  }
663 
664  redirdest += encode_str(resource.c_str()).c_str();
665 
666  // Here we put back the opaque info, if any
667  if (vardata) {
668  redirdest += "?&";
669  redirdest += encode_opaque(vardata).c_str();
670  }
671 
672  // Shall we put also the opaque data of the request? Maybe not
673  //int l;
674  //if (opaque && opaque->Env(l))
675  // redirdest += opaque->Env(l);
676 
677 
678  time_t timenow = 0;
679  if (!prot->isdesthttps && prot->ishttps) {
680  // If the destination is not https, then we suppose that it
681  // will need this token to fill its authorization info
682  timenow = time(0);
683  calcHashes(hash, this->resource.c_str(), (kXR_int16) request,
684  &prot->SecEntity,
685  timenow,
686  prot->secretkey);
687  }
688 
689  if (hash[0]) {
690  appendOpaque(redirdest, &prot->SecEntity, hash, timenow);
691  } else
692  appendOpaque(redirdest, 0, 0, 0);
693 
694  if (!prot->strp_cgi_params.empty()) {
695  stripCgi(redirdest, prot->strp_cgi_params); /* appendOpaque() may have added credentials */
696  }
697 
698  TRACE(REQ, " XrdHttpReq::Redir Redirecting to " << redirdest.c_str());
699 
700  if (request != rtGET)
701  prot->SendSimpleResp(307, NULL, (char *) redirdest.c_str(), 0, 0, keepalive);
702  else
703  prot->SendSimpleResp(302, NULL, (char *) redirdest.c_str(), 0, 0, keepalive);
704 
705  bool ret_keepalive = keepalive; // reset() clears keepalive
706  reset();
707  return ret_keepalive;
708 };
short kXR_int16
Definition: XPtypes.hh:66
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
void stripCgi(std::string &url, const std::unordered_set< std::string > &cgiKeys)
static char * secretkey
The key used to calculate the url hashes.
static bool isdesthttps
True if the redirections must be towards https targets.
static std::unordered_set< std::string > strp_cgi_params
CGI parameters (names) to strip from redirect URLs.
XrdSecEntity SecEntity
Authentication area.
XrdOucString redirdest
Definition: XrdHttpReq.hh:340

References appendOpaque(), XrdOucString::c_str(), calcHashes(), encode_opaque(), encode_str(), XrdHttpProtocol::isdesthttps, keepalive, redirdest, request, reset(), resource, rtGET, XrdHttpProtocol::SecEntity, XrdHttpProtocol::secretkey, stripCgi(), XrdHttpProtocol::strp_cgi_params, and TRACE.

+ Here is the call graph for this function:

◆ ReqReadV()

int XrdHttpReq::ReqReadV ( const XrdHttpIOList cl)

Prepare the buffers for sending a readv request.

Definition at line 451 of file XrdHttpReq.cc.

451  {
452 
453 
454  // Now we build the protocol-ready read ahead list
455  // and also put the correct placeholders inside the cache
456  int n = cl.size();
457  ralist.clear();
458  ralist.reserve(n);
459 
460  int j = 0;
461  for (const auto &c: cl) {
462  ralist.emplace_back();
463  auto &ra = ralist.back();
464  memcpy(&ra.fhandle, this->fhandle, 4);
465 
466  ra.offset = c.offset;
467  ra.rlen = c.size;
468  j++;
469  }
470 
471  if (j > 0) {
472 
473  // Prepare a request header
474 
475  memset(&xrdreq, 0, sizeof (xrdreq));
476 
477  xrdreq.header.requestid = htons(kXR_readv);
478  xrdreq.readv.dlen = htonl(j * sizeof (struct readahead_list));
479 
480  clientMarshallReadAheadList(j);
481 
482 
483  }
484 
485  return (j * sizeof (struct readahead_list));
486 }
struct ClientReadVRequest readv
Definition: XProtocol.hh:910
@ kXR_readv
Definition: XProtocol.hh:138

References ClientReadVRequest::dlen, ClientRequest::header, kXR_readv, ralist, ClientRequest::readv, ClientRequestHdr::requestid, and xrdreq.

Referenced by ProcessHTTPReq().

+ Here is the caller graph for this function:

◆ reset()

void XrdHttpReq::reset ( )
virtual

State machine to talk to the bridge

Definition at line 2771 of file XrdHttpReq.cc.

2771  {
2772 
2773  TRACE(REQ, " XrdHttpReq request ended.");
2774 
2775  //if (xmlbody) xmlFreeDoc(xmlbody);
2777  readClosing = false;
2778  closeAfterError = false;
2779  writtenbytes = 0;
2780  etext.clear();
2781  redirdest = "";
2782 
2783  // // Here we should deallocate this
2784  // const struct iovec *iovP //!< pointer to data array
2785  // int iovN, //!< array count
2786  // int iovL, //!< byte count
2787  // bool final //!< true -> final result
2788 
2789 
2790  //xmlbody = 0;
2791  depth = 0;
2794  ralist.clear();
2795  ralist.shrink_to_fit();
2796 
2797  request = rtUnset;
2798  resource = "";
2799  allheaders.clear();
2800 
2801  // Reset the state of the request's digest request.
2802  m_want_digest.clear();
2803  m_digest_header.clear();
2804  m_req_cksum = nullptr;
2805 
2806  m_user_agent = "";
2807  m_origin = "";
2808 
2809  httpStatusCode = -1;
2810  initialStatusCode= -1;
2811  httpErrorCode = "";
2812  httpErrorBody = "";
2813 
2814  headerok = false;
2815  keepalive = true;
2816  length = 0;
2817  length_seen = false;
2818  filesize = 0;
2819  depth = 0;
2820  sendcontinue = false;
2821 
2822  m_transfer_encoding_chunked = false;
2823  m_current_chunk_size = -1;
2824  m_current_chunk_offset = 0;
2825 
2826  m_trailer_headers = false;
2827  m_status_trailer = false;
2828 
2830  reqstate = 0;
2831 
2832  memset(&xrdreq, 0, sizeof (xrdreq));
2833  memset(&xrdresp, 0, sizeof (xrdresp));
2835 
2836  etext.clear();
2837  redirdest = "";
2838 
2839  stringresp = "";
2840 
2841  host = "";
2842  destination = "";
2843  hdr2cgistr = "";
2844  m_appended_hdr2cgistr = false;
2845  m_appended_asize = false;
2846 
2847  iovP = 0;
2848  iovN = 0;
2849  iovL = 0;
2850 
2851  if (opaque) delete(opaque);
2852  opaque = 0;
2853 
2854  fopened = false;
2855  final = false;
2856  mScitag = -1;
2857 
2858  m_repr_digest.clear();
2859  m_want_repr_digest.clear();
2860 
2862  startTime = std::chrono::steady_clock::time_point::min();
2863 }
@ kXR_noErrorYet
Definition: XProtocol.hh:1069
@ kXR_noResponsesYet
Definition: XProtocol.hh:950
void reset()
resets this handler
std::string m_digest_header
The computed digest for the HTTP response header.
Definition: XrdHttpReq.hh:317
std::string stringresp
If we want to give a string as a response, we compose it here.
Definition: XrdHttpReq.hh:358
XrdHttpMonState monState
Definition: XrdHttpReq.hh:379

References allheaders, closeAfterError, depth, destination, etext, filesize, fopened, hdr2cgistr, headerok, host, iovL, iovN, iovP, keepalive, kXR_noErrorYet, kXR_noResponsesYet, length, length_seen, m_appended_asize, m_appended_hdr2cgistr, m_digest_header, m_origin, m_repr_digest, m_req_cksum, m_want_digest, m_want_repr_digest, monState, mScitag, NEW, opaque, ralist, readClosing, readRangeHandler, redirdest, reqstate, request, XrdHttpReadRangeHandler::reset(), resource, rtUnset, sendcontinue, startTime, stringresp, TRACE, writtenbytes, xrderrcode, xrdreq, and xrdresp.

Referenced by ~XrdHttpReq(), Data(), Done(), Error(), XrdHttpProtocol::Process(), ProcessHTTPReq(), and Redir().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ setHttpStatusCode()

void XrdHttpReq::setHttpStatusCode ( int  code)
inline

Definition at line 229 of file XrdHttpReq.hh.

229  {
230  httpStatusCode = code;
231  if (initialStatusCode < 0 && code >= 200 ) {
232  initialStatusCode = code;
233  }
234  }

◆ setTransferStatusHeader()

void XrdHttpReq::setTransferStatusHeader ( std::string &  header)

Definition at line 2090 of file XrdHttpReq.cc.

2090  {
2091  if (m_status_trailer) {
2092  if (header.empty()) {
2093  header += "Trailer: X-Transfer-Status";
2094  } else {
2095  header += "\r\nTrailer: X-Transfer-Status";
2096  }
2097  }
2098 }

◆ userAgent()

const std::string& XrdHttpReq::userAgent ( ) const
inline

Definition at line 265 of file XrdHttpReq.hh.

265 {return m_user_agent;}

Referenced by XrdHttpProtocol::Process().

+ Here is the caller graph for this function:

Member Data Documentation

◆ allheaders

std::map<std::string, std::string> XrdHttpReq::allheaders

Definition at line 274 of file XrdHttpReq.hh.

Referenced by parseLine(), and reset().

◆ closeAfterError

bool XrdHttpReq::closeAfterError

Definition at line 293 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq(), and reset().

◆ depth

int XrdHttpReq::depth

Definition at line 298 of file XrdHttpReq.hh.

Referenced by XrdHttpReq(), parseLine(), ProcessHTTPReq(), and reset().

◆ destination

std::string XrdHttpReq::destination

The destination field specified in the req.

Definition at line 304 of file XrdHttpReq.hh.

Referenced by parseLine(), ProcessHTTPReq(), and reset().

◆ etagval

long long XrdHttpReq::etagval

Definition at line 349 of file XrdHttpReq.hh.

◆ etext

std::string XrdHttpReq::etext

Definition at line 339 of file XrdHttpReq.hh.

Referenced by Error(), and reset().

◆ fhandle

char XrdHttpReq::fhandle[4]

Definition at line 354 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq().

◆ filectime

long XrdHttpReq::filectime

Definition at line 353 of file XrdHttpReq.hh.

◆ fileflags

long XrdHttpReq::fileflags

Definition at line 351 of file XrdHttpReq.hh.

Referenced by XrdHttpProtocol::doStat(), and ProcessHTTPReq().

◆ filemodtime

long XrdHttpReq::filemodtime

Definition at line 352 of file XrdHttpReq.hh.

Referenced by XrdHttpProtocol::doStat().

◆ filesize

long long XrdHttpReq::filesize

Definition at line 350 of file XrdHttpReq.hh.

Referenced by XrdHttpProtocol::doStat(), ProcessHTTPReq(), and reset().

◆ final

bool XrdHttpReq::final

true -> final result

Definition at line 346 of file XrdHttpReq.hh.

◆ fopened

bool XrdHttpReq::fopened

Definition at line 355 of file XrdHttpReq.hh.

Referenced by XrdHttpReq(), ProcessHTTPReq(), and reset().

◆ hdr2cgistr

std::string XrdHttpReq::hdr2cgistr

Additional opaque info that may come from the hdr2cgi directive.

Definition at line 320 of file XrdHttpReq.hh.

Referenced by addCgi(), appendOpaque(), ProcessHTTPReq(), and reset().

◆ headerok

bool XrdHttpReq::headerok

Tells if we have finished reading the header.

Definition at line 285 of file XrdHttpReq.hh.

Referenced by XrdHttpReq(), XrdHttpProtocol::Process(), and reset().

◆ host

std::string XrdHttpReq::host

The host field specified in the req.

Definition at line 302 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq(), and reset().

◆ iovL

int XrdHttpReq::iovL

byte count

Definition at line 345 of file XrdHttpReq.hh.

Referenced by Data(), and reset().

◆ iovN

int XrdHttpReq::iovN

array count

Definition at line 344 of file XrdHttpReq.hh.

Referenced by Data(), Done(), and reset().

◆ iovP

const struct iovec* XrdHttpReq::iovP

The latest data chunks got from the xrd layer. These are valid only inside the callbacks!

pointer to data array

Definition at line 343 of file XrdHttpReq.hh.

Referenced by Data(), and reset().

◆ keepalive

bool XrdHttpReq::keepalive

Definition at line 295 of file XrdHttpReq.hh.

Referenced by parseFirstLine(), parseLine(), ProcessHTTPReq(), Redir(), and reset().

◆ length

ssize_t XrdHttpReq::length

◆ length_seen

bool XrdHttpReq::length_seen {false}

Definition at line 297 of file XrdHttpReq.hh.

Referenced by parseLine(), and reset().

◆ m_appended_asize

bool XrdHttpReq::m_appended_asize {false}

Track whether we already appended the oss.asize argument for PUTs.

Definition at line 323 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq(), and reset().

◆ m_appended_hdr2cgistr

bool XrdHttpReq::m_appended_hdr2cgistr

Definition at line 321 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq(), and reset().

◆ m_digest_header

std::string XrdHttpReq::m_digest_header

The computed digest for the HTTP response header.

Definition at line 317 of file XrdHttpReq.hh.

Referenced by reset().

◆ m_origin

std::string XrdHttpReq::m_origin

Definition at line 368 of file XrdHttpReq.hh.

Referenced by parseLine(), and reset().

◆ m_repr_digest

std::map<std::string,std::string> XrdHttpReq::m_repr_digest

Repr-Digest map where the key is the digest name and the value is the base64 encoded digest value.

Definition at line 373 of file XrdHttpReq.hh.

Referenced by XrdHttpExtReq::XrdHttpExtReq(), parseLine(), and reset().

◆ m_req_cksum

XrdHttpChecksumHandler::XrdHttpChecksumRawPtr XrdHttpReq::m_req_cksum = nullptr

The checksum that was ran for this request.

Definition at line 310 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq(), and reset().

◆ m_resource_with_digest

XrdOucString XrdHttpReq::m_resource_with_digest

The checksum algorithm is specified as part of the opaque data in the URL. Hence, when a digest is generated to satisfy a request, we cache the tweaked URL in this data member.

Definition at line 315 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq().

◆ m_want_digest

std::string XrdHttpReq::m_want_digest

The requested digest type.

Definition at line 307 of file XrdHttpReq.hh.

Referenced by parseLine(), ProcessHTTPReq(), and reset().

◆ m_want_repr_digest

std::map<std::string,uint8_t> XrdHttpReq::m_want_repr_digest

Want-Repr-Digest map where the key is the digest name and the value is the preference (between 0 and 9)

Definition at line 377 of file XrdHttpReq.hh.

Referenced by XrdHttpExtReq::XrdHttpExtReq(), parseLine(), ProcessHTTPReq(), and reset().

◆ monState

XrdHttpMonState XrdHttpReq::monState

Definition at line 379 of file XrdHttpReq.hh.

Referenced by XrdHttpMon::Record(), and reset().

◆ mScitag

int XrdHttpReq::mScitag

Definition at line 366 of file XrdHttpReq.hh.

Referenced by XrdHttpExtReq::XrdHttpExtReq(), XrdHttpReq(), and reset().

◆ opaque

XrdOucEnv* XrdHttpReq::opaque

The opaque data, after parsing.

Definition at line 279 of file XrdHttpReq.hh.

Referenced by XrdHttpExtReq::XrdHttpExtReq(), XrdHttpReq(), appendOpaque(), parseLine(), XrdHttpProtocol::Process(), ProcessHTTPReq(), and reset().

◆ ralist

std::vector<readahead_list> XrdHttpReq::ralist

Definition at line 247 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq(), ReqReadV(), and reset().

◆ readClosing

bool XrdHttpReq::readClosing

Definition at line 289 of file XrdHttpReq.hh.

Referenced by ProcessHTTPReq(), and reset().

◆ readRangeHandler

XrdHttpReadRangeHandler XrdHttpReq::readRangeHandler

Tracking the next ranges of data to read during GET.

Definition at line 288 of file XrdHttpReq.hh.

Referenced by File(), parseLine(), ProcessHTTPReq(), and reset().

◆ redirdest

XrdOucString XrdHttpReq::redirdest

Definition at line 340 of file XrdHttpReq.hh.

Referenced by Redir(), and reset().

◆ reqstate

int XrdHttpReq::reqstate

State machine to talk to the bridge.

Definition at line 361 of file XrdHttpReq.hh.

Referenced by XrdHttpProtocol::Process(), ProcessHTTPReq(), and reset().

◆ request

ReqType XrdHttpReq::request

The request we got.

Definition at line 269 of file XrdHttpReq.hh.

Referenced by Error(), parseFirstLine(), parseLine(), XrdHttpProtocol::Process(), ProcessHTTPReq(), XrdHttpMon::Record(), Redir(), and reset().

◆ requestverb

std::string XrdHttpReq::requestverb

Definition at line 270 of file XrdHttpReq.hh.

Referenced by parseFirstLine().

◆ resource

XrdOucString XrdHttpReq::resource

The resource specified by the request, stripped of opaque data.

Definition at line 277 of file XrdHttpReq.hh.

Referenced by XrdHttpExtReq::XrdHttpExtReq(), XrdHttpProtocol::Process(), ProcessHTTPReq(), Redir(), and reset().

◆ resourceplusopaque

XrdOucString XrdHttpReq::resourceplusopaque

The resource specified by the request, including all the opaque data.

Definition at line 281 of file XrdHttpReq.hh.

Referenced by XrdHttpExtReq::XrdHttpExtReq(), and ProcessHTTPReq().

◆ rwOpDone

unsigned int XrdHttpReq::rwOpDone

To coordinate multipart responses across multiple calls.

Definition at line 331 of file XrdHttpReq.hh.

◆ rwOpPartialDone

unsigned int XrdHttpReq::rwOpPartialDone

Definition at line 331 of file XrdHttpReq.hh.

◆ sendcontinue

bool XrdHttpReq::sendcontinue

Definition at line 299 of file XrdHttpReq.hh.

Referenced by parseLine(), ProcessHTTPReq(), and reset().

◆ startTime

std::chrono::steady_clock::time_point XrdHttpReq::startTime = std::chrono::steady_clock::time_point::min()

Definition at line 370 of file XrdHttpReq.hh.

Referenced by XrdHttpProtocol::Process(), ProcessHTTPReq(), XrdHttpMon::Record(), and reset().

◆ stringresp

std::string XrdHttpReq::stringresp

If we want to give a string as a response, we compose it here.

Definition at line 358 of file XrdHttpReq.hh.

Referenced by reset().

◆ writtenbytes

long long XrdHttpReq::writtenbytes

In a long write, we track where we have arrived.

Definition at line 364 of file XrdHttpReq.hh.

Referenced by XrdHttpReq(), ProcessHTTPReq(), and reset().

◆ xrderrcode

XErrorCode XrdHttpReq::xrderrcode

Definition at line 338 of file XrdHttpReq.hh.

Referenced by Error(), and reset().

◆ xrdreq

ClientRequest XrdHttpReq::xrdreq

The last issued xrd request, often pending.

Definition at line 334 of file XrdHttpReq.hh.

Referenced by XrdHttpProtocol::doChksum(), XrdHttpProtocol::doStat(), Error(), XrdHttpProtocol::Process(), ProcessHTTPReq(), ReqReadV(), and reset().

◆ xrdresp

XResponseType XrdHttpReq::xrdresp

The last response data we got.

Definition at line 337 of file XrdHttpReq.hh.

Referenced by Data(), Done(), Error(), and reset().


The documentation for this class was generated from the following files: