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