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