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 
933  if (!m_appended_hdr2cgistr && !hdr2cgistr.empty()) {
934  const char *p = strchr(resourceplusopaque.c_str(), '?');
935  if (p) {
937  } else {
939  }
940 
941  char *q = quote(hdr2cgistr.c_str());
943  if (TRACING(TRACE_DEBUG)) {
944  // The obfuscation of "authz" will only be done if the server http.header2cgi config contains something that maps a header to this "authz" cgi.
945  // Unfortunately the obfuscation code will be called no matter what is configured in http.header2cgi.
946  std::string header2cgistrObf = obfuscateAuth(hdr2cgistr);
947 
948  TRACEI(DEBUG, "Appended header fields to opaque info: '"
949  << header2cgistrObf.c_str() << "'");
950 
951  }
952  // We assume that anything appended to the CGI str should also
953  // apply to the destination in case of a MOVE.
954  if (strchr(destination.c_str(), '?')) destination.append("&");
955  else destination.append("?");
956  destination.append(q);
957 
958  free(q);
959  m_appended_hdr2cgistr = true;
960  }
961 
962  // Verify if we have an external handler for this request
963  if (reqstate == 0) {
964  XrdHttpExtHandler *exthandler = prot->FindMatchingExtHandler(*this);
965  if (exthandler) {
966  XrdHttpExtReq xreq(this, prot);
967  int r = exthandler->ProcessReq(xreq);
968  reset();
969  if (!r) return 1; // All went fine, response sent
970  if (r < 0) return -1; // There was a hard error... close the connection
971 
972  return 1; // There was an error and a response was sent
973  }
974  }
975 
976  //
977  // Here we process the request locally
978  //
979 
980  switch (request) {
981  case XrdHttpReq::rtUnset:
983  {
984  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request unknown", 0, false);
985  reset();
986  return -1;
987  }
989  {
990  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed", 0, false);
991  reset();
992  return -1;
993  }
994  case XrdHttpReq::rtHEAD:
995  {
996  if (reqstate == 0) {
997  // Always start with Stat; in the case of a checksum request, we'll have a follow-up query
998  if (prot->doStat((char *) resourceplusopaque.c_str())) {
999  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1000  return -1;
1001  }
1002  return 0;
1003  } else {
1004  const char *opaque = strchr(resourceplusopaque.c_str(), '?');
1005  // Note that doChksum requires that the memory stays alive until the callback is invoked.
1007 
1009  if(!m_req_cksum) {
1010  // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1011  prot->SendSimpleResp(403, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1012  return -1;
1013  }
1014  if (!opaque) {
1015  m_resource_with_digest += "?cks.type=";
1017  } else {
1018  m_resource_with_digest += "&cks.type=";
1020  }
1021  if (prot->doChksum(m_resource_with_digest) < 0) {
1022  // In this case, the Want-Digest header was set and PostProcess gave the go-ahead to do a checksum.
1023  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to create initial checksum request.", 0, false);
1024  return -1;
1025  }
1026  return 1;
1027  }
1028  }
1029  case XrdHttpReq::rtGET:
1030  {
1031  int retval = keepalive ? 1 : -1; // reset() clears keepalive
1032 
1033  if (resource.beginswith("/static/")) {
1034 
1035  // This is a request for a /static resource
1036  // If we have to use the embedded ones then we return the ones in memory as constants
1037 
1038  // The sysadmin can always redirect the request to another host that
1039  // contains his static resources
1040 
1041  // We also allow xrootd to preread from the local disk all the files
1042  // that have to be served as static resources.
1043 
1044  if (prot->embeddedstatic) {
1045 
1046  // Default case: the icon and the css of the HTML rendering of XrdHttp
1047  if (resource == "/static/css/xrdhttp.css") {
1048  prot->SendSimpleResp(200, NULL, NULL, (char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len, keepalive);
1049  reset();
1050  return retval;
1051  }
1052  if (resource == "/static/icons/xrdhttp.ico") {
1053  prot->SendSimpleResp(200, NULL, NULL, (char *) favicon_ico, favicon_ico_len, keepalive);
1054  reset();
1055  return retval;
1056  }
1057 
1058  }
1059 
1060  // If we are here then none of the embedded resources match (or they are disabled)
1061  // We may have to redirect to a host that is supposed to serve the static resources
1062  if (prot->staticredir) {
1063 
1064  XrdOucString s = "Location: ";
1065  s.append(prot->staticredir);
1066 
1067  if (s.endswith('/'))
1068  s.erasefromend(1);
1069 
1070  s.append(resource);
1071  appendOpaque(s, 0, 0, 0);
1072 
1073  prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1074  return -1;
1075 
1076 
1077  } else {
1078 
1079  // We lookup the requested path in a hash containing the preread files
1080  if (prot->staticpreload) {
1082  if (mydata) {
1083  prot->SendSimpleResp(200, NULL, NULL, (char *) mydata->data, mydata->len, keepalive);
1084  reset();
1085  return retval;
1086  }
1087  }
1088 
1089  }
1090 
1091 
1092  }
1093 
1094  // The reqstate parameter basically moves us through a simple state machine.
1095  // To optimize things, we start off by opening the file; if it turns out to be a directory, then
1096  // we close the file handle and switch to doing a HTML-based rendering of the directory. This
1097  // avoids needing to always to do "stat" first to determine the next step (since the file-open also
1098  // does a "stat").
1099  // - 0: Perform an open on the resource
1100  // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
1101  // - 2: Perform a close (for dirlist only)
1102  // - 3: Perform a dirlist.
1103  // - 4+: Reads from file; if at end, perform a close.
1104  switch (reqstate) {
1105  case 0: // Open the path for reading.
1106  {
1107  memset(&xrdreq, 0, sizeof (ClientRequest));
1108  xrdreq.open.requestid = htons(kXR_open);
1109  l = resourceplusopaque.length() + 1;
1110  xrdreq.open.dlen = htonl(l);
1111  xrdreq.open.mode = 0;
1113 
1114  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1115  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1116  return -1;
1117  }
1118 
1119  // Prepare to chunk up the request
1120  writtenbytes = 0;
1121 
1122  // We want to be invoked again after this request is finished
1123  return 0;
1124  }
1125  case 1: // Checksum request
1126  if (!(fileflags & kXR_isDir) && !m_req_digest.empty()) {
1127  // In this case, the Want-Digest header was set.
1128  bool has_opaque = strchr(resourceplusopaque.c_str(), '?');
1129  // Note that doChksum requires that the memory stays alive until the callback is invoked.
1131  if(!m_req_cksum) {
1132  // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1133  prot->SendSimpleResp(403, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1134  return -1;
1135  }
1137  if (has_opaque) {
1138  m_resource_with_digest += "&cks.type=";
1140  } else {
1141  m_resource_with_digest += "?cks.type=";
1143  }
1144  if (prot->doChksum(m_resource_with_digest) < 0) {
1145  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to start internal checksum request to satisfy Want-Digest header.", 0, false);
1146  return -1;
1147  }
1148  return 0;
1149  } else {
1150  TRACEI(DEBUG, "No checksum requested; skipping to request state 2");
1151  reqstate += 1;
1152  }
1153  // fallthrough
1154  case 2: // Close file handle for directory
1155  if ((fileflags & kXR_isDir) && fopened) {
1156  memset(&xrdreq, 0, sizeof (ClientRequest));
1157  xrdreq.close.requestid = htons(kXR_close);
1158  memcpy(xrdreq.close.fhandle, fhandle, 4);
1159 
1160  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1161  mapXrdErrorToHttpStatus();
1162  sendFooterError("Could not run close request on the bridge");
1163  return -1;
1164  }
1165  return 0;
1166  } else {
1167  reqstate += 1;
1168  }
1169  // fallthrough
1170  case 3: // List directory
1171  if (fileflags & kXR_isDir) {
1172  if (prot->listdeny) {
1173  prot->SendSimpleResp(503, NULL, NULL, (char *) "Listings are disabled.", 0, false);
1174  return -1;
1175  }
1176 
1177  if (prot->listredir) {
1178  XrdOucString s = "Location: ";
1179  s.append(prot->listredir);
1180 
1181  if (s.endswith('/'))
1182  s.erasefromend(1);
1183 
1184  s.append(resource);
1185  appendOpaque(s, 0, 0, 0);
1186 
1187  prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1188  return -1;
1189  }
1190 
1191  std::string res;
1192  res = resourceplusopaque.c_str();
1193 
1194  // --------- DIRLIST
1195  memset(&xrdreq, 0, sizeof (ClientRequest));
1198  l = res.length() + 1;
1199  xrdreq.dirlist.dlen = htonl(l);
1200 
1201  if (!prot->Bridge->Run((char *) &xrdreq, (char *) res.c_str(), l)) {
1202  mapXrdErrorToHttpStatus();
1203  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(), false);
1204  sendFooterError("Could not run listing request on the bridge");
1205  return -1;
1206  }
1207 
1208  // We don't want to be invoked again after this request is finished
1209  return 1;
1210  }
1211  else {
1212  reqstate += 1;
1213  }
1214  // fallthrough
1215  case 4:
1216  {
1217  auto retval = ReturnGetHeaders();
1218  if (retval) {
1219  return retval;
1220  }
1221  }
1222  // fallthrough
1223  default: // Read() or Close(); reqstate is 4+
1224  {
1225  const XrdHttpIOList &readChunkList = readRangeHandler.NextReadList();
1226 
1227  // Close() if we have finished, otherwise read the next chunk
1228 
1229  // --------- CLOSE
1230  if ( readChunkList.empty() )
1231  {
1232 
1233  memset(&xrdreq, 0, sizeof (ClientRequest));
1234  xrdreq.close.requestid = htons(kXR_close);
1235  memcpy(xrdreq.close.fhandle, fhandle, 4);
1236 
1237  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1238  TRACEI(REQ, " Failed to run close request on the bridge.");
1239  // Note: we have already completed the request and sent the data to the client.
1240  // Hence, there's no need to send an error. However, since the bridge is potentially
1241  // in a bad state, we close the TCP socket to force the client to reconnect.
1242  return -1;
1243  }
1244 
1245  // We have finished
1246  readClosing = true;
1247  return 1;
1248 
1249  }
1250  // --------- READ or READV
1251 
1252  if ( readChunkList.size() == 1 ) {
1253  // Use a read request for single range
1254 
1255  long l;
1256  long long offs;
1257 
1258  // --------- READ
1259  memset(&xrdreq, 0, sizeof (xrdreq));
1260  xrdreq.read.requestid = htons(kXR_read);
1261  memcpy(xrdreq.read.fhandle, fhandle, 4);
1262  xrdreq.read.dlen = 0;
1263 
1264  offs = readChunkList[0].offset;
1265  l = readChunkList[0].size;
1266 
1267  xrdreq.read.offset = htonll(offs);
1268  xrdreq.read.rlen = htonl(l);
1269 
1270  // If we are using HTTPS or if the client requested trailers, or if the
1271  // read concerns a multirange reponse, disable sendfile
1272  // (in the latter two cases, the extra framing is only done in PostProcessHTTPReq)
1273  if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1275  if (!prot->Bridge->setSF((kXR_char *) fhandle, false)) {
1276  TRACE(REQ, " XrdBridge::SetSF(false) failed.");
1277 
1278  }
1279  }
1280 
1281 
1282 
1283  if (l <= 0) {
1284  if (l < 0) {
1285  TRACE(ALL, " Data sizes mismatch.");
1286  return -1;
1287  }
1288  else {
1289  TRACE(ALL, " No more bytes to send.");
1290  reset();
1291  return 1;
1292  }
1293  }
1294 
1295  if ((offs >= filesize) || (offs+l > filesize)) {
1296  httpStatusCode = 416;
1297  httpStatusText = "Range Not Satisfiable";
1298  std::stringstream ss;
1299  ss << "Requested range " << l << "@" << offs << " is past the end of file (" << filesize << ")";
1300  sendFooterError(ss.str());
1301  return -1;
1302  }
1303 
1304  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1305  mapXrdErrorToHttpStatus();
1306  sendFooterError("Could not run read request on the bridge");
1307  return -1;
1308  }
1309  } else {
1310  // --------- READV
1311 
1312  length = ReqReadV(readChunkList);
1313 
1314  if (!prot->Bridge->Run((char *) &xrdreq, (char *) &ralist[0], length)) {
1315  mapXrdErrorToHttpStatus();
1316  sendFooterError("Could not run ReadV request on the bridge");
1317  return -1;
1318  }
1319 
1320  }
1321 
1322  // We want to be invoked again after this request is finished
1323  return 0;
1324  } // case 3+
1325 
1326  } // switch (reqstate)
1327 
1328 
1329  } // case XrdHttpReq::rtGET
1330 
1331  case XrdHttpReq::rtPUT:
1332  {
1333  //if (prot->ishttps) {
1334  //prot->SendSimpleResp(501, NULL, NULL, (char *) "HTTPS not supported yet for direct writing. Sorry.", 0);
1335  //return -1;
1336  //}
1337 
1338  if (!fopened) {
1339 
1340  // --------- OPEN for write!
1341  memset(&xrdreq, 0, sizeof (ClientRequest));
1342  xrdreq.open.requestid = htons(kXR_open);
1343  l = resourceplusopaque.length() + 1;
1344  xrdreq.open.dlen = htonl(l);
1345  xrdreq.open.mode = htons(kXR_ur | kXR_uw | kXR_gw | kXR_gr | kXR_or);
1346  if (! XrdHttpProtocol::usingEC)
1348  else
1350 
1351  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1352  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, keepalive);
1353  return -1;
1354  }
1355 
1356 
1357  // We want to be invoked again after this request is finished
1358  // Only if there is data to fetch from the socket or there will
1359  // never be more data
1360  if (prot->BuffUsed() > 0 || (length == 0 && !sendcontinue))
1361  return 0;
1362 
1363  return 1;
1364 
1365  } else {
1366 
1367  if (m_transfer_encoding_chunked) {
1368  if (m_current_chunk_size == m_current_chunk_offset) {
1369  // Chunk has been consumed; we now must process the CRLF.
1370  // Note that we don't support trailer headers.
1371  if (prot->BuffUsed() < 2) return 1;
1372  if (prot->myBuffStart[0] != '\r' || prot->myBuffStart[1] != '\n') {
1373  prot->SendSimpleResp(400, NULL, NULL, (char *) "Invalid trailing chunk encoding.", 0, keepalive);
1374  return -1;
1375  }
1376  prot->BuffConsume(2);
1377  if (m_current_chunk_size == 0) {
1378  // All data has been sent. Turn off chunk processing and
1379  // set the bytes written and length appropriately; on next callback,
1380  // we will hit the close() block below.
1381  m_transfer_encoding_chunked = false;
1382  length = writtenbytes;
1383  return ProcessHTTPReq();
1384  }
1385  m_current_chunk_size = -1;
1386  m_current_chunk_offset = 0;
1387  // If there is more data, we try to process the next chunk; otherwise, return
1388  if (!prot->BuffUsed()) return 1;
1389  }
1390  if (-1 == m_current_chunk_size) {
1391 
1392  // Parse out the next chunk size.
1393  long long idx = 0;
1394  bool found_newline = false;
1395  // Set a maximum size of chunk we will allow
1396  // Nginx sets this to "NGX_MAX_OFF_T_VALUE", which is 9223372036854775807 (a some crazy number)
1397  // We set it to 1TB, which is 1099511627776
1398  // This is to prevent a malicious client from sending a very large chunk size
1399  // or a malformed chunk request.
1400  // 1TB in base-16 is 0x40000000000, so only allow 11 characters, plus the CRLF
1401  long long max_chunk_size_chars = std::min(static_cast<long long>(prot->BuffUsed()), static_cast<long long>(13));
1402  for (; idx < max_chunk_size_chars; idx++) {
1403  if (prot->myBuffStart[idx] == '\n') {
1404  found_newline = true;
1405  break;
1406  }
1407  }
1408  // If we found a new line, but it is the first character in the buffer (no chunk length)
1409  // or if the previous character is not a CR.
1410  if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] != '\r')) {
1411  prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1412  TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1413  return -1;
1414  }
1415  if (found_newline) {
1416  char *endptr = NULL;
1417  std::string line_contents(prot->myBuffStart, idx);
1418  long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1419  // Chunk sizes can be followed by trailer information or CRLF
1420  if (*endptr != ';' && *endptr != '\r') {
1421  prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1422  TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1423  return -1;
1424  }
1425  m_current_chunk_size = chunk_contents;
1426  m_current_chunk_offset = 0;
1427  prot->BuffConsume(idx + 1);
1428  TRACE(REQ, "XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size << " bytes");
1429  } else {
1430  // Need more data!
1431  return 1;
1432  }
1433  }
1434 
1435  if (m_current_chunk_size == 0) {
1436  // All data has been sent. Invoke this routine again immediately to process CRLF
1437  return ProcessHTTPReq();
1438  } else {
1439  // At this point, we have a chunk size defined and should consume payload data
1440  memset(&xrdreq, 0, sizeof (xrdreq));
1441  xrdreq.write.requestid = htons(kXR_write);
1442  memcpy(xrdreq.write.fhandle, fhandle, 4);
1443 
1444  long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1445  long long bytes_to_write = std::min(static_cast<long long>(prot->BuffUsed()),
1446  chunk_bytes_remaining);
1447 
1448  xrdreq.write.offset = htonll(writtenbytes);
1449  xrdreq.write.dlen = htonl(bytes_to_write);
1450 
1451  TRACEI(REQ, "XrdHTTP PUT: Writing chunk of size " << bytes_to_write << " starting with '" << *(prot->myBuffStart) << "'" << " with " << chunk_bytes_remaining << " bytes remaining in the chunk");
1452  if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_write)) {
1453  mapXrdErrorToHttpStatus();
1454  sendFooterError("Could not run write request on the bridge");
1455  return -1;
1456  }
1457  // If there are more bytes in the buffer, then immediately call us after the
1458  // write is finished; otherwise, wait for data.
1459  return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1460  }
1461  } else if (writtenbytes < length) {
1462 
1463 
1464  // --------- WRITE
1465  memset(&xrdreq, 0, sizeof (xrdreq));
1466  xrdreq.write.requestid = htons(kXR_write);
1467  memcpy(xrdreq.write.fhandle, fhandle, 4);
1468 
1469  long long bytes_to_read = std::min(static_cast<long long>(prot->BuffUsed()),
1470  length - writtenbytes);
1471 
1472  xrdreq.write.offset = htonll(writtenbytes);
1473  xrdreq.write.dlen = htonl(bytes_to_read);
1474 
1475  TRACEI(REQ, "Writing " << bytes_to_read);
1476  if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_read)) {
1477  mapXrdErrorToHttpStatus();
1478  sendFooterError("Could not run write request on the bridge");
1479  return -1;
1480  }
1481 
1482  if (writtenbytes + prot->BuffUsed() >= length)
1483  // Trigger an immediate recall after this request has finished
1484  return 0;
1485  else
1486  // We want to be invoked again after this request is finished
1487  // only if there is pending data
1488  return 1;
1489 
1490 
1491 
1492  } else {
1493 
1494  // --------- CLOSE
1495  memset(&xrdreq, 0, sizeof (ClientRequest));
1496  xrdreq.close.requestid = htons(kXR_close);
1497  memcpy(xrdreq.close.fhandle, fhandle, 4);
1498 
1499 
1500  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1501  mapXrdErrorToHttpStatus();
1502  sendFooterError("Could not run close request on the bridge");
1503  return -1;
1504  }
1505 
1506  // We have finished
1507  return 1;
1508 
1509  }
1510 
1511  }
1512 
1513  break;
1514 
1515  }
1516  case XrdHttpReq::rtOPTIONS:
1517  {
1518  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);
1519  bool ret_keepalive = keepalive; // reset() clears keepalive
1520  reset();
1521  return ret_keepalive ? 1 : -1;
1522  }
1523  case XrdHttpReq::rtDELETE:
1524  {
1525 
1526 
1527  switch (reqstate) {
1528 
1529  case 0: // Stat()
1530  {
1531 
1532 
1533  // --------- STAT is always the first step
1534  memset(&xrdreq, 0, sizeof (ClientRequest));
1535  xrdreq.stat.requestid = htons(kXR_stat);
1536  std::string s = resourceplusopaque.c_str();
1537 
1538 
1539  l = resourceplusopaque.length() + 1;
1540  xrdreq.stat.dlen = htonl(l);
1541 
1542  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1543  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1544  return -1;
1545  }
1546 
1547  // We need to be invoked again to complete the request
1548  return 0;
1549  }
1550  default:
1551 
1552  if (fileflags & kXR_isDir) {
1553  // --------- RMDIR
1554  memset(&xrdreq, 0, sizeof (ClientRequest));
1555  xrdreq.rmdir.requestid = htons(kXR_rmdir);
1556 
1557  std::string s = resourceplusopaque.c_str();
1558 
1559  l = s.length() + 1;
1560  xrdreq.rmdir.dlen = htonl(l);
1561 
1562  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1563  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rmdir request.", 0, false);
1564  return -1;
1565  }
1566  } else {
1567  // --------- DELETE
1568  memset(&xrdreq, 0, sizeof (ClientRequest));
1569  xrdreq.rm.requestid = htons(kXR_rm);
1570 
1571  std::string s = resourceplusopaque.c_str();
1572 
1573  l = s.length() + 1;
1574  xrdreq.rm.dlen = htonl(l);
1575 
1576  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1577  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rm request.", 0, false);
1578  return -1;
1579  }
1580  }
1581 
1582 
1583  // We don't want to be invoked again after this request is finished
1584  return 1;
1585 
1586  }
1587 
1588 
1589 
1590  }
1591  case XrdHttpReq::rtPATCH:
1592  {
1593  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported yet.", 0, false);
1594 
1595  return -1;
1596  }
1598  {
1599 
1600 
1601 
1602  switch (reqstate) {
1603 
1604  case 0: // Stat() and add the current item to the list of the things to send
1605  {
1606 
1607  if (length > 0) {
1608  TRACE(REQ, "Reading request body " << length << " bytes.");
1609  char *p = 0;
1610  // We have to specifically read all the request body
1611 
1612  if (prot->BuffgetData(length, &p, true) < length) {
1613  prot->SendSimpleResp(501, NULL, NULL, (char *) "Error in getting the PROPFIND request body.", 0, false);
1614  return -1;
1615  }
1616 
1617  if ((depth > 1) || (depth < 0)) {
1618  prot->SendSimpleResp(501, NULL, NULL, (char *) "Invalid depth value.", 0, false);
1619  return -1;
1620  }
1621 
1622 
1623  parseBody(p, length);
1624  }
1625 
1626 
1627  // --------- STAT is always the first step
1628  memset(&xrdreq, 0, sizeof (ClientRequest));
1629  xrdreq.stat.requestid = htons(kXR_stat);
1630  std::string s = resourceplusopaque.c_str();
1631 
1632 
1633  l = resourceplusopaque.length() + 1;
1634  xrdreq.stat.dlen = htonl(l);
1635 
1636  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1637  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1638  return -1;
1639  }
1640 
1641 
1642  if (depth == 0) {
1643  // We don't need to be invoked again
1644  return 1;
1645  } else
1646  // We need to be invoked again to complete the request
1647  return 0;
1648 
1649 
1650 
1651  break;
1652  }
1653 
1654  default: // Dirlist()
1655  {
1656 
1657  // --------- DIRLIST
1658  memset(&xrdreq, 0, sizeof (ClientRequest));
1660 
1661  std::string s = resourceplusopaque.c_str();
1663  //s += "?xrd.dirstat=1";
1664 
1665  l = s.length() + 1;
1666  xrdreq.dirlist.dlen = htonl(l);
1667 
1668  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1669  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1670  return -1;
1671  }
1672 
1673  // We don't want to be invoked again after this request is finished
1674  return 1;
1675  }
1676  }
1677 
1678 
1679  break;
1680  }
1681  case XrdHttpReq::rtMKCOL:
1682  {
1683 
1684  // --------- MKDIR
1685  memset(&xrdreq, 0, sizeof (ClientRequest));
1686  xrdreq.mkdir.requestid = htons(kXR_mkdir);
1687 
1688  std::string s = resourceplusopaque.c_str();
1690 
1691  l = s.length() + 1;
1692  xrdreq.mkdir.dlen = htonl(l);
1693 
1694  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1695  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1696  return -1;
1697  }
1698 
1699  // We don't want to be invoked again after this request is finished
1700  return 1;
1701  }
1702  case XrdHttpReq::rtMOVE:
1703  {
1704 
1705  // --------- MOVE
1706  memset(&xrdreq, 0, sizeof (ClientRequest));
1707  xrdreq.mv.requestid = htons(kXR_mv);
1708 
1709  std::string s = resourceplusopaque.c_str();
1710  s += " ";
1711 
1712  char buf[256];
1713  char *ppath;
1714  int port = 0;
1715  if (parseURL((char *) destination.c_str(), buf, port, &ppath)) {
1716  prot->SendSimpleResp(501, NULL, NULL, (char *) "Cannot parse destination url.", 0, false);
1717  return -1;
1718  }
1719 
1720  char buf2[256];
1721  strcpy(buf2, host.c_str());
1722  char *pos = strchr(buf2, ':');
1723  if (pos) *pos = '\0';
1724 
1725  // If we are a redirector we enforce that the host field is equal to
1726  // whatever was written in the destination url
1727  //
1728  // If we are a data server instead we cannot enforce anything, we will
1729  // just ignore the host part of the destination
1730  if ((prot->myRole == kXR_isManager) && strcmp(buf, buf2)) {
1731  prot->SendSimpleResp(501, NULL, NULL, (char *) "Only in-place renaming is supported for MOVE.", 0, false);
1732  return -1;
1733  }
1734 
1735 
1736 
1737 
1738  s += ppath;
1739 
1740  l = s.length() + 1;
1741  xrdreq.mv.dlen = htonl(l);
1743 
1744  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1745  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1746  return -1;
1747  }
1748 
1749  // We don't want to be invoked again after this request is finished
1750  return 1;
1751 
1752  }
1753  default:
1754  {
1755  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported.", 0, false);
1756  return -1;
1757  }
1758 
1759  }
1760 
1761  return 1;
1762 }
1763 
1764 
1765 int
1766 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1767  if (iovN > 0) {
1768  if (xrdresp == kXR_error) {
1769  prot->SendSimpleResp(httpStatusCode, NULL, NULL, "Failed to determine checksum", 0, false);
1770  return -1;
1771  }
1772 
1773  TRACEI(REQ, "Checksum for HEAD " << resource.c_str() << " "
1774  << reinterpret_cast<char *>(iovP[0].iov_base) << "="
1775  << reinterpret_cast<char *>(iovP[iovN-1].iov_base));
1776 
1777  bool convert_to_base64 = m_req_cksum->needsBase64Padding();
1778  char *digest_value = reinterpret_cast<char *>(iovP[iovN-1].iov_base);
1779  if (convert_to_base64) {
1780  size_t digest_length = strlen(digest_value);
1781  unsigned char *digest_binary_value = (unsigned char *)malloc(digest_length);
1782  if (!Fromhexdigest(reinterpret_cast<unsigned char *>(digest_value), digest_length, digest_binary_value)) {
1783  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to convert checksum hexdigest to base64.", 0, false);
1784  free(digest_binary_value);
1785  return -1;
1786  }
1787  char *digest_base64_value = (char *)malloc(digest_length + 1);
1788  // Binary length is precisely half the size of the hex-encoded digest_value; hence, divide length by 2.
1789  Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1790  free(digest_binary_value);
1791  digest_value = digest_base64_value;
1792  }
1793 
1794  digest_header = "Digest: ";
1795  digest_header += m_req_cksum->getHttpName();
1796  digest_header += "=";
1797  digest_header += digest_value;
1798  if (convert_to_base64) {free(digest_value);}
1799  return 0;
1800  } else {
1801  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(), false);
1802  return -1;
1803  }
1804 }
1805 
1806 int
1807 XrdHttpReq::PostProcessListing(bool final_) {
1808 
1809  if (xrdresp == kXR_error) {
1810  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1811  httpStatusText.c_str(), httpStatusText.length(), false);
1812  return -1;
1813  }
1814 
1815  if (stringresp.empty()) {
1816  // Start building the HTML response
1817  stringresp = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1818  "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1819  "<head>\n"
1820  "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1821  "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1822  "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1823 
1824  stringresp += "<title>";
1825  stringresp += resource.c_str();
1826  stringresp += "</title>\n";
1827 
1828  stringresp += "</head>\n"
1829  "<body>\n";
1830 
1831  char *estr = escapeXML(resource.c_str());
1832 
1833  stringresp += "<h1>Listing of: ";
1834  stringresp += estr;
1835  stringresp += "</h1>\n";
1836 
1837  free(estr);
1838 
1839  stringresp += "<div id=\"header\">";
1840 
1841  stringresp += "<table id=\"ft\">\n"
1842  "<thead><tr>\n"
1843  "<th class=\"mode\">Mode</th>"
1844  "<th class=\"flags\">Flags</th>"
1845  "<th class=\"size\">Size</th>"
1846  "<th class=\"datetime\">Modified</th>"
1847  "<th class=\"name\">Name</th>"
1848  "</tr></thead>\n";
1849  }
1850 
1851  // Now parse the answer building the entries vector
1852  if (iovN > 0) {
1853  char *startp = (char *) iovP[0].iov_base, *endp = 0;
1854  char entry[1024];
1855  DirListInfo e;
1856  while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) {
1857  // Find the filename, it comes before the \n
1858  if ((endp = (char *) strchr((const char*) startp, '\n'))) {
1859  strncpy(entry, (char *) startp, endp - startp);
1860  entry[endp - startp] = 0;
1861  e.path = entry;
1862 
1863  endp++;
1864 
1865  // Now parse the stat info
1866  TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry
1867  << " stat=" << endp);
1868 
1869  long dummyl;
1870  sscanf(endp, "%ld %lld %ld %ld",
1871  &dummyl,
1872  &e.size,
1873  &e.flags,
1874  &e.modtime);
1875  } else
1876  strcpy(entry, (char *) startp);
1877 
1878  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
1879  // The entry is filled. <td class="ft-file"><a href="file1.txt">file1.txt</a></td>
1880  std::string p = "<tr>"
1881  "<td class=\"mode\">";
1882 
1883  if (e.flags & kXR_isDir) p += "d";
1884  else p += "-";
1885 
1886  if (e.flags & kXR_other) p += "o";
1887  else p += "-";
1888 
1889  if (e.flags & kXR_offline) p += "O";
1890  else p += "-";
1891 
1892  if (e.flags & kXR_readable) p += "r";
1893  else p += "-";
1894 
1895  if (e.flags & kXR_writable) p += "w";
1896  else p += "-";
1897 
1898  if (e.flags & kXR_xset) p += "x";
1899  else p += "-";
1900 
1901  p += "</td>";
1902  p += "<td class=\"mode\">" + itos(e.flags) + "</td>"
1903  "<td class=\"size\">" + itos(e.size) + "</td>"
1904  "<td class=\"datetime\">" + ISOdatetime(e.modtime) + "</td>"
1905  "<td class=\"name\">"
1906  "<a href=\"";
1907 
1908  if (resource != "/") {
1909 
1910  char *estr = escapeXML(resource.c_str());
1911 
1912  p += estr;
1913  if (!p.empty() && p[p.size() - 1] != '/')
1914  p += "/";
1915 
1916  free(estr);
1917  }
1918 
1919  char *estr = escapeXML(e.path.c_str());
1920 
1921  p += e.path + "\">";
1922  p += e.path;
1923 
1924  free(estr);
1925 
1926  p += "</a></td></tr>";
1927 
1928  stringresp += p;
1929  }
1930 
1931  if (endp) {
1932  char *pp = (char *)strchr((const char *)endp, '\n');
1933  if (pp) startp = pp+1;
1934  else break;
1935  } else break;
1936 
1937  }
1938  }
1939 
1940  // If this was the last bunch of entries, send the buffer and empty it immediately
1941  if (final_) {
1942  stringresp += "</table></div><br><br><hr size=1>"
1943  "<p><span id=\"requestby\">Request by ";
1944 
1945  if (prot->SecEntity.name)
1946  stringresp += prot->SecEntity.name;
1947  else
1948  stringresp += prot->Link->ID;
1949 
1950  if (prot->SecEntity.vorg ||
1951  prot->SecEntity.name ||
1952  prot->SecEntity.moninfo ||
1953  prot->SecEntity.role)
1954  stringresp += " (";
1955 
1956  if (prot->SecEntity.vorg) {
1957  stringresp += " VO: ";
1958  stringresp += prot->SecEntity.vorg;
1959  }
1960 
1961  if (prot->SecEntity.moninfo) {
1962  stringresp += " DN: ";
1963  stringresp += prot->SecEntity.moninfo;
1964  } else
1965  if (prot->SecEntity.name) {
1966  stringresp += " DN: ";
1967  stringresp += prot->SecEntity.name;
1968  }
1969 
1970  if (prot->SecEntity.role) {
1971  stringresp += " Role: ";
1972  stringresp += prot->SecEntity.role;
1973  if (prot->SecEntity.endorsements) {
1974  stringresp += " (";
1976  stringresp += ") ";
1977  }
1978  }
1979 
1980  if (prot->SecEntity.vorg ||
1981  prot->SecEntity.moninfo ||
1982  prot->SecEntity.role)
1983  stringresp += " )";
1984 
1985  if (prot->SecEntity.host) {
1986  stringresp += " ( ";
1987  stringresp += prot->SecEntity.host;
1988  stringresp += " )";
1989  }
1990 
1991  stringresp += "</span></p>\n";
1992  stringresp += "<p>Powered by XrdHTTP ";
1993  stringresp += XrdVSTRING;
1994  stringresp += " (CERN IT-SDC)</p>\n";
1995 
1996  prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive);
1997  stringresp.clear();
1998  return keepalive ? 1 : -1;
1999  }
2000 
2001  return 0;
2002 }
2003 
2004 int
2005 XrdHttpReq::ReturnGetHeaders() {
2006  std::string responseHeader;
2007  if (!m_digest_header.empty()) {
2008  responseHeader = m_digest_header;
2009  }
2010  long one;
2011  if (filemodtime && XrdOucEnv::Import("XRDPFC", one)) {
2012  if (!responseHeader.empty()) {
2013  responseHeader += "\r\n";
2014  }
2015  long object_age = time(NULL) - filemodtime;
2016  responseHeader += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2017  }
2018 
2020  if (uranges.empty() && readRangeHandler.getError()) {
2021  prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false);
2022  return -1;
2023  }
2024 
2025  if (readRangeHandler.isFullFile()) {
2026  // Full file.
2027  TRACEI(REQ, "Sending full file: " << filesize);
2028  if (m_transfer_encoding_chunked && m_trailer_headers) {
2029  prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive);
2030  } else {
2031  prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive);
2032  }
2033  return 0;
2034  }
2035 
2037  // Possibly with zero sized file but should have been included
2038  // in the FullFile case above
2039  if (uranges.size() != 1)
2040  return -1;
2041 
2042  // Only one range to return to the user
2043  char buf[64];
2044  const off_t cnt = uranges[0].end - uranges[0].start + 1;
2045 
2046  XrdOucString s = "Content-Range: bytes ";
2047  sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize);
2048  s += buf;
2049  if (!responseHeader.empty()) {
2050  s += "\r\n";
2051  s += responseHeader.c_str();
2052  }
2053 
2054  if (m_transfer_encoding_chunked && m_trailer_headers) {
2055  prot->StartChunkedResp(206, NULL, (char *)s.c_str(), -1, keepalive);
2056  } else {
2057  prot->SendSimpleResp(206, NULL, (char *)s.c_str(), NULL, cnt, keepalive);
2058  }
2059  return 0;
2060  }
2061 
2062  // Multiple reads to perform, compose and send the header
2063  off_t cnt = 0;
2064  for (auto &ur : uranges) {
2065  cnt += ur.end - ur.start + 1;
2066 
2067  cnt += buildPartialHdr(ur.start,
2068  ur.end,
2069  filesize,
2070  (char *) "123456").size();
2071 
2072  }
2073  cnt += buildPartialHdrEnd((char *) "123456").size();
2074  std::string header = "Content-Type: multipart/byteranges; boundary=123456";
2075  if (!m_digest_header.empty()) {
2076  header += "\n";
2077  header += m_digest_header;
2078  }
2079 
2080  if (m_transfer_encoding_chunked && m_trailer_headers) {
2081  prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive);
2082  } else {
2083  prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive);
2084  }
2085  return 0;
2086 }
2087 
2088 // This is invoked by the callbacks, after something has happened in the bridge
2089 
2090 int XrdHttpReq::PostProcessHTTPReq(bool final_) {
2091 
2092  TRACEI(REQ, "PostProcessHTTPReq req: " << request << " reqstate: " << reqstate << " final_:" << final_);
2093  mapXrdErrorToHttpStatus();
2094 
2095  if(xrdreq.set.requestid == htons(kXR_set)) {
2096  // We have set the user agent, if it fails we return a 500 error, otherwise the callback is successful --> we continue
2097  if(xrdresp != kXR_ok) {
2098  prot->SendSimpleResp(500, nullptr, nullptr, "Could not set user agent.", 0, false);
2099  return -1;
2100  }
2101  return 0;
2102  }
2103 
2104  switch (request) {
2105  case XrdHttpReq::rtUnknown:
2106  {
2107  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 1", 0, false);
2108  return -1;
2109  }
2111  {
2112  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 2", 0, false);
2113  return -1;
2114  }
2115  case XrdHttpReq::rtHEAD:
2116  {
2117  if (xrdresp != kXR_ok) {
2118  // NOTE that HEAD MUST NOT return a body, even in the case of failure.
2119  prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, false);
2120  return -1;
2121  } else if (reqstate == 0) {
2122  if (iovN > 0) {
2123 
2124  // Now parse the stat info
2125  TRACEI(REQ, "Stat for HEAD " << resource.c_str()
2126  << " stat=" << (char *) iovP[0].iov_base);
2127 
2128  long dummyl;
2129  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2130  &dummyl,
2131  &filesize,
2132  &fileflags,
2133  &filemodtime);
2134 
2135  if (m_req_digest.size()) {
2136  return 0;
2137  } else {
2138  prot->SendSimpleResp(200, NULL, "Accept-Ranges: bytes", NULL, filesize, keepalive);
2139  return keepalive ? 1 : -1;
2140  }
2141  }
2142 
2143  prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, keepalive);
2144  bool ret_keepalive = keepalive; // reset() clears keepalive
2145  reset();
2146  return ret_keepalive ? 1 : -1;
2147  } else { // We requested a checksum and now have its response.
2148  if (iovN > 0) {
2149  std::string response_headers;
2150  int response = PostProcessChecksum(response_headers);
2151  if (-1 == response) {
2152  return -1;
2153  }
2154  if (!response_headers.empty()) {response_headers += "\r\n";}
2155  response_headers += "Accept-Ranges: bytes";
2156  prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2157  return keepalive ? 1 : -1;
2158  } else {
2159  prot->SendSimpleResp(500, NULL, NULL, "Underlying filesystem failed to calculate checksum.", 0, false);
2160  return -1;
2161  }
2162  }
2163  }
2164  case XrdHttpReq::rtGET:
2165  {
2166  // To duplicate the state diagram from the rtGET request state
2167  // - 0: Perform an open request
2168  // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
2169  // - 2: Perform a close (for directory listings only)
2170  // - 3: Perform a dirlist
2171  // - 4+: Reads from file; if at end, perform a close.
2172  switch (reqstate) {
2173  case 0: // open
2174  {
2175  if (xrdresp == kXR_ok) {
2176  fopened = true;
2177  getfhandle();
2178 
2179  // Always try to parse response. In the case of a caching proxy, the open
2180  // will have created the file in cache
2181  if (iovP[1].iov_len > 1) {
2182  TRACEI(REQ, "Stat for GET " << resource.c_str()
2183  << " stat=" << (char *) iovP[1].iov_base);
2184 
2185  long dummyl;
2186  sscanf((const char *) iovP[1].iov_base, "%ld %lld %ld %ld",
2187  &dummyl,
2188  &filesize,
2189  &fileflags,
2190  &filemodtime);
2191 
2192  // If this is a directory, bail out early; we will close the file handle
2193  // and then issue a directory listing.
2194  if (fileflags & kXR_isDir) {
2195  return 0;
2196  }
2197 
2199 
2200  // As above: if the client specified a response size, we use that.
2201  // Otherwise, utilize the filesize
2202  if (!length) {
2203  length = filesize;
2204  }
2205  }
2206  else {
2207  TRACEI(ALL, "GET returned no STAT information. Internal error?");
2208  prot->SendSimpleResp(500, NULL, NULL, "Storage system did not return stat info.", 0, false);
2209  return -1;
2210  }
2211  return 0;
2212  } else if (xrderrcode == kXR_isDirectory) { // This is a directory; trigger directory-handling topic.
2213  fileflags = kXR_isDir;
2214  return 0;
2215  } else { // xrdresp indicates an error occurred
2216 
2217  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2218  httpStatusText.c_str(), httpStatusText.length(), false);
2219  return -1;
2220  }
2221  // Case should not be reachable
2222  return -1;
2223  } // end open
2224  case 1: // checksum was requested and now we have its response.
2225  {
2226  return PostProcessChecksum(m_digest_header);
2227  }
2228  case 2: // close file handle in case of the directory
2229  {
2230  if (xrdresp != kXR_ok) {
2231  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2232  httpStatusText.c_str(), httpStatusText.length(), false);
2233  return -1;
2234  }
2235  return 0;
2236  }
2237  case 3: // handle the directory listing response
2238  {
2239  return PostProcessListing(final_);
2240  }
2241  default: //read or readv, followed by a close.
2242  {
2243  // If we are postprocessing a close, potentially send out informational trailers
2244  if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing)
2245  {
2247  if (rrerror) {
2248  httpStatusCode = rrerror.httpRetCode;
2249  httpStatusText = rrerror.errMsg;
2250  }
2251 
2252  if (m_transfer_encoding_chunked && m_trailer_headers) {
2253  if (prot->ChunkRespHeader(0))
2254  return -1;
2255 
2256  const std::string crlf = "\r\n";
2257  std::stringstream ss;
2258  ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf;
2259 
2260  const auto header = ss.str();
2261  if (prot->SendData(header.c_str(), header.size()))
2262  return -1;
2263 
2264  if (prot->ChunkRespFooter())
2265  return -1;
2266  }
2267 
2268  if (rrerror) return -1;
2269  return keepalive ? 1 : -1;
2270  }
2271 
2272  // On error, we can only send out a message if trailers are enabled and the
2273  // status response in trailer behavior is requested.
2274  if (xrdresp == kXR_error) {
2275  sendFooterError("");
2276  return -1;
2277  }
2278 
2279 
2280  TRACEI(REQ, "Got data vectors to send:" << iovN);
2281 
2282  XrdHttpIOList received;
2283  getReadResponse(received);
2284 
2285  int rc;
2287  rc = sendReadResponseSingleRange(received);
2288  } else {
2289  rc = sendReadResponsesMultiRanges(received);
2290  }
2291  if (rc) {
2292  // make sure readRangeHandler will trigger close
2293  // of file after next NextReadList().
2295  }
2296 
2297  return 0;
2298  } // end read or readv
2299 
2300  } // switch reqstate
2301  break;
2302  } // case GET
2303 
2304  case XrdHttpReq::rtPUT:
2305  {
2306  if (!fopened) {
2307 
2308  if (xrdresp != kXR_ok) {
2309 
2310  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2311  httpStatusText.c_str(), httpStatusText.length(), keepalive);
2312  return -1;
2313  }
2314 
2315  getfhandle();
2316  fopened = true;
2317 
2318  // We try to completely fill up our buffer before flushing
2319  prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2320 
2321  if (sendcontinue) {
2322  prot->SendSimpleResp(100, NULL, NULL, 0, 0, keepalive);
2323  return 0;
2324  }
2325 
2326  break;
2327  } else {
2328 
2329 
2330  // If we are here it's too late to send a proper error message...
2331  if (xrdresp == kXR_error) return -1;
2332 
2333  if (ntohs(xrdreq.header.requestid) == kXR_write) {
2334  int l = ntohl(xrdreq.write.dlen);
2335 
2336  // Consume the written bytes
2337  prot->BuffConsume(ntohl(xrdreq.write.dlen));
2338  writtenbytes += l;
2339 
2340  // Update the chunk offset
2341  if (m_transfer_encoding_chunked) {
2342  m_current_chunk_offset += l;
2343  }
2344 
2345  // We try to completely fill up our buffer before flushing
2346  prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2347 
2348  return 0;
2349  }
2350 
2351  if (ntohs(xrdreq.header.requestid) == kXR_close) {
2352  if (xrdresp == kXR_ok) {
2353  prot->SendSimpleResp(200, NULL, NULL, (char *) ":-)", 0, keepalive);
2354  return keepalive ? 1 : -1;
2355  } else {
2356  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2357  httpStatusText.c_str(), httpStatusText.length(), keepalive);
2358  return -1;
2359  }
2360  }
2361 
2362 
2363  }
2364 
2365 
2366 
2367 
2368 
2369  break;
2370  }
2371 
2372 
2373 
2374  case XrdHttpReq::rtDELETE:
2375  {
2376 
2377  if (xrdresp != kXR_ok) {
2378  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2379  httpStatusText.c_str(), httpStatusText.length(), keepalive);
2380  return -1;
2381  }
2382 
2383 
2384 
2385 
2386  switch (reqstate) {
2387 
2388  case 0: // response to stat()
2389  {
2390  if (iovN > 0) {
2391 
2392  // Now parse the stat info
2393  TRACEI(REQ, "Stat for removal " << resource.c_str()
2394  << " stat=" << (char *) iovP[0].iov_base);
2395 
2396  long dummyl;
2397  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2398  &dummyl,
2399  &filesize,
2400  &fileflags,
2401  &filemodtime);
2402  }
2403 
2404  return 0;
2405  }
2406  default: // response to rm
2407  {
2408  if (xrdresp == kXR_ok) {
2409  prot->SendSimpleResp(200, NULL, NULL, (char *) ":-)", 0, keepalive);
2410  return keepalive ? 1 : -1;
2411  }
2412  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2413  httpStatusText.c_str(), httpStatusText.length(), keepalive);
2414  return -1;
2415  }
2416  }
2417 
2418 
2419  }
2420 
2422  {
2423 
2424  if (xrdresp == kXR_error) {
2425  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2426  httpStatusText.c_str(), httpStatusText.length(), false);
2427  return -1;
2428  }
2429 
2430  switch (reqstate) {
2431 
2432  case 0: // response to stat()
2433  {
2434  DirListInfo e;
2435  e.size = 0;
2436  e.flags = 0;
2437 
2438  // Now parse the answer building the entries vector
2439  if (iovN > 0) {
2440  e.path = resource.c_str();
2441 
2442  // Now parse the stat info
2443  TRACEI(REQ, "Collection " << resource.c_str()
2444  << " stat=" << (char *) iovP[0].iov_base);
2445 
2446  long dummyl;
2447  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2448  &dummyl,
2449  &e.size,
2450  &e.flags,
2451  &e.modtime);
2452 
2453  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2454  /* The entry is filled. */
2455 
2456 
2457  std::string p;
2458  stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2459 
2460  char *estr = escapeXML(e.path.c_str());
2461 
2462  stringresp += "<D:href>";
2463  stringresp += estr;
2464  stringresp += "</D:href>\n";
2465 
2466  free(estr);
2467 
2468  stringresp += "<D:propstat>\n<D:prop>\n";
2469 
2470  // Now add the properties that we have to add
2471 
2472  // File size
2473  stringresp += "<lp1:getcontentlength>";
2474  stringresp += itos(e.size);
2475  stringresp += "</lp1:getcontentlength>\n";
2476 
2477 
2478 
2479  stringresp += "<lp1:getlastmodified>";
2481  stringresp += "</lp1:getlastmodified>\n";
2482 
2483 
2484 
2485  if (e.flags & kXR_isDir) {
2486  stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2487  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2488  } else {
2489  stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2490  }
2491 
2492  if (e.flags & kXR_xset) {
2493  stringresp += "<lp1:executable>T</lp1:executable>\n";
2494  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2495  } else {
2496  stringresp += "<lp1:executable>F</lp1:executable>\n";
2497  }
2498 
2499 
2500 
2501  stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2502 
2503 
2504  }
2505 
2506 
2507  }
2508 
2509  // If this was the last bunch of entries, send the buffer and empty it immediately
2510  if ((depth == 0) || !(e.flags & kXR_isDir)) {
2511  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";
2512  stringresp.insert(0, s);
2513  stringresp += "</D:multistatus>\n";
2514  prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2515  (char *) stringresp.c_str(), stringresp.length(), keepalive);
2516  stringresp.clear();
2517  return keepalive ? 1 : -1;
2518  }
2519 
2520  break;
2521  }
2522  default: // response to dirlist()
2523  {
2524 
2525 
2526  // Now parse the answer building the entries vector
2527  if (iovN > 0) {
2528  char *startp = (char *) iovP[0].iov_base, *endp = 0;
2529  char entry[1024];
2530  DirListInfo e;
2531 
2532  while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)(iovP[0].iov_len - 1) ) {
2533  // Find the filename, it comes before the \n
2534  if ((endp = (char *) mystrchrnul((const char*) startp, '\n'))) {
2535  strncpy(entry, (char *) startp, endp - startp);
2536  entry[endp - startp] = 0;
2537  e.path = entry;
2538 
2539  endp++;
2540 
2541  // Now parse the stat info
2542  TRACEI(REQ, "Dirlist " <<resource.c_str() <<" entry=" <<entry
2543  << " stat=" << endp);
2544 
2545  long dummyl;
2546  sscanf(endp, "%ld %lld %ld %ld",
2547  &dummyl,
2548  &e.size,
2549  &e.flags,
2550  &e.modtime);
2551  }
2552 
2553 
2554  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2555  /* The entry is filled.
2556 
2557  <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/" xmlns:lp3="LCGDM:">
2558  <D:href>/dpm/cern.ch/home/testers2.eu-emi.eu/</D:href>
2559  <D:propstat>
2560  <D:prop>
2561  <lp1:getcontentlength>1</lp1:getcontentlength>
2562  <lp1:getlastmodified>Tue, 01 May 2012 02:42:13 GMT</lp1:getlastmodified>
2563  <lp1:resourcetype>
2564  <D:collection/>
2565  </lp1:resourcetype>
2566  </D:prop>
2567  <D:status>HTTP/1.1 200 OK</D:status>
2568  </D:propstat>
2569  </D:response>
2570  */
2571 
2572 
2573  std::string p = resource.c_str();
2574  if (*p.rbegin() != '/') p += "/";
2575 
2576  p += e.path;
2577 
2578  stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2579 
2580  char *estr = escapeXML(p.c_str());
2581  stringresp += "<D:href>";
2582  stringresp += estr;
2583  stringresp += "</D:href>\n";
2584  free(estr);
2585 
2586  stringresp += "<D:propstat>\n<D:prop>\n";
2587 
2588 
2589 
2590  // Now add the properties that we have to add
2591 
2592  // File size
2593  stringresp += "<lp1:getcontentlength>";
2594  stringresp += itos(e.size);
2595  stringresp += "</lp1:getcontentlength>\n";
2596 
2597  stringresp += "<lp1:getlastmodified>";
2599  stringresp += "</lp1:getlastmodified>\n";
2600 
2601  if (e.flags & kXR_isDir) {
2602  stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2603  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2604  } else {
2605  stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2606  }
2607 
2608  if (e.flags & kXR_xset) {
2609  stringresp += "<lp1:executable>T</lp1:executable>\n";
2610  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2611  } else {
2612  stringresp += "<lp1:executable>F</lp1:executable>\n";
2613  }
2614 
2615  stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2616 
2617 
2618  }
2619 
2620 
2621 
2622  if (endp) {
2623  char *pp = (char *)strchr((const char *)endp, '\n');
2624  if (pp) startp = pp+1;
2625  else break;
2626  } else break;
2627 
2628  }
2629  }
2630 
2631 
2632 
2633  // If this was the last bunch of entries, send the buffer and empty it immediately
2634  if (final_) {
2635  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";
2636  stringresp.insert(0, s);
2637  stringresp += "</D:multistatus>\n";
2638  prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2639  (char *) stringresp.c_str(), stringresp.length(), keepalive);
2640  stringresp.clear();
2641  return keepalive ? 1 : -1;
2642  }
2643 
2644  break;
2645  } // default reqstate
2646  } // switch reqstate
2647 
2648 
2649  break;
2650 
2651  } // case propfind
2652 
2653  case XrdHttpReq::rtMKCOL:
2654  {
2655 
2656  if (xrdresp != kXR_ok) {
2657  if (xrderrcode == kXR_ItExists) {
2658  prot->SendSimpleResp(405, NULL, NULL, (char *) "Method is not allowed; resource already exists.", 0, false);
2659  } else {
2660  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2661  httpStatusText.c_str(), httpStatusText.length(), false);
2662  }
2663  return -1;
2664  }
2665 
2666  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2667  return keepalive ? 1 : -1;
2668 
2669  }
2670  case XrdHttpReq::rtMOVE:
2671  {
2672 
2673  if (xrdresp != kXR_ok) {
2674  prot->SendSimpleResp(httpStatusCode, NULL, NULL, (char *) etext.c_str(), 0, false);
2675  return -1;
2676  }
2677 
2678  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2679  return keepalive ? 1 : -1;
2680 
2681  }
2682 
2683  default:
2684  break;
2685 
2686  }
2687 
2688 
2689  switch (xrdresp) {
2690  case kXR_error:
2691  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2692  httpStatusText.c_str(), httpStatusText.length(), false);
2693  return -1;
2694  break;
2695 
2696  default:
2697 
2698  break;
2699  }
2700 
2701 
2702  return 0;
2703 }
2704 
2705 void
2706 XrdHttpReq::sendFooterError(const std::string &extra_text) {
2707  if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2708  // A trailer header is appropriate in this case; this is signified by
2709  // a chunk with size zero, then the trailer, then a crlf.
2710  //
2711  // We only send the status trailer when explicitly requested; otherwise a
2712  // "normal" HTTP client might simply see a short response and think it's a
2713  // success
2714 
2715  if (prot->ChunkRespHeader(0))
2716  return;
2717 
2718  std::stringstream ss;
2719  ss << httpStatusCode << ": " << httpStatusText;
2720  if (!extra_text.empty())
2721  ss << ": " << extra_text;
2722  TRACEI(REQ, ss.str());
2723  ss << "\r\n";
2724 
2725  const auto header = "X-Transfer-Status: " + ss.str();
2726  if (prot->SendData(header.c_str(), header.size()))
2727  return;
2728 
2729  prot->ChunkRespFooter();
2730  } else {
2731  TRACEI(REQ, httpStatusCode << ": " << httpStatusText << (extra_text.empty() ? "" : (": " + extra_text)));
2732  }
2733 }
2734 
2736 
2737  TRACE(REQ, " XrdHttpReq request ended.");
2738 
2739  //if (xmlbody) xmlFreeDoc(xmlbody);
2741  readClosing = false;
2742  writtenbytes = 0;
2743  etext.clear();
2744  redirdest = "";
2745 
2746  // // Here we should deallocate this
2747  // const struct iovec *iovP //!< pointer to data array
2748  // int iovN, //!< array count
2749  // int iovL, //!< byte count
2750  // bool final //!< true -> final result
2751 
2752 
2753  //xmlbody = 0;
2754  depth = 0;
2757  ralist.clear();
2758  ralist.shrink_to_fit();
2759 
2760  request = rtUnset;
2761  resource = "";
2762  allheaders.clear();
2763 
2764  // Reset the state of the request's digest request.
2765  m_req_digest.clear();
2766  m_digest_header.clear();
2767  m_req_cksum = nullptr;
2768 
2770  m_user_agent = "";
2771 
2772  headerok = false;
2773  keepalive = true;
2774  length = 0;
2775  filesize = 0;
2776  depth = 0;
2777  sendcontinue = false;
2778 
2779  m_transfer_encoding_chunked = false;
2780  m_current_chunk_size = -1;
2781  m_current_chunk_offset = 0;
2782 
2783  m_trailer_headers = false;
2784  m_status_trailer = false;
2785 
2787  reqstate = 0;
2788 
2789  memset(&xrdreq, 0, sizeof (xrdreq));
2790  memset(&xrdresp, 0, sizeof (xrdresp));
2792 
2793  etext.clear();
2794  redirdest = "";
2795 
2796  stringresp = "";
2797 
2798  host = "";
2799  destination = "";
2800  hdr2cgistr = "";
2801  m_appended_hdr2cgistr = false;
2802 
2803  iovP = 0;
2804  iovN = 0;
2805  iovL = 0;
2806 
2807 
2808  if (opaque) delete(opaque);
2809  opaque = 0;
2810 
2811  fopened = false;
2812 
2813  final = false;
2814 
2815  mScitag = -1;
2816 }
2817 
2818 void XrdHttpReq::getfhandle() {
2819 
2820  memcpy(fhandle, iovP[0].iov_base, 4);
2821  TRACEI(REQ, "fhandle:" <<
2822  (int) fhandle[0] << ":" << (int) fhandle[1] << ":" << (int) fhandle[2] << ":" << (int) fhandle[3]);
2823 
2824 }
2825 
2826 void XrdHttpReq::getReadResponse(XrdHttpIOList &received) {
2827  received.clear();
2828 
2829  if (ntohs(xrdreq.header.requestid) == kXR_readv) {
2830  readahead_list *l;
2831  char *p;
2832  kXR_int32 len;
2833 
2834  // Cycle on all the data that is coming from the server
2835  for (int i = 0; i < iovN; i++) {
2836 
2837  for (p = (char *) iovP[i].iov_base; p < (char *) iovP[i].iov_base + iovP[i].iov_len;) {
2838  l = (readahead_list *) p;
2839  len = ntohl(l->rlen);
2840 
2841  received.emplace_back(p+sizeof(readahead_list), -1, len);
2842 
2843  p += sizeof (readahead_list);
2844  p += len;
2845 
2846  }
2847  }
2848  return;
2849  }
2850 
2851  // kXR_read result
2852  for (int i = 0; i < iovN; i++) {
2853  received.emplace_back((char*)iovP[i].iov_base, -1, iovP[i].iov_len);
2854  }
2855 
2856 }
2857 
2858 int XrdHttpReq::sendReadResponsesMultiRanges(const XrdHttpIOList &received) {
2859 
2860  if (received.size() == 0) {
2861  bool start, finish;
2862  if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2863  return -1;
2864  }
2865  return 0;
2866  }
2867 
2868  // user is expecting multiple ranges, we must be prepared to send an
2869  // individual header for each and format it according to the http rules
2870 
2871  struct rinfo {
2872  bool start;
2873  bool finish;
2874  const XrdOucIOVec2 *ci;
2876  std::string st_header;
2877  std::string fin_header;
2878  };
2879 
2880  // report each received byte chunk to the range handler and record the details
2881  // of original user range it related to and if starts a range or finishes all.
2882  // also sum the total of the headers and data which need to be sent to the user,
2883  // in case we need it for chunked transfer encoding
2884  std::vector<rinfo> rvec;
2885  off_t sum_len = 0;
2886 
2887  rvec.reserve(received.size());
2888 
2889  for(const auto &rcv: received) {
2890  rinfo rentry;
2891  bool start, finish;
2893 
2894  if (readRangeHandler.NotifyReadResult(rcv.size, &ur, start, finish) < 0) {
2895  return -1;
2896  }
2897  rentry.ur = ur;
2898  rentry.start = start;
2899  rentry.finish = finish;
2900  rentry.ci = &rcv;
2901 
2902  if (start) {
2903  std::string s = buildPartialHdr(ur->start,
2904  ur->end,
2905  filesize,
2906  (char *) "123456");
2907 
2908  rentry.st_header = s;
2909  sum_len += s.size();
2910  }
2911 
2912  sum_len += rcv.size;
2913 
2914  if (finish) {
2915  std::string s = buildPartialHdrEnd((char *) "123456");
2916  rentry.fin_header = s;
2917  sum_len += s.size();
2918  }
2919 
2920  rvec.push_back(rentry);
2921  }
2922 
2923 
2924  // Send chunked encoding header
2925  if (m_transfer_encoding_chunked && m_trailer_headers) {
2926  prot->ChunkRespHeader(sum_len);
2927  }
2928 
2929  // send the user the headers / data
2930  for(const auto &rentry: rvec) {
2931 
2932  if (rentry.start) {
2933  TRACEI(REQ, "Sending multipart: " << rentry.ur->start << "-" << rentry.ur->end);
2934  if (prot->SendData((char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2935  return -1;
2936  }
2937  }
2938 
2939  // Send all the data we have
2940  if (prot->SendData((char *) rentry.ci->data, rentry.ci->size)) {
2941  return -1;
2942  }
2943 
2944  if (rentry.finish) {
2945  if (prot->SendData((char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2946  return -1;
2947  }
2948  }
2949  }
2950 
2951  // Send chunked encoding footer
2952  if (m_transfer_encoding_chunked && m_trailer_headers) {
2953  prot->ChunkRespFooter();
2954  }
2955 
2956  return 0;
2957 }
2958 
2959 int XrdHttpReq::sendReadResponseSingleRange(const XrdHttpIOList &received) {
2960  // single range http transfer
2961 
2962  if (received.size() == 0) {
2963  bool start, finish;
2964  if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2965  return -1;
2966  }
2967  return 0;
2968  }
2969 
2970  off_t sum = 0;
2971  // notify the range handler and return if error
2972  for(const auto &rcv: received) {
2973  bool start, finish;
2974  if (readRangeHandler.NotifyReadResult(rcv.size, nullptr, start, finish) < 0) {
2975  return -1;
2976  }
2977  sum += rcv.size;
2978  }
2979 
2980  // Send chunked encoding header
2981  if (m_transfer_encoding_chunked && m_trailer_headers) {
2982  prot->ChunkRespHeader(sum);
2983  }
2984  for(const auto &rcv: received) {
2985  if (prot->SendData((char *) rcv.data, rcv.size)) return -1;
2986  }
2987  if (m_transfer_encoding_chunked && m_trailer_headers) {
2988  prot->ChunkRespFooter();
2989  }
2990  return 0;
2991 }
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:320
char fhandle[4]
Definition: XrdHttpReq.hh:313
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:299
std::string stringresp
If we want to give a string as a response, we compose it here.
Definition: XrdHttpReq.hh:317
XResponseType xrdresp
The last response data we got.
Definition: XrdHttpReq.hh:297
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:323
XrdOucEnv * opaque
The opaque data, after parsing.
Definition: XrdHttpReq.hh:246
long fileflags
Definition: XrdHttpReq.hh:310
int iovL
byte count
Definition: XrdHttpReq.hh:305
bool fopened
Definition: XrdHttpReq.hh:314
const struct iovec * iovP
The latest data chunks got from the xrd layer. These are valid only inside the callbacks!
Definition: XrdHttpReq.hh:303
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:311
int parseFirstLine(char *line, int len)
Parse the first line of the header.
Definition: XrdHttpReq.cc:256
XrdOucString redirdest
Definition: XrdHttpReq.hh:300
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:304
XrdOucString m_resource_with_digest
Definition: XrdHttpReq.hh:277
long long filesize
Definition: XrdHttpReq.hh:309
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:298
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:294
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:2735
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:204
char * Get(const char *varname)
Definition: XrdOucEnv.hh:69
char * Env(int &envlen)
Definition: XrdOucEnv.hh:48
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