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