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