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