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