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