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