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