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