XRootD
XrdHttpReq.cc
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // This file is part of XrdHTTP: A pragmatic implementation of the
3 // HTTP/WebDAV protocol for the Xrootd framework
4 //
5 // Copyright (c) 2013 by European Organization for Nuclear Research (CERN)
6 // Author: Fabrizio Furano <furano@cern.ch>
7 // File Date: Nov 2012
8 //------------------------------------------------------------------------------
9 // XRootD is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU Lesser General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // XRootD is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public License
20 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
21 //------------------------------------------------------------------------------
22 
23 
24 
25 
26 
27 
28 
29 
30 
39 #include "XrdVersion.hh"
40 #include "XrdHttpReq.hh"
41 #include "XrdHttpTrace.hh"
42 #include "XrdHttpExtHandler.hh"
43 #include <cstring>
44 #include <arpa/inet.h>
45 #include <sstream>
46 #include "XrdSys/XrdSysPlatform.hh"
47 #include "XrdOuc/XrdOucEnv.hh"
48 #include "XrdHttpProtocol.hh"
49 #include "Xrd/XrdLink.hh"
51 #include "Xrd/XrdBuffer.hh"
52 #include <algorithm>
53 #include <functional>
54 #include <cctype>
55 #include <locale>
56 #include <string>
57 #include "XrdOuc/XrdOucTUtils.hh"
58 #include "XrdOuc/XrdOucUtils.hh"
61 
62 #include "XrdHttpUtils.hh"
63 
64 #include "XrdHttpStatic.hh"
65 
66 #define MAX_TK_LEN 256
67 #define MAX_RESOURCE_LEN 16384
68 
69 // This is to fix the trace macros
70 #define TRACELINK prot->Link
71 
72 namespace
73 {
74 const char *TraceID = "Req";
75 }
76 
77 void trim(std::string &str)
78 {
79  XrdOucUtils::trim(str);
80 }
81 
82 
83 std::string ISOdatetime(time_t t) {
84  char datebuf[128];
85  struct tm t1;
86 
87  memset(&t1, 0, sizeof (t1));
88  gmtime_r(&t, &t1);
89 
90  strftime(datebuf, 127, "%a, %d %b %Y %H:%M:%S GMT", &t1);
91  return (std::string) datebuf;
92 
93 }
94 
95 int XrdHttpReq::parseBody(char *body, long long len) {
96  /*
97  * The document being in memory, it has no base per RFC 2396,
98  * and the "noname.xml" argument will serve as its base.
99  */
100  //xmlbody = xmlReadMemory(body, len, "noname.xml", NULL, 0);
101  //if (xmlbody == NULL) {
102  // fprintf(stderr, "Failed to parse document\n");
103  // return 1;
104  //}
105 
106 
107 
108  return 1;
109 }
110 
112  //if (xmlbody) xmlFreeDoc(xmlbody);
113 
114  reset();
115 }
116 
117 int XrdHttpReq::parseLine(char *line, int len) {
118 
119  char *key = line;
120  int pos;
121 
122  // Do the parsing
123  if (!line) return -1;
124 
125 
126  char *p = strchr((char *) line, (int) ':');
127  if (!p) {
128 
130  return -1;
131  }
132 
133  pos = (p - line);
134  if (pos > (MAX_TK_LEN - 1)) {
135 
137  return -2;
138  }
139 
140  if (pos > 0) {
141  line[pos] = 0;
142  char *val = line + pos + 1;
143 
144  // Trim left
145  while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
146 
147  // We memorize the headers also as a string
148  // because external plugins may need to process it differently
149  std::string ss = val;
150  if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) != "\r\n") {
152  return -3;
153  }
154  trim(ss);
155  allheaders[key] = ss;
156 
157  // Here we are supposed to initialize whatever flag or variable that is needed
158  // by looking at the first token of the line
159  // The token is key
160  // The value is val
161 
162  // Screen out the needed header lines
163  if (!strcasecmp(key, "connection")) {
164 
165  if (!strcasecmp(val, "Keep-Alive\r\n")) {
166  keepalive = true;
167  } else if (!strcasecmp(val, "close\r\n")) {
168  keepalive = false;
169  }
170 
171  } else if (!strcasecmp(key, "host")) {
172  parseHost(val);
173  } else if (!strcasecmp(key, "range")) {
174  // (rfc2616 14.35.1) says if Range header contains any range
175  // which is syntactically invalid the Range header should be ignored.
176  // Therefore no need for the range handler to report an error.
178  } else if (!strcasecmp(key, "content-length")) {
179  length = atoll(val);
180 
181  } else if (!strcasecmp(key, "destination")) {
182  destination.assign(val, line+len-val);
183  trim(destination);
184  } else if (!strcasecmp(key, "want-digest")) {
185  m_req_digest.assign(val, line + len - val);
187  //Transform the user requests' want-digest to lowercase
188  std::transform(m_req_digest.begin(),m_req_digest.end(),m_req_digest.begin(),::tolower);
189  } else if (!strcasecmp(key, "depth")) {
190  depth = -1;
191  if (strcmp(val, "infinity"))
192  depth = atoll(val);
193 
194  } else if (!strcasecmp(key, "expect") && strstr(val, "100-continue")) {
195  sendcontinue = true;
196  } else if (!strcasecmp(key, "te") && strstr(val, "trailers")) {
197  m_trailer_headers = true;
198  } else if (!strcasecmp(key, "transfer-encoding") && strstr(val, "chunked")) {
199  m_transfer_encoding_chunked = true;
200  } else if (!strcasecmp(key, "x-transfer-status") && strstr(val, "true")) {
201  m_transfer_encoding_chunked = true;
202  m_status_trailer = true;
203  } else if (!strcasecmp(key, "scitag")) {
204  if(prot->pmarkHandle != nullptr) {
205  parseScitag(val);
206  }
207  } else if (!strcasecmp(key, "user-agent")) {
208  m_user_agent = val;
209  trim(m_user_agent);
210  } else if (!strcasecmp(key,"origin")) {
211  m_origin = val;
212  trim(m_origin);
213  } else {
214  // Some headers need to be translated into "local" cgi info.
215  auto it = std::find_if(prot->hdr2cgimap.begin(), prot->hdr2cgimap.end(),[key](const auto & item) {
216  return !strcasecmp(key,item.first.c_str());
217  });
218  if (it != prot->hdr2cgimap.end() && (opaque ? (0 == opaque->Get(it->second.c_str())) : true)) {
219  std::string s;
220  s.assign(val, line+len-val);
221  trim(s);
222  addCgi(it->second,s);
223  }
224  }
225 
226 
227  line[pos] = ':';
228  }
229 
230  return 0;
231 }
232 
233 int XrdHttpReq::parseHost(char *line) {
234  host = line;
235  trim(host);
236  return 0;
237 }
238 
239 void XrdHttpReq::parseScitag(const std::string & val) {
240  // The scitag header has been populated and the packet marking was configured, the scitag will either be equal to 0
241  // or to the value passed by the client
242  mScitag = 0;
243  std::string scitagS = val;
244  trim(scitagS);
245  if(scitagS.size()) {
246  if(scitagS[0] != '-') {
247  try {
248  mScitag = std::stoi(scitagS.c_str(), nullptr, 10);
250  mScitag = 0;
251  }
252  } catch (...) {
253  //Nothing to do, scitag = 0 by default
254  }
255  }
256  }
257  addCgi("scitag.flow", std::to_string(mScitag));
258  if(request == ReqType::rtGET || request == ReqType::rtPUT) {
259  // We specify to the packet marking handle the type of transfer this request is
260  // so the source and destination in the firefly are properly set
261  addCgi("pmark.appname",this->request == ReqType::rtGET ? "http-get" : "http-put");
262  }
263 }
264 
265 int XrdHttpReq::parseFirstLine(char *line, int len) {
266 
267  char *key = line;
268 
269  int pos;
270 
271  // Do the naive parsing
272  if (!line) return -1;
273 
274  // Look for the first space-delimited token
275  char *p = strchr((char *) line, (int) ' ');
276  if (!p) {
278  return -1;
279  }
280 
281 
282  pos = p - line;
283  // The first token cannot be too long
284  if (pos > MAX_TK_LEN - 1) {
286  return -2;
287  }
288 
289  // The first space-delimited char cannot be the first one
290  // this allows to deal with the case when a client sends a first line that starts with a space " GET / HTTP/1.1"
291  if(pos == 0) {
293  return -4;
294  }
295 
296  // the first token must be non empty
297  if (pos > 0) {
298  line[pos] = 0;
299  char *val = line + pos + 1;
300 
301  // Here we are supposed to initialize whatever flag or variable that is needed
302  // by looking at the first token of the line
303 
304  // The token is key
305  // The remainder is val, look for the resource
306  p = strchr((char *) val, (int) ' ');
307 
308  if (!p) {
310  line[pos] = ' ';
311  return -3;
312  }
313 
314  *p = '\0';
315  parseResource(val);
316 
317  *p = ' ';
318 
319  // Xlate the known header lines
320  if (!strcmp(key, "GET")) {
321  request = rtGET;
322  } else if (!strcmp(key, "HEAD")) {
323  request = rtHEAD;
324  } else if (!strcmp(key, "PUT")) {
325  request = rtPUT;
326  } else if (!strcmp(key, "POST")) {
327  request = rtPOST;
328  } else if (!strcmp(key, "PATCH")) {
329  request = rtPATCH;
330  } else if (!strcmp(key, "OPTIONS")) {
331  request = rtOPTIONS;
332  } else if (!strcmp(key, "DELETE")) {
333  request = rtDELETE;
334  } else if (!strcmp(key, "PROPFIND")) {
336 
337  } else if (!strcmp(key, "MKCOL")) {
338  request = rtMKCOL;
339 
340  } else if (!strcmp(key, "MOVE")) {
341  request = rtMOVE;
342  } else {
343  request = rtUnknown;
344  }
345 
346  requestverb = key;
347 
348  // The last token should be the protocol. If it is HTTP/1.0, then
349  // keepalive is disabled by default.
350  if (!strcmp(p+1, "HTTP/1.0\r\n")) {
351  keepalive = false;
352  }
353  line[pos] = ' ';
354  }
355 
356  return 0;
357 }
358 
359 
360 
361 
362 //___________________________________________________________________________
363 
364 void XrdHttpReq::clientMarshallReadAheadList(int nitems) {
365  // This function applies the network byte order on the
366  // vector of read-ahead information
367  kXR_int64 tmpl;
368 
369 
370 
371  for (int i = 0; i < nitems; i++) {
372  memcpy(&tmpl, &(ralist[i].offset), sizeof (kXR_int64));
373  tmpl = htonll(tmpl);
374  memcpy(&(ralist[i].offset), &tmpl, sizeof (kXR_int64));
375  ralist[i].rlen = htonl(ralist[i].rlen);
376  }
377 }
378 
379 
380 //___________________________________________________________________________
381 
382 void XrdHttpReq::clientUnMarshallReadAheadList(int nitems) {
383  // This function applies the network byte order on the
384  // vector of read-ahead information
385  kXR_int64 tmpl;
386 
387 
388 
389  for (int i = 0; i < nitems; i++) {
390  memcpy(&tmpl, &(ralist[i].offset), sizeof (kXR_int64));
391  tmpl = ntohll(tmpl);
392  memcpy(&(ralist[i].offset), &tmpl, sizeof (kXR_int64));
393  ralist[i].rlen = ntohl(ralist[i].rlen);
394  }
395 }
396 
398 
399 
400  // Now we build the protocol-ready read ahead list
401  // and also put the correct placeholders inside the cache
402  int n = cl.size();
403  ralist.clear();
404  ralist.reserve(n);
405 
406  int j = 0;
407  for (const auto &c: cl) {
408  ralist.emplace_back();
409  auto &ra = ralist.back();
410  memcpy(&ra.fhandle, this->fhandle, 4);
411 
412  ra.offset = c.offset;
413  ra.rlen = c.size;
414  j++;
415  }
416 
417  if (j > 0) {
418 
419  // Prepare a request header
420 
421  memset(&xrdreq, 0, sizeof (xrdreq));
422 
423  xrdreq.header.requestid = htons(kXR_readv);
424  xrdreq.readv.dlen = htonl(j * sizeof (struct readahead_list));
425 
426  clientMarshallReadAheadList(j);
427 
428 
429  }
430 
431  return (j * sizeof (struct readahead_list));
432 }
433 
434 std::string XrdHttpReq::buildPartialHdr(long long bytestart, long long byteend, long long fsz, char *token) {
435  std::ostringstream s;
436 
437  s << "\r\n--" << token << "\r\n";
438  s << "Content-type: text/plain; charset=UTF-8\r\n";
439  s << "Content-range: bytes " << bytestart << "-" << byteend << "/" << fsz << "\r\n\r\n";
440 
441  return s.str();
442 }
443 
444 std::string XrdHttpReq::buildPartialHdrEnd(char *token) {
445  std::ostringstream s;
446 
447  s << "\r\n--" << token << "--\r\n";
448 
449  return s.str();
450 }
451 
453  const
454  struct iovec *iovP_,
455  int iovN_,
456  int iovL_,
457  bool final_
458  ) {
459 
460  TRACE(REQ, " XrdHttpReq::Data! final=" << final);
461 
462  this->xrdresp = kXR_ok;
463  this->iovP = iovP_;
464  this->iovN = iovN_;
465  this->iovL = iovL_;
466  this->final = final_;
467 
468  if (PostProcessHTTPReq(final_)) reset();
469 
470  return true;
471 
472 };
473 
475  int dlen
476  ) {
477 
478  // sendfile about to be sent by bridge for fetching data for GET:
479  // no https, no chunked+trailer, no multirange
480 
481  //prot->SendSimpleResp(200, NULL, NULL, NULL, dlen);
482  int rc = info.Send(0, 0, 0, 0);
483  TRACE(REQ, " XrdHttpReq::File dlen:" << dlen << " send rc:" << rc);
484  bool start, finish;
485  // short read will be classed as error
486  if (rc) {
488  return false;
489  }
490 
491  if (readRangeHandler.NotifyReadResult(dlen, nullptr, start, finish) < 0)
492  return false;
493 
494 
495  return true;
496 };
497 
499 
500  TRACE(REQ, " XrdHttpReq::Done");
501 
502  xrdresp = kXR_ok;
503 
504  this->iovN = 0;
505 
506  int r = PostProcessHTTPReq(true);
507  // Beware, we don't have to reset() if the result is 0
508  if (r) reset();
509  if (r < 0) return false;
510 
511 
512  return true;
513 };
514 
516  int ecode,
517  const char *etext_
518  ) {
519 
520  TRACE(REQ, " XrdHttpReq::Error");
521 
522  xrdresp = kXR_error;
523  xrderrcode = (XErrorCode) ecode;
524 
525  if (etext_) {
526  char *s = escapeXML(etext_);
527  this->etext = s;
528  free(s);
529  }
530 
531  auto rc = PostProcessHTTPReq();
532  if (rc) {
533  reset();
534  }
535 
536  // If we are servicing a GET on a directory, it'll generate an error for the default
537  // OSS (we don't assume this is always true). Catch and suppress the error so we can instead
538  // generate a directory listing (if configured).
539  if ((request == rtGET) && (xrdreq.header.requestid == ntohs(kXR_open)) && (xrderrcode == kXR_isDirectory))
540  return true;
541 
542  return rc == 0;
543 };
544 
546  int port,
547  const char *hname
548  ) {
549 
550 
551 
552  char buf[512];
553  char hash[512];
554  hash[0] = '\0';
555 
556  bool invalid_host = false;
557 
558  if (!hname) {
559  invalid_host = true;
560  } else {
561  for (const char *c = hname; *c; ++c)
562  if (*c == '\r' || *c == '\n')
563  invalid_host = true;
564  }
565 
566  if (invalid_host) {
567  prot->SendSimpleResp(502, nullptr, nullptr, "Invalid redirect host", 0, false);
568  return keepalive;
569  }
570 
571  if (prot->isdesthttps)
572  redirdest = "Location: https://";
573  else
574  redirdest = "Location: http://";
575 
576  // port < 0 signals switch to full URL
577  if (port < 0)
578  {
579  if (strncmp(hname, "file://", 7) == 0)
580  {
581  TRACE(REQ, " XrdHttpReq::Redir Switching to file:// ");
582  redirdest = "Location: "; // "file://" already contained in hname
583  }
584  }
585  // Beware, certain Ofs implementations (e.g. EOS) add opaque data directly to the host name
586  // This must be correctly treated here and appended to the opaque info
587  // that we may already have
588  char *pp = strchr((char *)hname, '?');
589  char *vardata = 0;
590  if (pp) {
591  *pp = '\0';
592  redirdest += hname;
593  vardata = pp+1;
594  int varlen = strlen(vardata);
595 
596  //Now extract the remaining, vardata points to it
597  while(*vardata == '&' && varlen) {vardata++; varlen--;}
598 
599  // Put the question mark back where it was
600  *pp = '?';
601  }
602  else
603  redirdest += hname;
604 
605  if (port > 0) {
606  sprintf(buf, ":%d", port);
607  redirdest += buf;
608  }
609 
610  redirdest += encode_str(resource.c_str()).c_str();
611 
612  // Here we put back the opaque info, if any
613  if (vardata) {
614  redirdest += "?&";
615  redirdest += encode_opaque(vardata).c_str();
616  }
617 
618  // Shall we put also the opaque data of the request? Maybe not
619  //int l;
620  //if (opaque && opaque->Env(l))
621  // redirdest += opaque->Env(l);
622 
623 
624  time_t timenow = 0;
625  if (!prot->isdesthttps && prot->ishttps) {
626  // If the destination is not https, then we suppose that it
627  // will need this token to fill its authorization info
628  timenow = time(0);
629  calcHashes(hash, this->resource.c_str(), (kXR_int16) request,
630  &prot->SecEntity,
631  timenow,
632  prot->secretkey);
633  }
634 
635  if (hash[0]) {
636  appendOpaque(redirdest, &prot->SecEntity, hash, timenow);
637  } else
638  appendOpaque(redirdest, 0, 0, 0);
639 
640 
641  TRACE(REQ, " XrdHttpReq::Redir Redirecting to " << obfuscateAuth(redirdest.c_str()).c_str());
642 
643  if (request != rtGET)
644  prot->SendSimpleResp(307, NULL, (char *) redirdest.c_str(), 0, 0, keepalive);
645  else
646  prot->SendSimpleResp(302, NULL, (char *) redirdest.c_str(), 0, 0, keepalive);
647 
648  bool ret_keepalive = keepalive; // reset() clears keepalive
649  reset();
650  return ret_keepalive;
651 };
652 
653 
654 void XrdHttpReq::appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow) {
655 
656  int l = 0;
657  char * p = 0;
658  if (opaque)
659  p = opaque->Env(l);
660 
661  if (hdr2cgistr.empty() && (l < 2) && !hash) return;
662 
663  // this works in most cases, except if the url already contains the xrdhttp tokens
664  s = s + "?";
665  if (!hdr2cgistr.empty()) {
666  s += encode_opaque(hdr2cgistr).c_str();
667  }
668  if (p && (l > 1)) {
669  if (!hdr2cgistr.empty()) {
670  s = s + "&";
671  }
672  s = s + encode_opaque(p + 1).c_str();
673  }
674 
675  if (hash) {
676  if (l > 1) s += "&";
677  s += "xrdhttptk=";
678  s += hash;
679 
680  s += "&xrdhttptime=";
681  char buf[256];
682  sprintf(buf, "%lld", (long long) tnow);
683  s += buf;
684 
685  if (secent) {
686  if (secent->name) {
687  s += "&xrdhttpname=";
688  s += encode_str(secent->name).c_str();
689  }
690  }
691 
692  if (secent->vorg) {
693  s += "&xrdhttpvorg=";
694  s += encode_str(secent->vorg).c_str();
695  }
696 
697  if (secent->host) {
698  s += "&xrdhttphost=";
699  s += encode_str(secent->host).c_str();
700  }
701 
702  if (secent->moninfo) {
703  s += "&xrdhttpdn=";
704  s += encode_str(secent->moninfo).c_str();
705  }
706 
707  if (secent->role) {
708  s += "&xrdhttprole=";
709  s += encode_str(secent->role).c_str();
710  }
711 
712  if (secent->grps) {
713  s += "&xrdhttpgrps=";
714  s += encode_str(secent->grps).c_str();
715  }
716 
717  if (secent->endorsements) {
718  s += "&xrdhttpendorsements=";
719  s += encode_str(secent->endorsements).c_str();
720  }
721 
722  if (secent->credslen) {
723  s += "&xrdhttpcredslen=";
724  char buf[16];
725  sprintf(buf, "%d", secent->credslen);
726  s += encode_str(buf).c_str();
727  }
728 
729  if (secent->credslen) {
730  if (secent->creds) {
731  s += "&xrdhttpcreds=";
732  // Apparently this string might be not 0-terminated (!)
733  char *zerocreds = strndup(secent->creds, secent->credslen);
734  if (zerocreds) {
735  s += encode_str(zerocreds).c_str();
736  free(zerocreds);
737  }
738  }
739  }
740  }
741  }
742 
743 // Sanitize the resource from the http[s]://[host]/ questionable prefix
744 // https://github.com/xrootd/xrootd/issues/1675
745 void XrdHttpReq::sanitizeResourcePfx() {
746 
747  if (resource.beginswith("https://")) {
748  // Find the slash that follows the hostname, and keep it
749  int p = resource.find('/', 8);
751  return;
752  }
753 
754  if (resource.beginswith("http://")) {
755  // Find the slash that follows the hostname, and keep it
756  int p = resource.find('/', 7);
758  return;
759  }
760 }
761 
762 void XrdHttpReq::addCgi(const std::string &key, const std::string &value) {
763  if (hdr2cgistr.length() > 0) {
764  hdr2cgistr.append("&");
765  }
766  hdr2cgistr.append(key);
767  hdr2cgistr.append("=");
768  hdr2cgistr.append(value);
769 }
770 
771 
772 // Parse a resource line:
773 // - sanitize
774 // - extracts the opaque info from the given url
775 // - sanitize the resource from http[s]://[host]/ questionable prefix
776 void XrdHttpReq::parseResource(char *res) {
777 
778 
779 
780 
781  // Look for the first '?'
782  char *p = strchr(res, '?');
783 
784  // Not found, then it's just a filename
785  if (!p) {
786  resource.assign(res, 0);
787 
788  // Some poor client implementations may inject a http[s]://[host]/ prefix
789  // to the resource string. Here we choose to ignore it as a protection measure
790  sanitizeResourcePfx();
791 
792  std::string resourceDecoded = decode_str(resource.c_str());
793  resource = resourceDecoded.c_str();
794  resourceplusopaque = resourceDecoded.c_str();
795 
796 
797  // Sanitize the resource string, removing double slashes
798  int pos = 0;
799  do {
800  pos = resource.find("//", pos);
801  if (pos != STR_NPOS)
802  resource.erase(pos, 1);
803  } while (pos != STR_NPOS);
804 
805  return;
806  }
807 
808  // Whatever comes before '?' is a filename
809 
810  int cnt = p - res; // Number of chars to copy
811  resource.assign(res, 0, cnt - 1);
812 
813  // Some poor client implementations may inject a http[s]://[host]/ prefix
814  // to the resource string. Here we choose to ignore it as a protection measure
815  sanitizeResourcePfx();
816 
817  resource = decode_str(resource.c_str()).c_str();
818 
819  // Sanitize the resource string, removing double slashes
820  int pos = 0;
821  do {
822  pos = resource.find("//", pos);
823  if (pos != STR_NPOS)
824  resource.erase(pos, 1);
825  } while (pos != STR_NPOS);
826 
828  // Whatever comes after is opaque data to be parsed
829  if (strlen(p) > 1) {
830  std::string decoded = decode_str(p + 1);
831  opaque = new XrdOucEnv(decoded.c_str());
833  resourceplusopaque.append(p + 1);
834  }
835 }
836 
837 void XrdHttpReq::generateWebdavErrMsg() {
838 
839  // This block is only used when sending an "X-Transfer-Status" trailer response.
840  // We set the body to "OK" so that the trailer becomes "X-Transfer-Status: 200 OK",
841  // indicating a successful transfer.
842  if (xrdresp == kXR_ok) {
843  httpStatusCode = 200;
844  httpErrorBody = "OK";
845  return;
846  }
847 
848  // default error
849  httpStatusCode = mapXrdErrToHttp(xrderrcode);
850  httpErrorBody = etext + "\n";
851 
852 }
853 
855 
856  kXR_int32 l;
857 
858  // State variable for tracking the query parameter search
859  // - 0: Indicates we've not yet searched the URL for '?'
860  // - 1: Indicates we have a '?' and hence query parameters
861  // - 2: Indicates we do *not* have '?' present -- no query parameters
862  int query_param_status = 0;
863  if (!m_appended_asize) {
864  m_appended_asize = true;
865  if (request == rtPUT && length) {
866  if (query_param_status == 0) {
867  query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
868  }
869  resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
870  query_param_status = 1;
871  auto length_str = std::to_string(length);
872  resourceplusopaque.append("oss.asize=");
873  resourceplusopaque.append(length_str.c_str());
874  if (!opaque) {
875  opaque = new XrdOucEnv();
876  }
877  opaque->Put("oss.asize", length_str.c_str());
878  }
879  }
880 
882  if (!m_appended_hdr2cgistr && !hdr2cgistr.empty()) {
883  if (query_param_status == 0) {
884  query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
885  }
886  resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
887 
888  std::string hdr2cgistrEncoded = encode_opaque(hdr2cgistr);
889  resourceplusopaque.append(hdr2cgistrEncoded.c_str());
890  if (TRACING(TRACE_DEBUG)) {
891  // The obfuscation of "authz" will only be done if the server http.header2cgi config contains something that maps a header to this "authz" cgi.
892  // Unfortunately the obfuscation code will be called no matter what is configured in http.header2cgi.
893  std::string header2cgistrObf = obfuscateAuth(hdr2cgistr);
894 
895  TRACEI(DEBUG, "Appended header fields to opaque info: '"
896  << header2cgistrObf.c_str() << "'");
897 
898  }
899 
900  m_appended_hdr2cgistr = true;
901  }
902 
903  // Verify if we have an external handler for this request
904  if (reqstate == 0) {
905  XrdHttpExtHandler *exthandler = prot->FindMatchingExtHandler(*this);
906  if (exthandler) {
907  XrdHttpExtReq xreq(this, prot);
908  int r = exthandler->ProcessReq(xreq);
909  reset();
910  if (!r) return 1; // All went fine, response sent
911  if (r < 0) return -1; // There was a hard error... close the connection
912 
913  return 1; // There was an error and a response was sent
914  }
915  }
916 
917  //
918  // Here we process the request locally
919  //
920 
921  switch (request) {
922  case XrdHttpReq::rtUnset:
925  generateWebdavErrMsg();
926  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
927  reset();
928  return -1;
929  }
930  case XrdHttpReq::rtHEAD:
931  {
932  if (reqstate == 0) {
933  // Always start with Stat; in the case of a checksum request, we'll have a follow-up query
934  if (prot->doStat((char *) resourceplusopaque.c_str())) {
935  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
936  return -1;
937  }
938  return 0;
939  } else {
940  const char *opaque = strchr(resourceplusopaque.c_str(), '?');
941  // Note that doChksum requires that the memory stays alive until the callback is invoked.
943 
945  if(!m_req_cksum) {
946  // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
947  // We should not send body in response to HEAD request
948  prot->SendSimpleResp(HTTP_METHOD_NOT_ALLOWED, NULL, NULL, NULL, 0, false);
949  return -1;
950  }
951  if (!opaque) {
952  m_resource_with_digest += "?cks.type=";
954  } else {
955  m_resource_with_digest += "&cks.type=";
957  }
958  if (prot->doChksum(m_resource_with_digest) < 0) {
959  // In this case, the Want-Digest header was set and PostProcess gave the go-ahead to do a checksum.
960  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to create initial checksum request.", 0, false);
961  return -1;
962  }
963  return 1;
964  }
965  }
966  case XrdHttpReq::rtGET:
967  {
968  int retval = keepalive ? 1 : -1; // reset() clears keepalive
969 
970  if (resource.beginswith("/static/")) {
971 
972  // This is a request for a /static resource
973  // If we have to use the embedded ones then we return the ones in memory as constants
974 
975  // The sysadmin can always redirect the request to another host that
976  // contains his static resources
977 
978  // We also allow xrootd to preread from the local disk all the files
979  // that have to be served as static resources.
980 
981  if (prot->embeddedstatic) {
982 
983  // Default case: the icon and the css of the HTML rendering of XrdHttp
984  if (resource == "/static/css/xrdhttp.css") {
985  prot->SendSimpleResp(200, NULL, NULL, (char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len, keepalive);
986  reset();
987  return retval;
988  }
989  if (resource == "/static/icons/xrdhttp.ico") {
990  prot->SendSimpleResp(200, NULL, NULL, (char *) favicon_ico, favicon_ico_len, keepalive);
991  reset();
992  return retval;
993  }
994 
995  }
996 
997  // If we are here then none of the embedded resources match (or they are disabled)
998  // We may have to redirect to a host that is supposed to serve the static resources
999  if (prot->staticredir) {
1000 
1001  XrdOucString s = "Location: ";
1002  s.append(prot->staticredir);
1003 
1004  if (s.endswith('/'))
1005  s.erasefromend(1);
1006 
1007  s.append(encode_str(std::string(resource.c_str())).c_str());
1008  appendOpaque(s, 0, 0, 0);
1009 
1010  prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1011  return -1;
1012 
1013 
1014  } else {
1015 
1016  // We lookup the requested path in a hash containing the preread files
1017  if (prot->staticpreload) {
1019  if (mydata) {
1020  prot->SendSimpleResp(200, NULL, NULL, (char *) mydata->data, mydata->len, keepalive);
1021  reset();
1022  return retval;
1023  }
1024  }
1025 
1026  }
1027 
1028 
1029  }
1030 
1031  // The reqstate parameter basically moves us through a simple state machine.
1032  // To optimize things, we start off by opening the file; if it turns out to be a directory, then
1033  // we close the file handle and switch to doing a HTML-based rendering of the directory. This
1034  // avoids needing to always to do "stat" first to determine the next step (since the file-open also
1035  // does a "stat").
1036  // - 0: Perform an open on the resource
1037  // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
1038  // - 2: Perform a close (for dirlist only)
1039  // - 3: Perform a dirlist.
1040  // - 4+: Reads from file; if at end, perform a close.
1041  switch (reqstate) {
1042  case 0: // Open the path for reading.
1043  {
1044  memset(&xrdreq, 0, sizeof (ClientRequest));
1045  xrdreq.open.requestid = htons(kXR_open);
1046  l = resourceplusopaque.length() + 1;
1047  xrdreq.open.dlen = htonl(l);
1048  xrdreq.open.mode = 0;
1050 
1051  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1052  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1053  return -1;
1054  }
1055 
1056  // Prepare to chunk up the request
1057  writtenbytes = 0;
1058 
1059  // We want to be invoked again after this request is finished
1060  return 0;
1061  }
1062  case 1: // Checksum request
1063  if (!(fileflags & kXR_isDir) && !m_req_digest.empty()) {
1064  // In this case, the Want-Digest header was set.
1065  bool has_opaque = strchr(resourceplusopaque.c_str(), '?');
1066  // Note that doChksum requires that the memory stays alive until the callback is invoked.
1068  if(!m_req_cksum) {
1069  // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1070  prot->SendSimpleResp(HTTP_METHOD_NOT_ALLOWED, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1071  return -1;
1072  }
1074  if (has_opaque) {
1075  m_resource_with_digest += "&cks.type=";
1077  } else {
1078  m_resource_with_digest += "?cks.type=";
1080  }
1081  if (prot->doChksum(m_resource_with_digest) < 0) {
1082  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to start internal checksum request to satisfy Want-Digest header.", 0, false);
1083  return -1;
1084  }
1085  return 0;
1086  } else {
1087  TRACEI(DEBUG, "No checksum requested; skipping to request state 2");
1088  reqstate += 1;
1089  }
1090  // fallthrough
1091  case 2: // Close file handle for directory
1092  if ((fileflags & kXR_isDir) && fopened) {
1093  memset(&xrdreq, 0, sizeof (ClientRequest));
1094  xrdreq.close.requestid = htons(kXR_close);
1095  memcpy(xrdreq.close.fhandle, fhandle, 4);
1096 
1097  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1098  generateWebdavErrMsg();
1099  return sendFooterError("Could not run close request on the bridge");
1100  }
1101  return 0;
1102  } else {
1103  reqstate += 1;
1104  }
1105  // fallthrough
1106  case 3: // List directory
1107  if (fileflags & kXR_isDir) {
1108  if (prot->listdeny) {
1109  prot->SendSimpleResp(503, NULL, NULL, (char *) "Listings are disabled.", 0, false);
1110  return -1;
1111  }
1112 
1113  if (prot->listredir) {
1114  XrdOucString s = "Location: ";
1115  s.append(prot->listredir);
1116 
1117  if (s.endswith('/'))
1118  s.erasefromend(1);
1119 
1120  s.append(encode_str(std::string(resource.c_str())).c_str());
1121  appendOpaque(s, 0, 0, 0);
1122 
1123  prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1124  return -1;
1125  }
1126 
1127  std::string res;
1128  res = resourceplusopaque.c_str();
1129 
1130  // --------- DIRLIST
1131  memset(&xrdreq, 0, sizeof (ClientRequest));
1134  l = res.length() + 1;
1135  xrdreq.dirlist.dlen = htonl(l);
1136 
1137  if (!prot->Bridge->Run((char *) &xrdreq, (char *) res.c_str(), l)) {
1138  generateWebdavErrMsg();
1139  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1140  sendFooterError("Could not run listing request on the bridge");
1141  return -1;
1142  }
1143 
1144  // We don't want to be invoked again after this request is finished
1145  return 1;
1146  }
1147  else {
1148  reqstate += 1;
1149  }
1150  // fallthrough
1151  case 4:
1152  {
1153  auto retval = ReturnGetHeaders();
1154  if (retval) {
1155  return retval;
1156  }
1157  }
1158  // fallthrough
1159  default: // Read() or Close(); reqstate is 4+
1160  {
1161  const XrdHttpIOList &readChunkList = readRangeHandler.NextReadList();
1162 
1163  // Close() if we have finished, otherwise read the next chunk
1164 
1165  // --------- CLOSE
1166  if ( closeAfterError || readChunkList.empty() )
1167  {
1168 
1169  memset(&xrdreq, 0, sizeof (ClientRequest));
1170  xrdreq.close.requestid = htons(kXR_close);
1171  memcpy(xrdreq.close.fhandle, fhandle, 4);
1172 
1173  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1174  TRACEI(REQ, " Failed to run close request on the bridge.");
1175  // Note: we have already completed the request and sent the data to the client.
1176  // Hence, there's no need to send an error. However, since the bridge is potentially
1177  // in a bad state, we close the TCP socket to force the client to reconnect.
1178  return -1;
1179  }
1180 
1181  // We have finished
1182  readClosing = true;
1183  return 1;
1184 
1185  }
1186  // --------- READ or READV
1187 
1188  if ( readChunkList.size() == 1 ) {
1189  // Use a read request for single range
1190 
1191  long l;
1192  long long offs;
1193 
1194  // --------- READ
1195  memset(&xrdreq, 0, sizeof (xrdreq));
1196  xrdreq.read.requestid = htons(kXR_read);
1197  memcpy(xrdreq.read.fhandle, fhandle, 4);
1198  xrdreq.read.dlen = 0;
1199 
1200  offs = readChunkList[0].offset;
1201  l = readChunkList[0].size;
1202 
1203  xrdreq.read.offset = htonll(offs);
1204  xrdreq.read.rlen = htonl(l);
1205 
1206  // If we are using HTTPS or if the client requested trailers, or if the
1207  // read concerns a multirange reponse, disable sendfile
1208  // (in the latter two cases, the extra framing is only done in PostProcessHTTPReq)
1209  if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1211  if (!prot->Bridge->setSF((kXR_char *) fhandle, false)) {
1212  TRACE(REQ, " XrdBridge::SetSF(false) failed.");
1213 
1214  }
1215  }
1216 
1217 
1218 
1219  if (l <= 0) {
1220  if (l < 0) {
1221  TRACE(ALL, " Data sizes mismatch.");
1222  return -1;
1223  }
1224  else {
1225  TRACE(ALL, " No more bytes to send.");
1226  reset();
1227  return 1;
1228  }
1229  }
1230 
1231  if ((offs >= filesize) || (offs+l > filesize)) {
1232  httpStatusCode = 416;
1233  httpErrorBody = "Range Not Satisfiable";
1234  std::stringstream ss;
1235  ss << "Requested range " << l << "@" << offs << " is past the end of file (" << filesize << ")";
1236  return sendFooterError(ss.str());
1237  }
1238 
1239  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1240  generateWebdavErrMsg();
1241  return sendFooterError("Could not run read request on the bridge");
1242  }
1243  } else {
1244  // --------- READV
1245 
1246  length = ReqReadV(readChunkList);
1247 
1248  if (!prot->Bridge->Run((char *) &xrdreq, (char *) &ralist[0], length)) {
1249  generateWebdavErrMsg();
1250  return sendFooterError("Could not run ReadV request on the bridge");
1251  }
1252 
1253  }
1254 
1255  // We want to be invoked again after this request is finished
1256  return 0;
1257  } // case 3+
1258 
1259  } // switch (reqstate)
1260 
1261 
1262  } // case XrdHttpReq::rtGET
1263 
1264  case XrdHttpReq::rtPUT:
1265  {
1266  //if (prot->ishttps) {
1267  //prot->SendSimpleResp(501, NULL, NULL, (char *) "HTTPS not supported yet for direct writing. Sorry.", 0);
1268  //return -1;
1269  //}
1270 
1271  if (!fopened) {
1272 
1273  // --------- OPEN for write!
1274  memset(&xrdreq, 0, sizeof (ClientRequest));
1275  xrdreq.open.requestid = htons(kXR_open);
1276  l = resourceplusopaque.length() + 1;
1277  xrdreq.open.dlen = htonl(l);
1278  xrdreq.open.mode = htons(kXR_ur | kXR_uw | kXR_gw | kXR_gr | kXR_or);
1279  if (! XrdHttpProtocol::usingEC)
1281  else
1283 
1284  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1285  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, keepalive);
1286  return -1;
1287  }
1288 
1289 
1290  // We want to be invoked again after this request is finished
1291  // Only if there is data to fetch from the socket or there will
1292  // never be more data
1293  if (prot->BuffUsed() > 0 || (length == 0 && !sendcontinue))
1294  return 0;
1295 
1296  return 1;
1297 
1298  } else {
1299 
1300  if (m_transfer_encoding_chunked) {
1301  if (m_current_chunk_size == m_current_chunk_offset) {
1302  // Chunk has been consumed; we now must process the CRLF.
1303  // Note that we don't support trailer headers.
1304  if (prot->BuffUsed() < 2) return 1;
1305  if (prot->myBuffStart[0] != '\r' || prot->myBuffStart[1] != '\n') {
1306  prot->SendSimpleResp(400, NULL, NULL, (char *) "Invalid trailing chunk encoding.", 0, keepalive);
1307  return -1;
1308  }
1309  prot->BuffConsume(2);
1310  if (m_current_chunk_size == 0) {
1311  // All data has been sent. Turn off chunk processing and
1312  // set the bytes written and length appropriately; on next callback,
1313  // we will hit the close() block below.
1314  m_transfer_encoding_chunked = false;
1315  length = writtenbytes;
1316  return ProcessHTTPReq();
1317  }
1318  m_current_chunk_size = -1;
1319  m_current_chunk_offset = 0;
1320  // If there is more data, we try to process the next chunk; otherwise, return
1321  if (!prot->BuffUsed()) return 1;
1322  }
1323  if (-1 == m_current_chunk_size) {
1324 
1325  // Parse out the next chunk size.
1326  long long idx = 0;
1327  bool found_newline = false;
1328  // Set a maximum size of chunk we will allow
1329  // Nginx sets this to "NGX_MAX_OFF_T_VALUE", which is 9223372036854775807 (a some crazy number)
1330  // We set it to 1TB, which is 1099511627776
1331  // This is to prevent a malicious client from sending a very large chunk size
1332  // or a malformed chunk request.
1333  // 1TB in base-16 is 0x40000000000, so only allow 11 characters, plus the CRLF
1334  long long max_chunk_size_chars = std::min(static_cast<long long>(prot->BuffUsed()), static_cast<long long>(13));
1335  for (; idx < max_chunk_size_chars; idx++) {
1336  if (prot->myBuffStart[idx] == '\n') {
1337  found_newline = true;
1338  break;
1339  }
1340  }
1341  // If we found a new line, but it is the first character in the buffer (no chunk length)
1342  // or if the previous character is not a CR.
1343  if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] != '\r')) {
1344  prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1345  TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1346  return -1;
1347  }
1348  if (found_newline) {
1349  char *endptr = NULL;
1350  std::string line_contents(prot->myBuffStart, idx);
1351  long long chunk_contents = strtoll(line_contents.c_str(), &endptr, 16);
1352  // Chunk sizes can be followed by trailer information or CRLF
1353  if (*endptr != ';' && *endptr != '\r') {
1354  prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1355  TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1356  return -1;
1357  }
1358  m_current_chunk_size = chunk_contents;
1359  m_current_chunk_offset = 0;
1360  prot->BuffConsume(idx + 1);
1361  TRACE(REQ, "XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size << " bytes");
1362  } else {
1363  // Need more data!
1364  return 1;
1365  }
1366  }
1367 
1368  if (m_current_chunk_size == 0) {
1369  // All data has been sent. Invoke this routine again immediately to process CRLF
1370  return ProcessHTTPReq();
1371  } else {
1372  // At this point, we have a chunk size defined and should consume payload data
1373  memset(&xrdreq, 0, sizeof (xrdreq));
1374  xrdreq.write.requestid = htons(kXR_write);
1375  memcpy(xrdreq.write.fhandle, fhandle, 4);
1376 
1377  long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1378  long long bytes_to_write = std::min(static_cast<long long>(prot->BuffUsed()),
1379  chunk_bytes_remaining);
1380 
1381  xrdreq.write.offset = htonll(writtenbytes);
1382  xrdreq.write.dlen = htonl(bytes_to_write);
1383 
1384  TRACEI(REQ, "XrdHTTP PUT: Writing chunk of size " << bytes_to_write << " starting with '" << *(prot->myBuffStart) << "'" << " with " << chunk_bytes_remaining << " bytes remaining in the chunk");
1385  if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_write)) {
1386  generateWebdavErrMsg();
1387  return sendFooterError("Could not run write request on the bridge");
1388  }
1389  // If there are more bytes in the buffer, then immediately call us after the
1390  // write is finished; otherwise, wait for data.
1391  return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1392  }
1393  } else if (writtenbytes < length) {
1394 
1395 
1396  // --------- WRITE
1397  memset(&xrdreq, 0, sizeof (xrdreq));
1398  xrdreq.write.requestid = htons(kXR_write);
1399  memcpy(xrdreq.write.fhandle, fhandle, 4);
1400 
1401  long long bytes_to_read = std::min(static_cast<long long>(prot->BuffUsed()),
1402  length - writtenbytes);
1403 
1404  xrdreq.write.offset = htonll(writtenbytes);
1405  xrdreq.write.dlen = htonl(bytes_to_read);
1406 
1407  TRACEI(REQ, "Writing " << bytes_to_read);
1408  if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_read)) {
1409  generateWebdavErrMsg();
1410  return sendFooterError("Could not run write request on the bridge");
1411  }
1412 
1413  if (writtenbytes + prot->BuffUsed() >= length)
1414  // Trigger an immediate recall after this request has finished
1415  return 0;
1416  else
1417  // We want to be invoked again after this request is finished
1418  // only if there is pending data
1419  return 1;
1420 
1421 
1422 
1423  } else {
1424 
1425  // --------- CLOSE
1426  memset(&xrdreq, 0, sizeof (ClientRequest));
1427  xrdreq.close.requestid = htons(kXR_close);
1428  memcpy(xrdreq.close.fhandle, fhandle, 4);
1429 
1430 
1431  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1432  generateWebdavErrMsg();
1433  return sendFooterError("Could not run close request on the bridge");
1434  }
1435 
1436  // We have finished
1437  return 1;
1438 
1439  }
1440 
1441  }
1442 
1443  break;
1444 
1445  }
1446  case XrdHttpReq::rtOPTIONS:
1447  {
1448  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);
1449  bool ret_keepalive = keepalive; // reset() clears keepalive
1450  reset();
1451  return ret_keepalive ? 1 : -1;
1452  }
1453  case XrdHttpReq::rtDELETE:
1454  {
1455 
1456 
1457  switch (reqstate) {
1458 
1459  case 0: // Stat()
1460  {
1461 
1462 
1463  // --------- STAT is always the first step
1464  memset(&xrdreq, 0, sizeof (ClientRequest));
1465  xrdreq.stat.requestid = htons(kXR_stat);
1466  std::string s = resourceplusopaque.c_str();
1467 
1468 
1469  l = resourceplusopaque.length() + 1;
1470  xrdreq.stat.dlen = htonl(l);
1471 
1472  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1473  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1474  return -1;
1475  }
1476 
1477  // We need to be invoked again to complete the request
1478  return 0;
1479  }
1480  default:
1481 
1482  if (fileflags & kXR_isDir) {
1483  // --------- RMDIR
1484  memset(&xrdreq, 0, sizeof (ClientRequest));
1485  xrdreq.rmdir.requestid = htons(kXR_rmdir);
1486 
1487  std::string s = resourceplusopaque.c_str();
1488 
1489  l = s.length() + 1;
1490  xrdreq.rmdir.dlen = htonl(l);
1491 
1492  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1493  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rmdir request.", 0, false);
1494  return -1;
1495  }
1496  } else {
1497  // --------- DELETE
1498  memset(&xrdreq, 0, sizeof (ClientRequest));
1499  xrdreq.rm.requestid = htons(kXR_rm);
1500 
1501  std::string s = resourceplusopaque.c_str();
1502 
1503  l = s.length() + 1;
1504  xrdreq.rm.dlen = htonl(l);
1505 
1506  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1507  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rm request.", 0, false);
1508  return -1;
1509  }
1510  }
1511 
1512 
1513  // We don't want to be invoked again after this request is finished
1514  return 1;
1515 
1516  }
1517 
1518 
1519 
1520  }
1521  case XrdHttpReq::rtPATCH:
1522  {
1523  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported yet.", 0, false);
1524 
1525  return -1;
1526  }
1528  {
1529 
1530 
1531 
1532  switch (reqstate) {
1533 
1534  case 0: // Stat() and add the current item to the list of the things to send
1535  {
1536 
1537  if (length > 0) {
1538  TRACE(REQ, "Reading request body " << length << " bytes.");
1539  char *p = 0;
1540  // We have to specifically read all the request body
1541 
1542  if (prot->BuffgetData(length, &p, true) < length) {
1543  prot->SendSimpleResp(501, NULL, NULL, (char *) "Error in getting the PROPFIND request body.", 0, false);
1544  return -1;
1545  }
1546 
1547  if ((depth > 1) || (depth < 0)) {
1548  prot->SendSimpleResp(501, NULL, NULL, (char *) "Invalid depth value.", 0, false);
1549  return -1;
1550  }
1551 
1552 
1553  parseBody(p, length);
1554  }
1555 
1556 
1557  // --------- STAT is always the first step
1558  memset(&xrdreq, 0, sizeof (ClientRequest));
1559  xrdreq.stat.requestid = htons(kXR_stat);
1560  std::string s = resourceplusopaque.c_str();
1561 
1562 
1563  l = resourceplusopaque.length() + 1;
1564  xrdreq.stat.dlen = htonl(l);
1565 
1566  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1567  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1568  return -1;
1569  }
1570 
1571 
1572  if (depth == 0) {
1573  // We don't need to be invoked again
1574  return 1;
1575  } else
1576  // We need to be invoked again to complete the request
1577  return 0;
1578 
1579 
1580 
1581  break;
1582  }
1583 
1584  default: // Dirlist()
1585  {
1586 
1587  // --------- DIRLIST
1588  memset(&xrdreq, 0, sizeof (ClientRequest));
1590 
1591  std::string s = resourceplusopaque.c_str();
1593  //s += "?xrd.dirstat=1";
1594 
1595  l = s.length() + 1;
1596  xrdreq.dirlist.dlen = htonl(l);
1597 
1598  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1599  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1600  return -1;
1601  }
1602 
1603  // We don't want to be invoked again after this request is finished
1604  return 1;
1605  }
1606  }
1607 
1608 
1609  break;
1610  }
1611  case XrdHttpReq::rtMKCOL:
1612  {
1613 
1614  // --------- MKDIR
1615  memset(&xrdreq, 0, sizeof (ClientRequest));
1616  xrdreq.mkdir.requestid = htons(kXR_mkdir);
1617 
1618  std::string s = resourceplusopaque.c_str();
1620 
1621  l = s.length() + 1;
1622  xrdreq.mkdir.dlen = htonl(l);
1623 
1624  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1625  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1626  return -1;
1627  }
1628 
1629  // We don't want to be invoked again after this request is finished
1630  return 1;
1631  }
1632  case XrdHttpReq::rtMOVE:
1633  {
1634  // Skip the protocol part of destination URL
1635  size_t skip = destination.find("://");
1636  skip = (skip == std::string::npos) ? 0 : skip + 3;
1637 
1638  // If we have a manager role, enforce source and destination are on the same host
1639  if (prot->myRole == kXR_isManager && destination.compare(skip, host.size(), host) != 0) {
1640  prot->SendSimpleResp(501, NULL, NULL, (char *) "Only in-place renaming is supported for MOVE.", 0, false);
1641  return -1;
1642  }
1643 
1644  // If needed, append opaque info from source onto destination
1645  int pos = resourceplusopaque.find("?");
1646  if (pos != STR_NPOS) {
1647  destination.append((destination.find("?") == std::string::npos) ? "?" : "&");
1648  destination.append(resourceplusopaque.c_str() + pos + 1);
1649  }
1650 
1651  size_t path_pos = destination.find('/', skip + 1);
1652 
1653  if (path_pos == std::string::npos) {
1654  prot->SendSimpleResp(400, NULL, NULL, (char *) "Cannot determine destination path", 0, false);
1655  return -1;
1656  }
1657 
1658  // Construct args to kXR_mv request (i.e. <src> + " " + <dst>)
1659  std::string mv_args = std::string(resourceplusopaque.c_str()) + " " + destination.substr(path_pos);
1660 
1661  l = mv_args.length() + 1;
1662 
1663  // Prepare and run kXR_mv request
1664  memset(&xrdreq, 0, sizeof (ClientRequest));
1665  xrdreq.mv.requestid = htons(kXR_mv);
1667  xrdreq.mv.dlen = htonl(l);
1668 
1669  if (!prot->Bridge->Run((char *) &xrdreq, (char *) mv_args.c_str(), l)) {
1670  prot->SendSimpleResp(500, NULL, NULL, (char *) "Could not run request.", 0, false);
1671  return -1;
1672  }
1673 
1674  // We don't want to be invoked again after this request is finished
1675  return 1;
1676  }
1677  default:
1678  {
1679  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported.", 0, false);
1680  return -1;
1681  }
1682 
1683  }
1684 
1685  return 1;
1686 }
1687 
1688 
1689 int
1690 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1691  if (iovN > 0) {
1692  if (xrdresp == kXR_error) {
1693  prot->SendSimpleResp(httpStatusCode, NULL, NULL, "Failed to determine checksum", 0, false);
1694  return -1;
1695  }
1696 
1697  TRACEI(REQ, "Checksum for HEAD " << resource.c_str() << " "
1698  << reinterpret_cast<char *>(iovP[0].iov_base) << "="
1699  << reinterpret_cast<char *>(iovP[iovN-1].iov_base));
1700 
1701  bool convert_to_base64 = m_req_cksum->needsBase64Padding();
1702  char *digest_value = reinterpret_cast<char *>(iovP[iovN-1].iov_base);
1703  if (convert_to_base64) {
1704  size_t digest_length = strlen(digest_value);
1705  unsigned char *digest_binary_value = (unsigned char *)malloc(digest_length);
1706  if (!Fromhexdigest(reinterpret_cast<unsigned char *>(digest_value), digest_length, digest_binary_value)) {
1707  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to convert checksum hexdigest to base64.", 0, false);
1708  free(digest_binary_value);
1709  return -1;
1710  }
1711  char *digest_base64_value = (char *)malloc(digest_length + 1);
1712  // Binary length is precisely half the size of the hex-encoded digest_value; hence, divide length by 2.
1713  Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1714  free(digest_binary_value);
1715  digest_value = digest_base64_value;
1716  }
1717 
1718  digest_header = "Digest: ";
1719  digest_header += m_req_cksum->getHttpName();
1720  digest_header += "=";
1721  digest_header += digest_value;
1722  if (convert_to_base64) {free(digest_value);}
1723  return 0;
1724  } else {
1725  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1726  return -1;
1727  }
1728 }
1729 
1730 int
1731 XrdHttpReq::PostProcessListing(bool final_) {
1732 
1733  if (xrdresp == kXR_error) {
1734  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1735  httpErrorBody.c_str(), httpErrorBody.length(), false);
1736  return -1;
1737  }
1738 
1739  if (stringresp.empty()) {
1740  // Start building the HTML response
1741  stringresp = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1742  "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1743  "<head>\n"
1744  "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1745  "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1746  "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1747 
1748  stringresp += "<title>";
1749  stringresp += resource.c_str();
1750  stringresp += "</title>\n";
1751 
1752  stringresp += "</head>\n"
1753  "<body>\n";
1754 
1755  char *estr = escapeXML(resource.c_str());
1756 
1757  stringresp += "<h1>Listing of: ";
1758  stringresp += estr;
1759  stringresp += "</h1>\n";
1760 
1761  free(estr);
1762 
1763  stringresp += "<div id=\"header\">";
1764 
1765  stringresp += "<table id=\"ft\">\n"
1766  "<thead><tr>\n"
1767  "<th class=\"mode\">Mode</th>"
1768  "<th class=\"flags\">Flags</th>"
1769  "<th class=\"size\">Size</th>"
1770  "<th class=\"datetime\">Modified</th>"
1771  "<th class=\"name\">Name</th>"
1772  "</tr></thead>\n";
1773  }
1774 
1775  // Now parse the answer building the entries vector
1776  if (iovN > 0) {
1777  char *startp = (char *) iovP[0].iov_base, *endp = 0;
1778  char entry[1024];
1779  DirListInfo e;
1780  while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) {
1781  // Find the filename, it comes before the \n
1782  if ((endp = (char *) strchr((const char*) startp, '\n'))) {
1783  strncpy(entry, (char *) startp, endp - startp);
1784  entry[endp - startp] = 0;
1785  e.path = entry;
1786 
1787  endp++;
1788 
1789  // Now parse the stat info
1790  TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry
1791  << " stat=" << endp);
1792 
1793  long dummyl;
1794  sscanf(endp, "%ld %lld %ld %ld",
1795  &dummyl,
1796  &e.size,
1797  &e.flags,
1798  &e.modtime);
1799  } else
1800  strcpy(entry, (char *) startp);
1801 
1802  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
1803  // The entry is filled. <td class="ft-file"><a href="file1.txt">file1.txt</a></td>
1804  std::string p = "<tr>"
1805  "<td class=\"mode\">";
1806 
1807  if (e.flags & kXR_isDir) p += "d";
1808  else p += "-";
1809 
1810  if (e.flags & kXR_other) p += "o";
1811  else p += "-";
1812 
1813  if (e.flags & kXR_offline) p += "O";
1814  else p += "-";
1815 
1816  if (e.flags & kXR_readable) p += "r";
1817  else p += "-";
1818 
1819  if (e.flags & kXR_writable) p += "w";
1820  else p += "-";
1821 
1822  if (e.flags & kXR_xset) p += "x";
1823  else p += "-";
1824 
1825  p += "</td>";
1826  p += "<td class=\"mode\">" + itos(e.flags) + "</td>"
1827  "<td class=\"size\">" + itos(e.size) + "</td>"
1828  "<td class=\"datetime\">" + ISOdatetime(e.modtime) + "</td>"
1829  "<td class=\"name\">"
1830  "<a href=\"";
1831 
1832  if (resource != "/") {
1833 
1834  char *estr = escapeXML(resource.c_str());
1835 
1836  p += estr;
1837  if (!p.empty() && p[p.size() - 1] != '/')
1838  p += "/";
1839 
1840  free(estr);
1841  }
1842  std::unique_ptr<char, decltype(&free)> estr(escapeXML(e.path.c_str()), &free);
1843  p += estr.get();
1844  if (e.flags & kXR_isDir) p += "/";
1845  p += "\">";
1846  p += estr.get();
1847  if (e.flags & kXR_isDir) p += "/";
1848  p += "</a></td></tr>";
1849 
1850  stringresp += p;
1851  }
1852 
1853  if (endp) {
1854  char *pp = (char *)strchr((const char *)endp, '\n');
1855  if (pp) startp = pp+1;
1856  else break;
1857  } else break;
1858 
1859  }
1860  }
1861 
1862  // If this was the last bunch of entries, send the buffer and empty it immediately
1863  if (final_) {
1864  stringresp += "</table></div><br><br><hr size=1>"
1865  "<p><span id=\"requestby\">Request by ";
1866 
1867  if (prot->SecEntity.name)
1868  stringresp += prot->SecEntity.name;
1869  else
1870  stringresp += prot->Link->ID;
1871 
1872  if (prot->SecEntity.vorg ||
1873  prot->SecEntity.name ||
1874  prot->SecEntity.moninfo ||
1875  prot->SecEntity.role)
1876  stringresp += " (";
1877 
1878  if (prot->SecEntity.vorg) {
1879  stringresp += " VO: ";
1880  stringresp += prot->SecEntity.vorg;
1881  }
1882 
1883  if (prot->SecEntity.moninfo) {
1884  stringresp += " DN: ";
1885  stringresp += prot->SecEntity.moninfo;
1886  } else
1887  if (prot->SecEntity.name) {
1888  stringresp += " DN: ";
1889  stringresp += prot->SecEntity.name;
1890  }
1891 
1892  if (prot->SecEntity.role) {
1893  stringresp += " Role: ";
1894  stringresp += prot->SecEntity.role;
1895  if (prot->SecEntity.endorsements) {
1896  stringresp += " (";
1898  stringresp += ") ";
1899  }
1900  }
1901 
1902  if (prot->SecEntity.vorg ||
1903  prot->SecEntity.moninfo ||
1904  prot->SecEntity.role)
1905  stringresp += " )";
1906 
1907  if (prot->SecEntity.host) {
1908  stringresp += " ( ";
1909  stringresp += prot->SecEntity.host;
1910  stringresp += " )";
1911  }
1912 
1913  stringresp += "</span></p>\n";
1914  stringresp += "<p>Powered by XrdHTTP ";
1915  stringresp += XrdVSTRING;
1916  stringresp += " (CERN IT-SDC)</p>\n";
1917 
1918  prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive);
1919  stringresp.clear();
1920  return keepalive ? 1 : -1;
1921  }
1922 
1923  return 0;
1924 }
1925 
1926 int
1927 XrdHttpReq::ReturnGetHeaders() {
1928  std::string responseHeader;
1929  if (!m_digest_header.empty()) {
1930  responseHeader = m_digest_header;
1931  }
1932  if (fileflags & kXR_cachersp) {
1933  if (!responseHeader.empty()) {
1934  responseHeader += "\r\n";
1935  }
1936  addAgeHeader(responseHeader);
1937  }
1938 
1940  if (uranges.empty() && readRangeHandler.getError()) {
1941  prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false);
1942  return -1;
1943  }
1944 
1945  if (readRangeHandler.isFullFile()) {
1946  // Full file.
1947  TRACEI(REQ, "Sending full file: " << filesize);
1948  if (m_transfer_encoding_chunked && m_trailer_headers) {
1949  setTransferStatusHeader(responseHeader);
1950  prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive);
1951  } else {
1952  prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive);
1953  }
1954  return 0;
1955  }
1956 
1958  // Possibly with zero sized file but should have been included
1959  // in the FullFile case above
1960  if (uranges.size() != 1)
1961  return -1;
1962 
1963  // Only one range to return to the user
1964  char buf[64];
1965  const off_t cnt = uranges[0].end - uranges[0].start + 1;
1966 
1967  std::string header = "Content-Range: bytes ";
1968  sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize);
1969  header += buf;
1970  if (!responseHeader.empty()) {
1971  header += "\r\n";
1972  header += responseHeader.c_str();
1973  }
1974 
1975  if (m_transfer_encoding_chunked && m_trailer_headers) {
1976  setTransferStatusHeader(header);
1977  prot->StartChunkedResp(206, NULL, header.empty() ? nullptr : header.c_str(), -1, keepalive);
1978  } else {
1979  prot->SendSimpleResp(206, NULL, header.empty() ? nullptr : header.c_str(), NULL, cnt, keepalive);
1980  }
1981  return 0;
1982  }
1983 
1984  // Multiple reads to perform, compose and send the header
1985  off_t cnt = 0;
1986  for (auto &ur : uranges) {
1987  cnt += ur.end - ur.start + 1;
1988 
1989  cnt += buildPartialHdr(ur.start,
1990  ur.end,
1991  filesize,
1992  (char *) "123456").size();
1993 
1994  }
1995  cnt += buildPartialHdrEnd((char *) "123456").size();
1996  std::string header = "Content-Type: multipart/byteranges; boundary=123456";
1997  if (!m_digest_header.empty()) {
1998  header += "\n";
1999  header += m_digest_header;
2000  }
2001  if (fileflags & kXR_cachersp) {
2002  if (!header.empty()) {
2003  header += "\r\n";
2004  }
2005  addAgeHeader(header);
2006  }
2007 
2008  if (m_transfer_encoding_chunked && m_trailer_headers) {
2009  setTransferStatusHeader(header);
2010  prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive);
2011  } else {
2012  prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive);
2013  }
2014  return 0;
2015 }
2016 
2017 void XrdHttpReq::setTransferStatusHeader(std::string &header) {
2018  if (m_status_trailer) {
2019  if (header.empty()) {
2020  header += "Trailer: X-Transfer-Status";
2021  } else {
2022  header += "\r\nTrailer: X-Transfer-Status";
2023  }
2024  }
2025 }
2026 
2027 // This is invoked by the callbacks, after something has happened in the bridge
2028 
2029 int XrdHttpReq::PostProcessHTTPReq(bool final_) {
2030 
2031  TRACEI(REQ, "PostProcessHTTPReq req: " << request << " reqstate: " << reqstate << " final_:" << final_);
2032  generateWebdavErrMsg();
2033 
2034  if(xrdreq.set.requestid == htons(kXR_set)) {
2035  // We have set the user agent, if it fails we return a 500 error, otherwise the callback is successful --> we continue
2036  if(xrdresp != kXR_ok) {
2037  prot->SendSimpleResp(500, nullptr, nullptr, "Could not set user agent.", 0, false);
2038  return -1;
2039  }
2040  return 0;
2041  }
2042 
2043  switch (request) {
2044  case XrdHttpReq::rtUnknown:
2045  {
2046  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 1", 0, false);
2047  return -1;
2048  }
2050  {
2051  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 2", 0, false);
2052  return -1;
2053  }
2054  case XrdHttpReq::rtHEAD:
2055  {
2056  if (xrdresp != kXR_ok) {
2057  // NOTE that HEAD MUST NOT return a body, even in the case of failure.
2058  prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, false);
2059  return -1;
2060  } else if (reqstate == 0) {
2061  if (iovN > 0) {
2062  std::string response_headers;
2063 
2064  // Now parse the stat info
2065  TRACEI(REQ, "Stat for HEAD " << resource.c_str()
2066  << " stat=" << (char *) iovP[0].iov_base);
2067 
2068  long dummyl;
2069  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2070  &dummyl,
2071  &filesize,
2072  &fileflags,
2073  &filemodtime);
2074 
2075  if (m_req_digest.size()) {
2076  return 0;
2077  } else {
2078  if (fileflags & kXR_cachersp) {
2079  addAgeHeader(response_headers);
2080  response_headers += "\r\n";
2081  }
2082  response_headers += "Accept-Ranges: bytes";
2083  prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2084  return keepalive ? 1 : -1;
2085  }
2086  }
2087 
2088  prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, keepalive);
2089  bool ret_keepalive = keepalive; // reset() clears keepalive
2090  reset();
2091  return ret_keepalive ? 1 : -1;
2092  } else { // We requested a checksum and now have its response.
2093  if (iovN > 0) {
2094  std::string response_headers;
2095  int response = PostProcessChecksum(response_headers);
2096  if (-1 == response) {
2097  return -1;
2098  }
2099  if (!response_headers.empty()) {response_headers += "\r\n";}
2100  if (fileflags & kXR_cachersp) {
2101  addAgeHeader(response_headers);
2102  response_headers += "\r\n";
2103  }
2104  response_headers += "Accept-Ranges: bytes";
2105  prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2106  return keepalive ? 1 : -1;
2107  } else {
2108  prot->SendSimpleResp(500, NULL, NULL, "Underlying filesystem failed to calculate checksum.", 0, false);
2109  return -1;
2110  }
2111  }
2112  }
2113  case XrdHttpReq::rtGET:
2114  {
2115  // To duplicate the state diagram from the rtGET request state
2116  // - 0: Perform an open request
2117  // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
2118  // - 2: Perform a close (for directory listings only)
2119  // - 3: Perform a dirlist
2120  // - 4+: Reads from file; if at end, perform a close.
2121  switch (reqstate) {
2122  case 0: // open
2123  {
2124  if (xrdresp == kXR_ok) {
2125  fopened = true;
2126  getfhandle();
2127 
2128  // Always try to parse response. In the case of a caching proxy, the open
2129  // will have created the file in cache
2130  if (iovP[1].iov_len > 1) {
2131  TRACEI(REQ, "Stat for GET " << resource.c_str()
2132  << " stat=" << (char *) iovP[1].iov_base);
2133 
2134  long dummyl;
2135  sscanf((const char *) iovP[1].iov_base, "%ld %lld %ld %ld",
2136  &dummyl,
2137  &filesize,
2138  &fileflags,
2139  &filemodtime);
2140 
2141  // If this is a directory, bail out early; we will close the file handle
2142  // and then issue a directory listing.
2143  if (fileflags & kXR_isDir) {
2144  return 0;
2145  }
2146 
2148 
2149  // As above: if the client specified a response size, we use that.
2150  // Otherwise, utilize the filesize
2151  if (!length) {
2152  length = filesize;
2153  }
2154  }
2155  else {
2156  TRACEI(ALL, "GET returned no STAT information. Internal error?");
2157  prot->SendSimpleResp(500, NULL, NULL, "Storage system did not return stat info.", 0, false);
2158  return -1;
2159  }
2160  return 0;
2161  } else if (xrderrcode == kXR_isDirectory) { // This is a directory; trigger directory-handling topic.
2162  fileflags = kXR_isDir;
2163  return 0;
2164  } else { // xrdresp indicates an error occurred
2165 
2166  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2167  httpErrorBody.c_str(), httpErrorBody.length(), false);
2168  return -1;
2169  }
2170  // Case should not be reachable
2171  return -1;
2172  } // end open
2173  case 1: // checksum was requested and now we have its response.
2174  {
2175  return PostProcessChecksum(m_digest_header);
2176  }
2177  case 2: // close file handle in case of the directory
2178  {
2179  if (xrdresp != kXR_ok) {
2180  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2181  httpErrorBody.c_str(), httpErrorBody.length(), false);
2182  return -1;
2183  }
2184  return 0;
2185  }
2186  case 3: // handle the directory listing response
2187  {
2188  return PostProcessListing(final_);
2189  }
2190  default: //read or readv, followed by a close.
2191  {
2192  // If we are postprocessing a close, potentially send out informational trailers
2193  if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing)
2194  {
2195  // If we already sent out an error, then we cannot send any further
2196  // messages
2197  if (closeAfterError) {
2198  TRACEI(REQ, "Close was completed after an error: " << xrdresp);
2199  return xrdresp != kXR_ok ? -1 : 1;
2200  }
2201 
2203  if (rrerror) {
2204  httpStatusCode = rrerror.httpRetCode;
2205  httpErrorBody = rrerror.errMsg;
2206  }
2207 
2208  if (m_transfer_encoding_chunked && m_trailer_headers) {
2209  if (prot->ChunkRespHeader(0))
2210  return -1;
2211 
2212  const std::string crlf = "\r\n";
2213  std::stringstream ss;
2214  ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpErrorBody << crlf;
2215 
2216  const auto header = ss.str();
2217  if (prot->SendData(header.c_str(), header.size()))
2218  return -1;
2219 
2220  if (prot->ChunkRespFooter())
2221  return -1;
2222  }
2223 
2224  if (rrerror) return -1;
2225  return keepalive ? 1 : -1;
2226  }
2227 
2228  // On error, we can only send out a message if trailers are enabled and the
2229  // status response in trailer behavior is requested.
2230  if (xrdresp == kXR_error) {
2231  auto rc = sendFooterError("");
2232  if (rc == 1) {
2233  closeAfterError = true;
2234  return 0;
2235  }
2236  return -1;
2237  }
2238 
2239 
2240  TRACEI(REQ, "Got data vectors to send:" << iovN);
2241 
2242  XrdHttpIOList received;
2243  getReadResponse(received);
2244 
2245  int rc;
2247  rc = sendReadResponseSingleRange(received);
2248  } else {
2249  rc = sendReadResponsesMultiRanges(received);
2250  }
2251  if (rc) {
2252  // make sure readRangeHandler will trigger close
2253  // of file after next NextReadList().
2255  }
2256 
2257  return 0;
2258  } // end read or readv
2259 
2260  } // switch reqstate
2261  break;
2262  } // case GET
2263 
2264  case XrdHttpReq::rtPUT:
2265  {
2266  if (!fopened) {
2267  if (xrdresp != kXR_ok) {
2268  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2269  return -1;
2270  }
2271 
2272  getfhandle();
2273  fopened = true;
2274 
2275  // We try to completely fill up our buffer before flushing
2276  prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2277 
2278  if (sendcontinue) {
2279  prot->SendSimpleResp(100, NULL, NULL, 0, 0, keepalive);
2280  return 0;
2281  }
2282 
2283  break;
2284  } else {
2285 
2286  // If we are here it's too late to send a proper error message...
2287  // However, we decide to send a response anyway before we close the connection
2288  // We are not sure if sending a final response before reading the entire request
2289  if (xrdresp == kXR_error) {
2290  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2291  return -1;
2292  }
2293 
2294  if (ntohs(xrdreq.header.requestid) == kXR_write) {
2295  int l = ntohl(xrdreq.write.dlen);
2296 
2297  // Consume the written bytes
2298  prot->BuffConsume(ntohl(xrdreq.write.dlen));
2299  writtenbytes += l;
2300 
2301  // Update the chunk offset
2302  if (m_transfer_encoding_chunked) {
2303  m_current_chunk_offset += l;
2304  }
2305 
2306  // We try to completely fill up our buffer before flushing
2307  prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2308 
2309  return 0;
2310  }
2311 
2312  if (ntohs(xrdreq.header.requestid) == kXR_close) {
2313  if (xrdresp == kXR_ok) {
2314  prot->SendSimpleResp(201, NULL, NULL, (char *)":-)", 0, keepalive);
2315  return keepalive ? 1 : -1;
2316  } else {
2317  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2318  return -1;
2319  }
2320  }
2321  }
2322 
2323 
2324 
2325 
2326 
2327  break;
2328  }
2329 
2330 
2331 
2332  case XrdHttpReq::rtDELETE:
2333  {
2334 
2335  if (xrdresp != kXR_ok) {
2336  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2337  httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2338  return -1;
2339  }
2340 
2341 
2342 
2343 
2344  switch (reqstate) {
2345 
2346  case 0: // response to stat()
2347  {
2348  if (iovN > 0) {
2349 
2350  // Now parse the stat info
2351  TRACEI(REQ, "Stat for removal " << resource.c_str()
2352  << " stat=" << (char *) iovP[0].iov_base);
2353 
2354  long dummyl;
2355  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2356  &dummyl,
2357  &filesize,
2358  &fileflags,
2359  &filemodtime);
2360  }
2361 
2362  return 0;
2363  }
2364  default: // response to rm
2365  {
2366  if (xrdresp == kXR_ok) {
2367  prot->SendSimpleResp(200, NULL, NULL, (char *) ":-)", 0, keepalive);
2368  return keepalive ? 1 : -1;
2369  }
2370  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2371  httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2372  return -1;
2373  }
2374  }
2375 
2376 
2377  }
2378 
2380  {
2381 
2382  if (xrdresp == kXR_error) {
2383  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2384  httpErrorBody.c_str(), httpErrorBody.length(), false);
2385  return -1;
2386  }
2387 
2388  switch (reqstate) {
2389 
2390  case 0: // response to stat()
2391  {
2392  DirListInfo e;
2393  e.size = 0;
2394  e.flags = 0;
2395 
2396  // Now parse the answer building the entries vector
2397  if (iovN > 0) {
2398  e.path = resource.c_str();
2399 
2400  // Now parse the stat info
2401  TRACEI(REQ, "Collection " << resource.c_str()
2402  << " stat=" << (char *) iovP[0].iov_base);
2403 
2404  long dummyl;
2405  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2406  &dummyl,
2407  &e.size,
2408  &e.flags,
2409  &e.modtime);
2410 
2411  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2412  /* The entry is filled. */
2413 
2414 
2415  std::string p;
2416  stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2417 
2418  char *estr = escapeXML(e.path.c_str());
2419 
2420  stringresp += "<D:href>";
2421  stringresp += estr;
2422  stringresp += "</D:href>\n";
2423 
2424  free(estr);
2425 
2426  stringresp += "<D:propstat>\n<D:prop>\n";
2427 
2428  // Now add the properties that we have to add
2429 
2430  // File size
2431  stringresp += "<lp1:getcontentlength>";
2432  stringresp += itos(e.size);
2433  stringresp += "</lp1:getcontentlength>\n";
2434 
2435 
2436 
2437  stringresp += "<lp1:getlastmodified>";
2439  stringresp += "</lp1:getlastmodified>\n";
2440 
2441 
2442 
2443  if (e.flags & kXR_isDir) {
2444  stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2445  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2446  } else {
2447  stringresp += "<lp1:resourcetype/>\n";
2448  stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2449  }
2450 
2451  if (e.flags & kXR_xset) {
2452  stringresp += "<lp1:executable>T</lp1:executable>\n";
2453  } else {
2454  stringresp += "<lp1:executable>F</lp1:executable>\n";
2455  }
2456 
2457 
2458 
2459  stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2460 
2461 
2462  }
2463 
2464 
2465  }
2466 
2467  // If this was the last bunch of entries, send the buffer and empty it immediately
2468  if ((depth == 0) || !(e.flags & kXR_isDir)) {
2469  std::string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:multistatus xmlns:D=\"DAV:\" xmlns:ns1=\"http://apache.org/dav/props/\" xmlns:ns0=\"DAV:\">\n";
2470  stringresp.insert(0, s);
2471  stringresp += "</D:multistatus>\n";
2472  prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2473  (char *) stringresp.c_str(), stringresp.length(), keepalive);
2474  stringresp.clear();
2475  return keepalive ? 1 : -1;
2476  }
2477 
2478  break;
2479  }
2480  default: // response to dirlist()
2481  {
2482 
2483 
2484  // Now parse the answer building the entries vector
2485  if (iovN > 0) {
2486  char *startp = (char *) iovP[0].iov_base, *endp = 0;
2487  char entry[1024];
2488  DirListInfo e;
2489 
2490  while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)(iovP[0].iov_len - 1) ) {
2491  // Find the filename, it comes before the \n
2492  if ((endp = (char *) mystrchrnul((const char*) startp, '\n'))) {
2493  strncpy(entry, (char *) startp, endp - startp);
2494  entry[endp - startp] = 0;
2495  e.path = entry;
2496 
2497  endp++;
2498 
2499  // Now parse the stat info
2500  TRACEI(REQ, "Dirlist " <<resource.c_str() <<" entry=" <<entry
2501  << " stat=" << endp);
2502 
2503  long dummyl;
2504  sscanf(endp, "%ld %lld %ld %ld",
2505  &dummyl,
2506  &e.size,
2507  &e.flags,
2508  &e.modtime);
2509  }
2510 
2511 
2512  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2513  /* The entry is filled.
2514 
2515  <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/" xmlns:lp3="LCGDM:">
2516  <D:href>/dpm/cern.ch/home/testers2.eu-emi.eu/</D:href>
2517  <D:propstat>
2518  <D:prop>
2519  <lp1:getcontentlength>1</lp1:getcontentlength>
2520  <lp1:getlastmodified>Tue, 01 May 2012 02:42:13 GMT</lp1:getlastmodified>
2521  <lp1:resourcetype>
2522  <D:collection/>
2523  </lp1:resourcetype>
2524  </D:prop>
2525  <D:status>HTTP/1.1 200 OK</D:status>
2526  </D:propstat>
2527  </D:response>
2528  */
2529 
2530 
2531  std::string p = resource.c_str();
2532  if (*p.rbegin() != '/') p += "/";
2533 
2534  p += e.path;
2535 
2536  stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2537 
2538  char *estr = escapeXML(p.c_str());
2539  stringresp += "<D:href>";
2540  stringresp += estr;
2541  stringresp += "</D:href>\n";
2542  free(estr);
2543 
2544  stringresp += "<D:propstat>\n<D:prop>\n";
2545 
2546 
2547 
2548  // Now add the properties that we have to add
2549 
2550  // File size
2551  stringresp += "<lp1:getcontentlength>";
2552  stringresp += itos(e.size);
2553  stringresp += "</lp1:getcontentlength>\n";
2554 
2555  stringresp += "<lp1:getlastmodified>";
2557  stringresp += "</lp1:getlastmodified>\n";
2558 
2559  if (e.flags & kXR_isDir) {
2560  stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2561  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2562  } else {
2563  stringresp += "<lp1:resourcetype/>\n";
2564  stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2565  }
2566 
2567  if (e.flags & kXR_xset) {
2568  stringresp += "<lp1:executable>T</lp1:executable>\n";
2569  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2570  } else {
2571  stringresp += "<lp1:executable>F</lp1:executable>\n";
2572  }
2573 
2574  stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2575 
2576 
2577  }
2578 
2579 
2580 
2581  if (endp) {
2582  char *pp = (char *)strchr((const char *)endp, '\n');
2583  if (pp) startp = pp+1;
2584  else break;
2585  } else break;
2586 
2587  }
2588  }
2589 
2590 
2591 
2592  // If this was the last bunch of entries, send the buffer and empty it immediately
2593  if (final_) {
2594  std::string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:multistatus xmlns:D=\"DAV:\" xmlns:ns1=\"http://apache.org/dav/props/\" xmlns:ns0=\"DAV:\">\n";
2595  stringresp.insert(0, s);
2596  stringresp += "</D:multistatus>\n";
2597  prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2598  (char *) stringresp.c_str(), stringresp.length(), keepalive);
2599  stringresp.clear();
2600  return keepalive ? 1 : -1;
2601  }
2602 
2603  break;
2604  } // default reqstate
2605  } // switch reqstate
2606 
2607 
2608  break;
2609 
2610  } // case propfind
2611 
2612  case XrdHttpReq::rtMKCOL:
2613  {
2614 
2615  if (xrdresp != kXR_ok) {
2616  if (xrderrcode == kXR_ItExists) {
2617  prot->SendSimpleResp(405, NULL, NULL, (char *) "Method is not allowed; resource already exists.", 0, false);
2618  } else {
2619  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2620  httpErrorBody.c_str(), httpErrorBody.length(), false);
2621  }
2622  return -1;
2623  }
2624 
2625  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2626  return keepalive ? 1 : -1;
2627 
2628  }
2629  case XrdHttpReq::rtMOVE:
2630  {
2631 
2632  if (xrdresp != kXR_ok) {
2633  prot->SendSimpleResp(httpStatusCode, NULL, NULL, (char *) etext.c_str(), 0, false);
2634  return -1;
2635  }
2636 
2637  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2638  return keepalive ? 1 : -1;
2639 
2640  }
2641 
2642  default:
2643  break;
2644 
2645  }
2646 
2647 
2648  switch (xrdresp) {
2649  case kXR_error:
2650  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2651  httpErrorBody.c_str(), httpErrorBody.length(), false);
2652  return -1;
2653  break;
2654 
2655  default:
2656 
2657  break;
2658  }
2659 
2660 
2661  return 0;
2662 }
2663 
2664 int
2665 XrdHttpReq::sendFooterError(const std::string &extra_text) {
2666  if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2667  // A trailer header is appropriate in this case; this is signified by
2668  // a chunk with size zero, then the trailer, then a crlf.
2669  //
2670  // We only send the status trailer when explicitly requested; otherwise a
2671  // "normal" HTTP client might simply see a short response and think it's a
2672  // success
2673 
2674  if (prot->ChunkRespHeader(0))
2675  return -1;
2676 
2677  std::stringstream ss;
2678 
2679  ss << httpStatusCode;
2680  if (!httpErrorBody.empty()) {
2681  std::string_view statusView(httpErrorBody);
2682  // Remove trailing newline; this is not valid in a trailer value
2683  // and causes incorrect framing of the response, confusing clients.
2684  if (statusView[statusView.size() - 1] == '\n') {
2685  ss << ": " << statusView.substr(0, statusView.size() - 1);
2686  } else {
2687  ss << ": " << httpErrorBody;
2688  }
2689  }
2690 
2691  if (!extra_text.empty())
2692  ss << ": " << extra_text;
2693  TRACEI(REQ, ss.str());
2694  ss << "\r\n";
2695 
2696  const auto header = "X-Transfer-Status: " + ss.str();
2697  if (prot->SendData(header.c_str(), header.size()))
2698  return -1;
2699 
2700  if (prot->ChunkRespFooter())
2701  return -1;
2702 
2703  return keepalive ? 1 : -1;
2704  } else {
2705  TRACEI(REQ, "Failure during response: " << httpStatusCode << ": " << httpErrorBody << (extra_text.empty() ? "" : (": " + extra_text)));
2706  return -1;
2707 
2708  }
2709 }
2710 
2711 void XrdHttpReq::addAgeHeader(std::string &headers) {
2712  long object_age = time(NULL) - filemodtime;
2713  headers += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2714 }
2715 
2717 
2718  TRACE(REQ, " XrdHttpReq request ended.");
2719 
2720  //if (xmlbody) xmlFreeDoc(xmlbody);
2722  readClosing = false;
2723  closeAfterError = false;
2724  writtenbytes = 0;
2725  etext.clear();
2726  redirdest = "";
2727 
2728  // // Here we should deallocate this
2729  // const struct iovec *iovP //!< pointer to data array
2730  // int iovN, //!< array count
2731  // int iovL, //!< byte count
2732  // bool final //!< true -> final result
2733 
2734 
2735  //xmlbody = 0;
2736  depth = 0;
2739  ralist.clear();
2740  ralist.shrink_to_fit();
2741 
2742  request = rtUnset;
2743  resource = "";
2744  allheaders.clear();
2745 
2746  // Reset the state of the request's digest request.
2747  m_req_digest.clear();
2748  m_digest_header.clear();
2749  m_req_cksum = nullptr;
2750 
2752  m_user_agent = "";
2753  m_origin = "";
2754 
2755  headerok = false;
2756  keepalive = true;
2757  length = 0;
2758  filesize = 0;
2759  depth = 0;
2760  sendcontinue = false;
2761 
2762  m_transfer_encoding_chunked = false;
2763  m_current_chunk_size = -1;
2764  m_current_chunk_offset = 0;
2765 
2766  m_trailer_headers = false;
2767  m_status_trailer = false;
2768 
2770  reqstate = 0;
2771 
2772  memset(&xrdreq, 0, sizeof (xrdreq));
2773  memset(&xrdresp, 0, sizeof (xrdresp));
2775 
2776  etext.clear();
2777  redirdest = "";
2778 
2779  stringresp = "";
2780 
2781  host = "";
2782  destination = "";
2783  hdr2cgistr = "";
2784  m_appended_hdr2cgistr = false;
2785  m_appended_asize = false;
2786 
2787  iovP = 0;
2788  iovN = 0;
2789  iovL = 0;
2790 
2791 
2792  if (opaque) delete(opaque);
2793  opaque = 0;
2794 
2795  fopened = false;
2796 
2797  final = false;
2798 
2799  mScitag = -1;
2800 
2801  httpStatusCode = -1;
2802  httpErrorCode = "";
2803  httpErrorBody = "";
2804 
2805 }
2806 
2807 void XrdHttpReq::getfhandle() {
2808 
2809  memcpy(fhandle, iovP[0].iov_base, 4);
2810  TRACEI(REQ, "fhandle:" <<
2811  (int) fhandle[0] << ":" << (int) fhandle[1] << ":" << (int) fhandle[2] << ":" << (int) fhandle[3]);
2812 
2813 }
2814 
2815 void XrdHttpReq::getReadResponse(XrdHttpIOList &received) {
2816  received.clear();
2817 
2818  if (ntohs(xrdreq.header.requestid) == kXR_readv) {
2819  readahead_list *l;
2820  char *p;
2821  kXR_int32 len;
2822 
2823  // Cycle on all the data that is coming from the server
2824  for (int i = 0; i < iovN; i++) {
2825 
2826  for (p = (char *) iovP[i].iov_base; p < (char *) iovP[i].iov_base + iovP[i].iov_len;) {
2827  l = (readahead_list *) p;
2828  len = ntohl(l->rlen);
2829 
2830  received.emplace_back(p+sizeof(readahead_list), -1, len);
2831 
2832  p += sizeof (readahead_list);
2833  p += len;
2834 
2835  }
2836  }
2837  return;
2838  }
2839 
2840  // kXR_read result
2841  for (int i = 0; i < iovN; i++) {
2842  received.emplace_back((char*)iovP[i].iov_base, -1, iovP[i].iov_len);
2843  }
2844 
2845 }
2846 
2847 int XrdHttpReq::sendReadResponsesMultiRanges(const XrdHttpIOList &received) {
2848 
2849  if (received.size() == 0) {
2850  bool start, finish;
2851  if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2852  return -1;
2853  }
2854  return 0;
2855  }
2856 
2857  // user is expecting multiple ranges, we must be prepared to send an
2858  // individual header for each and format it according to the http rules
2859 
2860  struct rinfo {
2861  bool start;
2862  bool finish;
2863  const XrdOucIOVec2 *ci;
2865  std::string st_header;
2866  std::string fin_header;
2867  };
2868 
2869  // report each received byte chunk to the range handler and record the details
2870  // of original user range it related to and if starts a range or finishes all.
2871  // also sum the total of the headers and data which need to be sent to the user,
2872  // in case we need it for chunked transfer encoding
2873  std::vector<rinfo> rvec;
2874  off_t sum_len = 0;
2875 
2876  rvec.reserve(received.size());
2877 
2878  for(const auto &rcv: received) {
2879  rinfo rentry;
2880  bool start, finish;
2882 
2883  if (readRangeHandler.NotifyReadResult(rcv.size, &ur, start, finish) < 0) {
2884  return -1;
2885  }
2886  rentry.ur = ur;
2887  rentry.start = start;
2888  rentry.finish = finish;
2889  rentry.ci = &rcv;
2890 
2891  if (start) {
2892  std::string s = buildPartialHdr(ur->start,
2893  ur->end,
2894  filesize,
2895  (char *) "123456");
2896 
2897  rentry.st_header = s;
2898  sum_len += s.size();
2899  }
2900 
2901  sum_len += rcv.size;
2902 
2903  if (finish) {
2904  std::string s = buildPartialHdrEnd((char *) "123456");
2905  rentry.fin_header = s;
2906  sum_len += s.size();
2907  }
2908 
2909  rvec.push_back(rentry);
2910  }
2911 
2912 
2913  // Send chunked encoding header
2914  if (m_transfer_encoding_chunked && m_trailer_headers) {
2915  prot->ChunkRespHeader(sum_len);
2916  }
2917 
2918  // send the user the headers / data
2919  for(const auto &rentry: rvec) {
2920 
2921  if (rentry.start) {
2922  TRACEI(REQ, "Sending multipart: " << rentry.ur->start << "-" << rentry.ur->end);
2923  if (prot->SendData((char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2924  return -1;
2925  }
2926  }
2927 
2928  // Send all the data we have
2929  if (prot->SendData((char *) rentry.ci->data, rentry.ci->size)) {
2930  return -1;
2931  }
2932 
2933  if (rentry.finish) {
2934  if (prot->SendData((char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2935  return -1;
2936  }
2937  }
2938  }
2939 
2940  // Send chunked encoding footer
2941  if (m_transfer_encoding_chunked && m_trailer_headers) {
2942  prot->ChunkRespFooter();
2943  }
2944 
2945  return 0;
2946 }
2947 
2948 int XrdHttpReq::sendReadResponseSingleRange(const XrdHttpIOList &received) {
2949  // single range http transfer
2950 
2951  if (received.size() == 0) {
2952  bool start, finish;
2953  if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2954  return -1;
2955  }
2956  return 0;
2957  }
2958 
2959  off_t sum = 0;
2960  // notify the range handler and return if error
2961  for(const auto &rcv: received) {
2962  bool start, finish;
2963  if (readRangeHandler.NotifyReadResult(rcv.size, nullptr, start, finish) < 0) {
2964  return -1;
2965  }
2966  sum += rcv.size;
2967  }
2968 
2969  // Send chunked encoding header
2970  if (m_transfer_encoding_chunked && m_trailer_headers) {
2971  prot->ChunkRespHeader(sum);
2972  }
2973  for(const auto &rcv: received) {
2974  if (prot->SendData((char *) rcv.data, rcv.size)) return -1;
2975  }
2976  if (m_transfer_encoding_chunked && m_trailer_headers) {
2977  prot->ChunkRespFooter();
2978  }
2979  return 0;
2980 }
kXR_unt16 requestid
Definition: XProtocol.hh:479
kXR_char options[1]
Definition: XProtocol.hh:248
XErrorCode
Definition: XProtocol.hh:989
@ kXR_ItExists
Definition: XProtocol.hh:1008
@ kXR_noErrorYet
Definition: XProtocol.hh:1027
@ kXR_isDirectory
Definition: XProtocol.hh:1006
kXR_int16 arg1len
Definition: XProtocol.hh:430
#define kXR_isManager
Definition: XProtocol.hh:1156
kXR_unt16 requestid
Definition: XProtocol.hh:806
struct ClientCloseRequest close
Definition: XProtocol.hh:851
kXR_char fhandle[4]
Definition: XProtocol.hh:807
struct ClientSetRequest set
Definition: XProtocol.hh:871
struct ClientMkdirRequest mkdir
Definition: XProtocol.hh:858
kXR_int32 dlen
Definition: XProtocol.hh:431
kXR_int64 offset
Definition: XProtocol.hh:646
kXR_unt16 requestid
Definition: XProtocol.hh:644
kXR_unt16 options
Definition: XProtocol.hh:481
struct ClientDirlistRequest dirlist
Definition: XProtocol.hh:852
kXR_unt16 requestid
Definition: XProtocol.hh:228
struct ClientReadVRequest readv
Definition: XProtocol.hh:868
@ kXR_open_wrto
Definition: XProtocol.hh:469
@ kXR_delete
Definition: XProtocol.hh:453
@ kXR_open_read
Definition: XProtocol.hh:456
@ kXR_mkpath
Definition: XProtocol.hh:460
@ kXR_seqio
Definition: XProtocol.hh:468
@ kXR_new
Definition: XProtocol.hh:455
@ kXR_retstat
Definition: XProtocol.hh:463
struct ClientOpenRequest open
Definition: XProtocol.hh:860
@ kXR_noResponsesYet
Definition: XProtocol.hh:908
@ kXR_ok
Definition: XProtocol.hh:899
@ kXR_error
Definition: XProtocol.hh:903
@ kXR_dstat
Definition: XProtocol.hh:240
struct ClientRequestHdr header
Definition: XProtocol.hh:846
kXR_unt16 requestid
Definition: XProtocol.hh:428
kXR_char fhandle[4]
Definition: XProtocol.hh:645
kXR_char fhandle[4]
Definition: XProtocol.hh:229
kXR_unt16 requestid
Definition: XProtocol.hh:157
@ kXR_read
Definition: XProtocol.hh:125
@ kXR_open
Definition: XProtocol.hh:122
@ kXR_readv
Definition: XProtocol.hh:137
@ kXR_mkdir
Definition: XProtocol.hh:120
@ kXR_dirlist
Definition: XProtocol.hh:116
@ kXR_rm
Definition: XProtocol.hh:126
@ kXR_write
Definition: XProtocol.hh:131
@ kXR_set
Definition: XProtocol.hh:130
@ kXR_rmdir
Definition: XProtocol.hh:127
@ kXR_mv
Definition: XProtocol.hh:121
@ kXR_stat
Definition: XProtocol.hh:129
@ kXR_close
Definition: XProtocol.hh:115
kXR_int32 dlen
Definition: XProtocol.hh:699
struct ClientRmRequest rm
Definition: XProtocol.hh:869
kXR_unt16 requestid
Definition: XProtocol.hh:719
kXR_int32 dlen
Definition: XProtocol.hh:648
struct ClientReadRequest read
Definition: XProtocol.hh:867
struct ClientMvRequest mv
Definition: XProtocol.hh:859
kXR_int32 rlen
Definition: XProtocol.hh:660
kXR_unt16 requestid
Definition: XProtocol.hh:768
kXR_int32 dlen
Definition: XProtocol.hh:483
struct ClientRmdirRequest rmdir
Definition: XProtocol.hh:870
kXR_unt16 requestid
Definition: XProtocol.hh:415
kXR_unt16 mode
Definition: XProtocol.hh:480
kXR_char options[1]
Definition: XProtocol.hh:416
kXR_unt16 requestid
Definition: XProtocol.hh:697
@ kXR_mkdirpath
Definition: XProtocol.hh:410
struct ClientStatRequest stat
Definition: XProtocol.hh:873
kXR_int64 offset
Definition: XProtocol.hh:808
struct ClientWriteRequest write
Definition: XProtocol.hh:876
kXR_int32 dlen
Definition: XProtocol.hh:772
kXR_int32 rlen
Definition: XProtocol.hh:647
@ kXR_gw
Definition: XProtocol.hh:444
@ kXR_ur
Definition: XProtocol.hh:440
@ kXR_uw
Definition: XProtocol.hh:441
@ kXR_gr
Definition: XProtocol.hh:443
@ kXR_or
Definition: XProtocol.hh:446
@ kXR_readable
Definition: XProtocol.hh:1224
@ kXR_isDir
Definition: XProtocol.hh:1221
@ kXR_offline
Definition: XProtocol.hh:1223
@ kXR_other
Definition: XProtocol.hh:1222
@ kXR_writable
Definition: XProtocol.hh:1225
@ kXR_cachersp
Definition: XProtocol.hh:1228
@ kXR_xset
Definition: XProtocol.hh:1220
kXR_unt16 requestid
Definition: XProtocol.hh:708
long long kXR_int64
Definition: XPtypes.hh:98
int kXR_int32
Definition: XPtypes.hh:89
short kXR_int16
Definition: XPtypes.hh:66
unsigned char kXR_char
Definition: XPtypes.hh:65
#define DEBUG(x)
Definition: XrdBwmTrace.hh:54
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
std::string ISOdatetime(time_t t)
Definition: XrdHttpReq.cc:83
#define MAX_TK_LEN
Definition: XrdHttpReq.cc:66
void trim(std::string &str)
Definition: XrdHttpReq.cc:77
Main request/response class, handling the logical status of the communication.
long long size
Definition: XrdHttpReq.hh:61
std::string path
Definition: XrdHttpReq.hh:60
long modtime
Definition: XrdHttpReq.hh:64
Static resources, here for performance and ease of setup.
Trace definitions.
std::string itos(long i)
void Tobase64(const unsigned char *input, int length, char *out)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
int mapXrdErrToHttp(XErrorCode xrdError)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
Utility functions for XrdHTTP.
@ HTTP_METHOD_NOT_ALLOWED
Definition: XrdHttpUtils.hh:86
std::string encode_opaque(const std::string &opaque)
std::string encode_str(const std::string &str)
std::vector< XrdOucIOVec2 > XrdHttpIOList
std::string decode_str(const std::string &str)
std::string obfuscateAuth(const std::string &input)
#define STR_NPOS
#define TRACE_DEBUG
Definition: XrdTrace.hh:36
#define TRACE(act, x)
Definition: XrdTrace.hh:63
#define TRACING(x)
Definition: XrdTrace.hh:70
#define TRACEI(act, x)
Definition: XrdTrace.hh:66
XrdHttpChecksumRawPtr getChecksumToRun(const std::string &userDigest) const
bool needsBase64Padding() const
std::string getXRootDConfigDigestName() const
std::string getHttpName() const
virtual int ProcessReq(XrdHttpExtReq &)=0
static char * secretkey
The key used to calculate the url hashes.
static kXR_int32 myRole
Our role.
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static XrdHttpChecksumHandler cksumHandler
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
XrdLink * Link
The link we are bound to.
int doStat(char *fname)
Perform a Stat request.
static bool isdesthttps
True if the redirections must be towards https targets.
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.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
void reset()
resets this handler
const XrdHttpIOList & NextReadList()
return XrdHttpIOList for sending to read or readv
void ParseContentRange(const char *const line)
parse the line after a "Range: " http request header
int SetFilesize(const off_t sz)
sets the filesize, used during resolving and issuing range requests
void NotifyError()
Force handler to enter error state.
bool isFullFile()
indicates when there were no valid Range head ranges supplied
std::vector< UserRange > UserRangeList
int NotifyReadResult(const ssize_t ret, const UserRange **const urp, bool &start, bool &allend)
Advance internal counters concerning received bytes.
size_t getMaxRanges() const
return the maximum number of ranges that may be requested
const Error & getError() const
return the Error object
bool isSingleRange()
indicates a single range (implied whole file, or single range) or empty file
const UserRangeList & ListResolvedRanges()
return resolved (i.e. obsolute start and end) byte ranges desired
int reqstate
State machine to talk to the bridge.
Definition: XrdHttpReq.hh:348
char fhandle[4]
Definition: XrdHttpReq.hh:341
int ReqReadV(const XrdHttpIOList &cl)
Prepare the buffers for sending a readv request.
Definition: XrdHttpReq.cc:397
bool keepalive
Definition: XrdHttpReq.hh:284
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:95
std::vector< readahead_list > ralist
Definition: XrdHttpReq.hh:236
long long length
Definition: XrdHttpReq.hh:285
std::string destination
The destination field specified in the req.
Definition: XrdHttpReq.hh:292
XrdOucString resource
The resource specified by the request, stripped of opaque data.
Definition: XrdHttpReq.hh:266
bool headerok
Tells if we have finished reading the header.
Definition: XrdHttpReq.hh:274
std::string m_digest_header
The computed digest for the HTTP response header.
Definition: XrdHttpReq.hh:305
std::string etext
Definition: XrdHttpReq.hh:327
std::string stringresp
If we want to give a string as a response, we compose it here.
Definition: XrdHttpReq.hh:345
XResponseType xrdresp
The last response data we got.
Definition: XrdHttpReq.hh:325
std::string requestverb
Definition: XrdHttpReq.hh:259
ReqType request
The request we got.
Definition: XrdHttpReq.hh:258
int ProcessHTTPReq()
Definition: XrdHttpReq.cc:854
bool closeAfterError
Definition: XrdHttpReq.hh:282
long long writtenbytes
In a long write, we track where we have arrived.
Definition: XrdHttpReq.hh:351
XrdOucEnv * opaque
The opaque data, after parsing.
Definition: XrdHttpReq.hh:268
long fileflags
Definition: XrdHttpReq.hh:338
int iovL
byte count
Definition: XrdHttpReq.hh:333
bool fopened
Definition: XrdHttpReq.hh:342
const struct iovec * iovP
The latest data chunks got from the xrd layer. These are valid only inside the callbacks!
Definition: XrdHttpReq.hh:331
virtual ~XrdHttpReq()
Definition: XrdHttpReq.cc:111
std::string m_req_digest
The requested digest type.
Definition: XrdHttpReq.hh:295
XrdOucString resourceplusopaque
The resource specified by the request, including all the opaque data.
Definition: XrdHttpReq.hh:270
virtual bool Data(XrdXrootd::Bridge::Context &info, const struct iovec *iovP, int iovN, int iovL, bool final)
Definition: XrdHttpReq.cc:452
std::string hdr2cgistr
Additional opaque info that may come from the hdr2cgi directive.
Definition: XrdHttpReq.hh:308
virtual bool Done(XrdXrootd::Bridge::Context &info)
the result context
Definition: XrdHttpReq.cc:498
std::string host
The host field specified in the req.
Definition: XrdHttpReq.hh:290
long filemodtime
Definition: XrdHttpReq.hh:339
int parseFirstLine(char *line, int len)
Parse the first line of the header.
Definition: XrdHttpReq.cc:265
XrdOucString redirdest
Definition: XrdHttpReq.hh:328
std::string m_origin
Definition: XrdHttpReq.hh:355
int parseLine(char *line, int len)
Parse the header.
Definition: XrdHttpReq.cc:117
std::string buildPartialHdrEnd(char *token)
Build the closing part for a multipart response.
Definition: XrdHttpReq.cc:444
XrdHttpChecksumHandler::XrdHttpChecksumRawPtr m_req_cksum
The checksum that was ran for this request.
Definition: XrdHttpReq.hh:298
void setTransferStatusHeader(std::string &header)
Definition: XrdHttpReq.cc:2017
bool m_appended_hdr2cgistr
Definition: XrdHttpReq.hh:309
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
Definition: XrdHttpReq.cc:654
int iovN
array count
Definition: XrdHttpReq.hh:332
bool m_appended_asize
Track whether we already appended the oss.asize argument for PUTs.
Definition: XrdHttpReq.hh:311
XrdOucString m_resource_with_digest
Definition: XrdHttpReq.hh:303
long long filesize
Definition: XrdHttpReq.hh:337
bool readClosing
Definition: XrdHttpReq.hh:278
virtual bool Redir(XrdXrootd::Bridge::Context &info, int port, const char *hname)
Definition: XrdHttpReq.cc:545
XErrorCode xrderrcode
Definition: XrdHttpReq.hh:326
virtual int File(XrdXrootd::Bridge::Context &info, int dlen)
Definition: XrdHttpReq.cc:474
std::map< std::string, std::string > allheaders
Definition: XrdHttpReq.hh:263
void addCgi(const std::string &key, const std::string &value)
Definition: XrdHttpReq.cc:762
bool sendcontinue
Definition: XrdHttpReq.hh:287
ClientRequest xrdreq
The last issued xrd request, often pending.
Definition: XrdHttpReq.hh:322
std::string buildPartialHdr(long long bytestart, long long byteend, long long filesize, char *token)
Build a partial header for a multipart response.
Definition: XrdHttpReq.cc:434
XrdHttpReadRangeHandler readRangeHandler
Tracking the next ranges of data to read during GET.
Definition: XrdHttpReq.hh:277
virtual void reset()
Definition: XrdHttpReq.cc:2716
virtual bool Error(XrdXrootd::Bridge::Context &info, int ecode, const char *etext)
Definition: XrdHttpReq.cc:515
static const int minTotID
Definition: XrdNetPMark.hh:89
static const int maxTotID
Definition: XrdNetPMark.hh:90
char * Get(const char *varname)
Definition: XrdOucEnv.hh:69
char * Env(int &envlen)
Definition: XrdOucEnv.hh:48
void Put(const char *varname, const char *value)
Definition: XrdOucEnv.hh:85
const char * c_str() const
void assign(const char *s, int j, int k=-1)
int erasefromstart(int sz=0)
int erasefromend(int sz=0)
bool endswith(char c)
bool beginswith(char c)
int erase(int start=0, int size=0)
int find(const char c, int start=0, bool forward=1)
int length() const
void append(const int i)
static void trim(std::string &str)
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
virtual int Send(const struct iovec *headP, int headN, const struct iovec *tailP, int tailN)
virtual int setSF(kXR_char *fhandle, bool seton=false)=0
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0