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  // Incase of a move cgi parameters present in the CGI str
1620  // are appended to the destination in case of a MOVE.
1621  if (resourceplusopaque != "") {
1622  int pos = resourceplusopaque.find("?");
1623  if (pos != STR_NPOS) {
1624  destination.append((destination.find("?") == std::string::npos) ? "?" : "&");
1625  destination.append(resourceplusopaque.c_str() + pos + 1);
1626  }
1627  }
1628 
1629  // --------- MOVE
1630  memset(&xrdreq, 0, sizeof (ClientRequest));
1631  xrdreq.mv.requestid = htons(kXR_mv);
1632 
1633  std::string s = resourceplusopaque.c_str();
1634  s += " ";
1635 
1636  char buf[256];
1637  char *ppath;
1638  int port = 0;
1639  if (parseURL((char *) destination.c_str(), buf, port, &ppath)) {
1640  prot->SendSimpleResp(501, NULL, NULL, (char *) "Cannot parse destination url.", 0, false);
1641  return -1;
1642  }
1643 
1644  char buf2[256];
1645  strcpy(buf2, host.c_str());
1646  char *pos = strchr(buf2, ':');
1647  if (pos) *pos = '\0';
1648 
1649  // If we are a redirector we enforce that the host field is equal to
1650  // whatever was written in the destination url
1651  //
1652  // If we are a data server instead we cannot enforce anything, we will
1653  // just ignore the host part of the destination
1654  if ((prot->myRole == kXR_isManager) && strcmp(buf, buf2)) {
1655  prot->SendSimpleResp(501, NULL, NULL, (char *) "Only in-place renaming is supported for MOVE.", 0, false);
1656  return -1;
1657  }
1658 
1659 
1660 
1661 
1662  s += ppath;
1663 
1664  l = s.length() + 1;
1665  xrdreq.mv.dlen = htonl(l);
1667 
1668  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1669  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1670  return -1;
1671  }
1672 
1673  // We don't want to be invoked again after this request is finished
1674  return 1;
1675 
1676  }
1677  default:
1678  {
1679  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported.", 0, false);
1680  return -1;
1681  }
1682 
1683  }
1684 
1685  return 1;
1686 }
1687 
1688 
1689 int
1690 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1691  if (iovN > 0) {
1692  if (xrdresp == kXR_error) {
1693  prot->SendSimpleResp(httpStatusCode, NULL, NULL, "Failed to determine checksum", 0, false);
1694  return -1;
1695  }
1696 
1697  TRACEI(REQ, "Checksum for HEAD " << resource.c_str() << " "
1698  << reinterpret_cast<char *>(iovP[0].iov_base) << "="
1699  << reinterpret_cast<char *>(iovP[iovN-1].iov_base));
1700 
1701  bool convert_to_base64 = m_req_cksum->needsBase64Padding();
1702  char *digest_value = reinterpret_cast<char *>(iovP[iovN-1].iov_base);
1703  if (convert_to_base64) {
1704  size_t digest_length = strlen(digest_value);
1705  unsigned char *digest_binary_value = (unsigned char *)malloc(digest_length);
1706  if (!Fromhexdigest(reinterpret_cast<unsigned char *>(digest_value), digest_length, digest_binary_value)) {
1707  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to convert checksum hexdigest to base64.", 0, false);
1708  free(digest_binary_value);
1709  return -1;
1710  }
1711  char *digest_base64_value = (char *)malloc(digest_length + 1);
1712  // Binary length is precisely half the size of the hex-encoded digest_value; hence, divide length by 2.
1713  Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1714  free(digest_binary_value);
1715  digest_value = digest_base64_value;
1716  }
1717 
1718  digest_header = "Digest: ";
1719  digest_header += m_req_cksum->getHttpName();
1720  digest_header += "=";
1721  digest_header += digest_value;
1722  if (convert_to_base64) {free(digest_value);}
1723  return 0;
1724  } else {
1725  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1726  return -1;
1727  }
1728 }
1729 
1730 int
1731 XrdHttpReq::PostProcessListing(bool final_) {
1732 
1733  if (xrdresp == kXR_error) {
1734  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1735  httpErrorBody.c_str(), httpErrorBody.length(), false);
1736  return -1;
1737  }
1738 
1739  if (stringresp.empty()) {
1740  // Start building the HTML response
1741  stringresp = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1742  "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1743  "<head>\n"
1744  "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1745  "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1746  "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1747 
1748  stringresp += "<title>";
1749  stringresp += resource.c_str();
1750  stringresp += "</title>\n";
1751 
1752  stringresp += "</head>\n"
1753  "<body>\n";
1754 
1755  char *estr = escapeXML(resource.c_str());
1756 
1757  stringresp += "<h1>Listing of: ";
1758  stringresp += estr;
1759  stringresp += "</h1>\n";
1760 
1761  free(estr);
1762 
1763  stringresp += "<div id=\"header\">";
1764 
1765  stringresp += "<table id=\"ft\">\n"
1766  "<thead><tr>\n"
1767  "<th class=\"mode\">Mode</th>"
1768  "<th class=\"flags\">Flags</th>"
1769  "<th class=\"size\">Size</th>"
1770  "<th class=\"datetime\">Modified</th>"
1771  "<th class=\"name\">Name</th>"
1772  "</tr></thead>\n";
1773  }
1774 
1775  // Now parse the answer building the entries vector
1776  if (iovN > 0) {
1777  char *startp = (char *) iovP[0].iov_base, *endp = 0;
1778  char entry[1024];
1779  DirListInfo e;
1780  while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) {
1781  // Find the filename, it comes before the \n
1782  if ((endp = (char *) strchr((const char*) startp, '\n'))) {
1783  strncpy(entry, (char *) startp, endp - startp);
1784  entry[endp - startp] = 0;
1785  e.path = entry;
1786 
1787  endp++;
1788 
1789  // Now parse the stat info
1790  TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry
1791  << " stat=" << endp);
1792 
1793  long dummyl;
1794  sscanf(endp, "%ld %lld %ld %ld",
1795  &dummyl,
1796  &e.size,
1797  &e.flags,
1798  &e.modtime);
1799  } else
1800  strcpy(entry, (char *) startp);
1801 
1802  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
1803  // The entry is filled. <td class="ft-file"><a href="file1.txt">file1.txt</a></td>
1804  std::string p = "<tr>"
1805  "<td class=\"mode\">";
1806 
1807  if (e.flags & kXR_isDir) p += "d";
1808  else p += "-";
1809 
1810  if (e.flags & kXR_other) p += "o";
1811  else p += "-";
1812 
1813  if (e.flags & kXR_offline) p += "O";
1814  else p += "-";
1815 
1816  if (e.flags & kXR_readable) p += "r";
1817  else p += "-";
1818 
1819  if (e.flags & kXR_writable) p += "w";
1820  else p += "-";
1821 
1822  if (e.flags & kXR_xset) p += "x";
1823  else p += "-";
1824 
1825  p += "</td>";
1826  p += "<td class=\"mode\">" + itos(e.flags) + "</td>"
1827  "<td class=\"size\">" + itos(e.size) + "</td>"
1828  "<td class=\"datetime\">" + ISOdatetime(e.modtime) + "</td>"
1829  "<td class=\"name\">"
1830  "<a href=\"";
1831 
1832  if (resource != "/") {
1833 
1834  char *estr = escapeXML(resource.c_str());
1835 
1836  p += estr;
1837  if (!p.empty() && p[p.size() - 1] != '/')
1838  p += "/";
1839 
1840  free(estr);
1841  }
1842  std::unique_ptr<char, decltype(&free)> estr(escapeXML(e.path.c_str()), &free);
1843  p += estr.get();
1844  if (e.flags & kXR_isDir) p += "/";
1845  p += "\">";
1846  p += estr.get();
1847  if (e.flags & kXR_isDir) p += "/";
1848  p += "</a></td></tr>";
1849 
1850  stringresp += p;
1851  }
1852 
1853  if (endp) {
1854  char *pp = (char *)strchr((const char *)endp, '\n');
1855  if (pp) startp = pp+1;
1856  else break;
1857  } else break;
1858 
1859  }
1860  }
1861 
1862  // If this was the last bunch of entries, send the buffer and empty it immediately
1863  if (final_) {
1864  stringresp += "</table></div><br><br><hr size=1>"
1865  "<p><span id=\"requestby\">Request by ";
1866 
1867  if (prot->SecEntity.name)
1868  stringresp += prot->SecEntity.name;
1869  else
1870  stringresp += prot->Link->ID;
1871 
1872  if (prot->SecEntity.vorg ||
1873  prot->SecEntity.name ||
1874  prot->SecEntity.moninfo ||
1875  prot->SecEntity.role)
1876  stringresp += " (";
1877 
1878  if (prot->SecEntity.vorg) {
1879  stringresp += " VO: ";
1880  stringresp += prot->SecEntity.vorg;
1881  }
1882 
1883  if (prot->SecEntity.moninfo) {
1884  stringresp += " DN: ";
1885  stringresp += prot->SecEntity.moninfo;
1886  } else
1887  if (prot->SecEntity.name) {
1888  stringresp += " DN: ";
1889  stringresp += prot->SecEntity.name;
1890  }
1891 
1892  if (prot->SecEntity.role) {
1893  stringresp += " Role: ";
1894  stringresp += prot->SecEntity.role;
1895  if (prot->SecEntity.endorsements) {
1896  stringresp += " (";
1898  stringresp += ") ";
1899  }
1900  }
1901 
1902  if (prot->SecEntity.vorg ||
1903  prot->SecEntity.moninfo ||
1904  prot->SecEntity.role)
1905  stringresp += " )";
1906 
1907  if (prot->SecEntity.host) {
1908  stringresp += " ( ";
1909  stringresp += prot->SecEntity.host;
1910  stringresp += " )";
1911  }
1912 
1913  stringresp += "</span></p>\n";
1914  stringresp += "<p>Powered by XrdHTTP ";
1915  stringresp += XrdVSTRING;
1916  stringresp += " (CERN IT-SDC)</p>\n";
1917 
1918  prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive);
1919  stringresp.clear();
1920  return keepalive ? 1 : -1;
1921  }
1922 
1923  return 0;
1924 }
1925 
1926 int
1927 XrdHttpReq::ReturnGetHeaders() {
1928  std::string responseHeader;
1929  if (!m_digest_header.empty()) {
1930  responseHeader = m_digest_header;
1931  }
1932  if (fileflags & kXR_cachersp) {
1933  if (!responseHeader.empty()) {
1934  responseHeader += "\r\n";
1935  }
1936  addAgeHeader(responseHeader);
1937  }
1938 
1940  if (uranges.empty() && readRangeHandler.getError()) {
1941  prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false);
1942  return -1;
1943  }
1944 
1945  if (readRangeHandler.isFullFile()) {
1946  // Full file.
1947  TRACEI(REQ, "Sending full file: " << filesize);
1948  if (m_transfer_encoding_chunked && m_trailer_headers) {
1949  setTransferStatusHeader(responseHeader);
1950  prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive);
1951  } else {
1952  prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive);
1953  }
1954  return 0;
1955  }
1956 
1958  // Possibly with zero sized file but should have been included
1959  // in the FullFile case above
1960  if (uranges.size() != 1)
1961  return -1;
1962 
1963  // Only one range to return to the user
1964  char buf[64];
1965  const off_t cnt = uranges[0].end - uranges[0].start + 1;
1966 
1967  std::string header = "Content-Range: bytes ";
1968  sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize);
1969  header += buf;
1970  if (!responseHeader.empty()) {
1971  header += "\r\n";
1972  header += responseHeader.c_str();
1973  }
1974 
1975  if (m_transfer_encoding_chunked && m_trailer_headers) {
1976  setTransferStatusHeader(header);
1977  prot->StartChunkedResp(206, NULL, header.empty() ? nullptr : header.c_str(), -1, keepalive);
1978  } else {
1979  prot->SendSimpleResp(206, NULL, header.empty() ? nullptr : header.c_str(), NULL, cnt, keepalive);
1980  }
1981  return 0;
1982  }
1983 
1984  // Multiple reads to perform, compose and send the header
1985  off_t cnt = 0;
1986  for (auto &ur : uranges) {
1987  cnt += ur.end - ur.start + 1;
1988 
1989  cnt += buildPartialHdr(ur.start,
1990  ur.end,
1991  filesize,
1992  (char *) "123456").size();
1993 
1994  }
1995  cnt += buildPartialHdrEnd((char *) "123456").size();
1996  std::string header = "Content-Type: multipart/byteranges; boundary=123456";
1997  if (!m_digest_header.empty()) {
1998  header += "\n";
1999  header += m_digest_header;
2000  }
2001  if (fileflags & kXR_cachersp) {
2002  if (!header.empty()) {
2003  header += "\r\n";
2004  }
2005  addAgeHeader(header);
2006  }
2007 
2008  if (m_transfer_encoding_chunked && m_trailer_headers) {
2009  setTransferStatusHeader(header);
2010  prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive);
2011  } else {
2012  prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive);
2013  }
2014  return 0;
2015 }
2016 
2017 void XrdHttpReq::setTransferStatusHeader(std::string &header) {
2018  if (m_status_trailer) {
2019  if (header.empty()) {
2020  header += "Trailer: X-Transfer-Status";
2021  } else {
2022  header += "\r\nTrailer: X-Transfer-Status";
2023  }
2024  }
2025 }
2026 
2027 // This is invoked by the callbacks, after something has happened in the bridge
2028 
2029 int XrdHttpReq::PostProcessHTTPReq(bool final_) {
2030 
2031  TRACEI(REQ, "PostProcessHTTPReq req: " << request << " reqstate: " << reqstate << " final_:" << final_);
2032  generateWebdavErrMsg();
2033 
2034  if(xrdreq.set.requestid == htons(kXR_set)) {
2035  // We have set the user agent, if it fails we return a 500 error, otherwise the callback is successful --> we continue
2036  if(xrdresp != kXR_ok) {
2037  prot->SendSimpleResp(500, nullptr, nullptr, "Could not set user agent.", 0, false);
2038  return -1;
2039  }
2040  return 0;
2041  }
2042 
2043  switch (request) {
2044  case XrdHttpReq::rtUnknown:
2045  {
2046  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 1", 0, false);
2047  return -1;
2048  }
2050  {
2051  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 2", 0, false);
2052  return -1;
2053  }
2054  case XrdHttpReq::rtHEAD:
2055  {
2056  if (xrdresp != kXR_ok) {
2057  // NOTE that HEAD MUST NOT return a body, even in the case of failure.
2058  prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, false);
2059  return -1;
2060  } else if (reqstate == 0) {
2061  if (iovN > 0) {
2062  std::string response_headers;
2063 
2064  // Now parse the stat info
2065  TRACEI(REQ, "Stat for HEAD " << resource.c_str()
2066  << " stat=" << (char *) iovP[0].iov_base);
2067 
2068  long dummyl;
2069  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2070  &dummyl,
2071  &filesize,
2072  &fileflags,
2073  &filemodtime);
2074 
2075  if (m_req_digest.size()) {
2076  return 0;
2077  } else {
2078  if (fileflags & kXR_cachersp) {
2079  addAgeHeader(response_headers);
2080  response_headers += "\r\n";
2081  }
2082  response_headers += "Accept-Ranges: bytes";
2083  prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2084  return keepalive ? 1 : -1;
2085  }
2086  }
2087 
2088  prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, keepalive);
2089  bool ret_keepalive = keepalive; // reset() clears keepalive
2090  reset();
2091  return ret_keepalive ? 1 : -1;
2092  } else { // We requested a checksum and now have its response.
2093  if (iovN > 0) {
2094  std::string response_headers;
2095  int response = PostProcessChecksum(response_headers);
2096  if (-1 == response) {
2097  return -1;
2098  }
2099  if (!response_headers.empty()) {response_headers += "\r\n";}
2100  if (fileflags & kXR_cachersp) {
2101  addAgeHeader(response_headers);
2102  response_headers += "\r\n";
2103  }
2104  response_headers += "Accept-Ranges: bytes";
2105  prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2106  return keepalive ? 1 : -1;
2107  } else {
2108  prot->SendSimpleResp(500, NULL, NULL, "Underlying filesystem failed to calculate checksum.", 0, false);
2109  return -1;
2110  }
2111  }
2112  }
2113  case XrdHttpReq::rtGET:
2114  {
2115  // To duplicate the state diagram from the rtGET request state
2116  // - 0: Perform an open request
2117  // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
2118  // - 2: Perform a close (for directory listings only)
2119  // - 3: Perform a dirlist
2120  // - 4+: Reads from file; if at end, perform a close.
2121  switch (reqstate) {
2122  case 0: // open
2123  {
2124  if (xrdresp == kXR_ok) {
2125  fopened = true;
2126  getfhandle();
2127 
2128  // Always try to parse response. In the case of a caching proxy, the open
2129  // will have created the file in cache
2130  if (iovP[1].iov_len > 1) {
2131  TRACEI(REQ, "Stat for GET " << resource.c_str()
2132  << " stat=" << (char *) iovP[1].iov_base);
2133 
2134  long dummyl;
2135  sscanf((const char *) iovP[1].iov_base, "%ld %lld %ld %ld",
2136  &dummyl,
2137  &filesize,
2138  &fileflags,
2139  &filemodtime);
2140 
2141  // If this is a directory, bail out early; we will close the file handle
2142  // and then issue a directory listing.
2143  if (fileflags & kXR_isDir) {
2144  return 0;
2145  }
2146 
2148 
2149  // As above: if the client specified a response size, we use that.
2150  // Otherwise, utilize the filesize
2151  if (!length) {
2152  length = filesize;
2153  }
2154  }
2155  else {
2156  TRACEI(ALL, "GET returned no STAT information. Internal error?");
2157  prot->SendSimpleResp(500, NULL, NULL, "Storage system did not return stat info.", 0, false);
2158  return -1;
2159  }
2160  return 0;
2161  } else if (xrderrcode == kXR_isDirectory) { // This is a directory; trigger directory-handling topic.
2162  fileflags = kXR_isDir;
2163  return 0;
2164  } else { // xrdresp indicates an error occurred
2165 
2166  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2167  httpErrorBody.c_str(), httpErrorBody.length(), false);
2168  return -1;
2169  }
2170  // Case should not be reachable
2171  return -1;
2172  } // end open
2173  case 1: // checksum was requested and now we have its response.
2174  {
2175  return PostProcessChecksum(m_digest_header);
2176  }
2177  case 2: // close file handle in case of the directory
2178  {
2179  if (xrdresp != kXR_ok) {
2180  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2181  httpErrorBody.c_str(), httpErrorBody.length(), false);
2182  return -1;
2183  }
2184  return 0;
2185  }
2186  case 3: // handle the directory listing response
2187  {
2188  return PostProcessListing(final_);
2189  }
2190  default: //read or readv, followed by a close.
2191  {
2192  // If we are postprocessing a close, potentially send out informational trailers
2193  if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing)
2194  {
2195  // If we already sent out an error, then we cannot send any further
2196  // messages
2197  if (closeAfterError) {
2198  TRACEI(REQ, "Close was completed after an error: " << xrdresp);
2199  return xrdresp != kXR_ok ? -1 : 1;
2200  }
2201 
2203  if (rrerror) {
2204  httpStatusCode = rrerror.httpRetCode;
2205  httpErrorBody = rrerror.errMsg;
2206  }
2207 
2208  if (m_transfer_encoding_chunked && m_trailer_headers) {
2209  if (prot->ChunkRespHeader(0))
2210  return -1;
2211 
2212  const std::string crlf = "\r\n";
2213  std::stringstream ss;
2214  ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpErrorBody << crlf;
2215 
2216  const auto header = ss.str();
2217  if (prot->SendData(header.c_str(), header.size()))
2218  return -1;
2219 
2220  if (prot->ChunkRespFooter())
2221  return -1;
2222  }
2223 
2224  if (rrerror) return -1;
2225  return keepalive ? 1 : -1;
2226  }
2227 
2228  // On error, we can only send out a message if trailers are enabled and the
2229  // status response in trailer behavior is requested.
2230  if (xrdresp == kXR_error) {
2231  auto rc = sendFooterError("");
2232  if (rc == 1) {
2233  closeAfterError = true;
2234  return 0;
2235  }
2236  return -1;
2237  }
2238 
2239 
2240  TRACEI(REQ, "Got data vectors to send:" << iovN);
2241 
2242  XrdHttpIOList received;
2243  getReadResponse(received);
2244 
2245  int rc;
2247  rc = sendReadResponseSingleRange(received);
2248  } else {
2249  rc = sendReadResponsesMultiRanges(received);
2250  }
2251  if (rc) {
2252  // make sure readRangeHandler will trigger close
2253  // of file after next NextReadList().
2255  }
2256 
2257  return 0;
2258  } // end read or readv
2259 
2260  } // switch reqstate
2261  break;
2262  } // case GET
2263 
2264  case XrdHttpReq::rtPUT:
2265  {
2266  if (!fopened) {
2267  if (xrdresp != kXR_ok) {
2268  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2269  return -1;
2270  }
2271 
2272  getfhandle();
2273  fopened = true;
2274 
2275  // We try to completely fill up our buffer before flushing
2276  prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2277 
2278  if (sendcontinue) {
2279  prot->SendSimpleResp(100, NULL, NULL, 0, 0, keepalive);
2280  return 0;
2281  }
2282 
2283  break;
2284  } else {
2285 
2286  // If we are here it's too late to send a proper error message...
2287  // However, we decide to send a response anyway before we close the connection
2288  // We are not sure if sending a final response before reading the entire request
2289  if (xrdresp == kXR_error) {
2290  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2291  return -1;
2292  }
2293 
2294  if (ntohs(xrdreq.header.requestid) == kXR_write) {
2295  int l = ntohl(xrdreq.write.dlen);
2296 
2297  // Consume the written bytes
2298  prot->BuffConsume(ntohl(xrdreq.write.dlen));
2299  writtenbytes += l;
2300 
2301  // Update the chunk offset
2302  if (m_transfer_encoding_chunked) {
2303  m_current_chunk_offset += l;
2304  }
2305 
2306  // We try to completely fill up our buffer before flushing
2307  prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2308 
2309  return 0;
2310  }
2311 
2312  if (ntohs(xrdreq.header.requestid) == kXR_close) {
2313  if (xrdresp == kXR_ok) {
2314  prot->SendSimpleResp(201, NULL, NULL, (char *)":-)", 0, keepalive);
2315  return keepalive ? 1 : -1;
2316  } else {
2317  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2318  return -1;
2319  }
2320  }
2321  }
2322 
2323 
2324 
2325 
2326 
2327  break;
2328  }
2329 
2330 
2331 
2332  case XrdHttpReq::rtDELETE:
2333  {
2334 
2335  if (xrdresp != kXR_ok) {
2336  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2337  httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2338  return -1;
2339  }
2340 
2341 
2342 
2343 
2344  switch (reqstate) {
2345 
2346  case 0: // response to stat()
2347  {
2348  if (iovN > 0) {
2349 
2350  // Now parse the stat info
2351  TRACEI(REQ, "Stat for removal " << resource.c_str()
2352  << " stat=" << (char *) iovP[0].iov_base);
2353 
2354  long dummyl;
2355  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2356  &dummyl,
2357  &filesize,
2358  &fileflags,
2359  &filemodtime);
2360  }
2361 
2362  return 0;
2363  }
2364  default: // response to rm
2365  {
2366  if (xrdresp == kXR_ok) {
2367  prot->SendSimpleResp(200, NULL, NULL, (char *) ":-)", 0, keepalive);
2368  return keepalive ? 1 : -1;
2369  }
2370  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2371  httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2372  return -1;
2373  }
2374  }
2375 
2376 
2377  }
2378 
2380  {
2381 
2382  if (xrdresp == kXR_error) {
2383  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2384  httpErrorBody.c_str(), httpErrorBody.length(), false);
2385  return -1;
2386  }
2387 
2388  switch (reqstate) {
2389 
2390  case 0: // response to stat()
2391  {
2392  DirListInfo e;
2393  e.size = 0;
2394  e.flags = 0;
2395 
2396  // Now parse the answer building the entries vector
2397  if (iovN > 0) {
2398  e.path = resource.c_str();
2399 
2400  // Now parse the stat info
2401  TRACEI(REQ, "Collection " << resource.c_str()
2402  << " stat=" << (char *) iovP[0].iov_base);
2403 
2404  long dummyl;
2405  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2406  &dummyl,
2407  &e.size,
2408  &e.flags,
2409  &e.modtime);
2410 
2411  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2412  /* The entry is filled. */
2413 
2414 
2415  std::string p;
2416  stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2417 
2418  char *estr = escapeXML(e.path.c_str());
2419 
2420  stringresp += "<D:href>";
2421  stringresp += estr;
2422  stringresp += "</D:href>\n";
2423 
2424  free(estr);
2425 
2426  stringresp += "<D:propstat>\n<D:prop>\n";
2427 
2428  // Now add the properties that we have to add
2429 
2430  // File size
2431  stringresp += "<lp1:getcontentlength>";
2432  stringresp += itos(e.size);
2433  stringresp += "</lp1:getcontentlength>\n";
2434 
2435 
2436 
2437  stringresp += "<lp1:getlastmodified>";
2439  stringresp += "</lp1:getlastmodified>\n";
2440 
2441 
2442 
2443  if (e.flags & kXR_isDir) {
2444  stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2445  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2446  } else {
2447  stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2448  }
2449 
2450  if (e.flags & kXR_xset) {
2451  stringresp += "<lp1:executable>T</lp1:executable>\n";
2452  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2453  } else {
2454  stringresp += "<lp1:executable>F</lp1:executable>\n";
2455  }
2456 
2457 
2458 
2459  stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2460 
2461 
2462  }
2463 
2464 
2465  }
2466 
2467  // If this was the last bunch of entries, send the buffer and empty it immediately
2468  if ((depth == 0) || !(e.flags & kXR_isDir)) {
2469  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";
2470  stringresp.insert(0, s);
2471  stringresp += "</D:multistatus>\n";
2472  prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2473  (char *) stringresp.c_str(), stringresp.length(), keepalive);
2474  stringresp.clear();
2475  return keepalive ? 1 : -1;
2476  }
2477 
2478  break;
2479  }
2480  default: // response to dirlist()
2481  {
2482 
2483 
2484  // Now parse the answer building the entries vector
2485  if (iovN > 0) {
2486  char *startp = (char *) iovP[0].iov_base, *endp = 0;
2487  char entry[1024];
2488  DirListInfo e;
2489 
2490  while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)(iovP[0].iov_len - 1) ) {
2491  // Find the filename, it comes before the \n
2492  if ((endp = (char *) mystrchrnul((const char*) startp, '\n'))) {
2493  strncpy(entry, (char *) startp, endp - startp);
2494  entry[endp - startp] = 0;
2495  e.path = entry;
2496 
2497  endp++;
2498 
2499  // Now parse the stat info
2500  TRACEI(REQ, "Dirlist " <<resource.c_str() <<" entry=" <<entry
2501  << " stat=" << endp);
2502 
2503  long dummyl;
2504  sscanf(endp, "%ld %lld %ld %ld",
2505  &dummyl,
2506  &e.size,
2507  &e.flags,
2508  &e.modtime);
2509  }
2510 
2511 
2512  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2513  /* The entry is filled.
2514 
2515  <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/" xmlns:lp3="LCGDM:">
2516  <D:href>/dpm/cern.ch/home/testers2.eu-emi.eu/</D:href>
2517  <D:propstat>
2518  <D:prop>
2519  <lp1:getcontentlength>1</lp1:getcontentlength>
2520  <lp1:getlastmodified>Tue, 01 May 2012 02:42:13 GMT</lp1:getlastmodified>
2521  <lp1:resourcetype>
2522  <D:collection/>
2523  </lp1:resourcetype>
2524  </D:prop>
2525  <D:status>HTTP/1.1 200 OK</D:status>
2526  </D:propstat>
2527  </D:response>
2528  */
2529 
2530 
2531  std::string p = resource.c_str();
2532  if (*p.rbegin() != '/') p += "/";
2533 
2534  p += e.path;
2535 
2536  stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2537 
2538  char *estr = escapeXML(p.c_str());
2539  stringresp += "<D:href>";
2540  stringresp += estr;
2541  stringresp += "</D:href>\n";
2542  free(estr);
2543 
2544  stringresp += "<D:propstat>\n<D:prop>\n";
2545 
2546 
2547 
2548  // Now add the properties that we have to add
2549 
2550  // File size
2551  stringresp += "<lp1:getcontentlength>";
2552  stringresp += itos(e.size);
2553  stringresp += "</lp1:getcontentlength>\n";
2554 
2555  stringresp += "<lp1:getlastmodified>";
2557  stringresp += "</lp1:getlastmodified>\n";
2558 
2559  if (e.flags & kXR_isDir) {
2560  stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2561  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2562  } else {
2563  stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2564  }
2565 
2566  if (e.flags & kXR_xset) {
2567  stringresp += "<lp1:executable>T</lp1:executable>\n";
2568  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2569  } else {
2570  stringresp += "<lp1:executable>F</lp1:executable>\n";
2571  }
2572 
2573  stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2574 
2575 
2576  }
2577 
2578 
2579 
2580  if (endp) {
2581  char *pp = (char *)strchr((const char *)endp, '\n');
2582  if (pp) startp = pp+1;
2583  else break;
2584  } else break;
2585 
2586  }
2587  }
2588 
2589 
2590 
2591  // If this was the last bunch of entries, send the buffer and empty it immediately
2592  if (final_) {
2593  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";
2594  stringresp.insert(0, s);
2595  stringresp += "</D:multistatus>\n";
2596  prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2597  (char *) stringresp.c_str(), stringresp.length(), keepalive);
2598  stringresp.clear();
2599  return keepalive ? 1 : -1;
2600  }
2601 
2602  break;
2603  } // default reqstate
2604  } // switch reqstate
2605 
2606 
2607  break;
2608 
2609  } // case propfind
2610 
2611  case XrdHttpReq::rtMKCOL:
2612  {
2613 
2614  if (xrdresp != kXR_ok) {
2615  if (xrderrcode == kXR_ItExists) {
2616  prot->SendSimpleResp(405, NULL, NULL, (char *) "Method is not allowed; resource already exists.", 0, false);
2617  } else {
2618  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2619  httpErrorBody.c_str(), httpErrorBody.length(), false);
2620  }
2621  return -1;
2622  }
2623 
2624  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2625  return keepalive ? 1 : -1;
2626 
2627  }
2628  case XrdHttpReq::rtMOVE:
2629  {
2630 
2631  if (xrdresp != kXR_ok) {
2632  prot->SendSimpleResp(httpStatusCode, NULL, NULL, (char *) etext.c_str(), 0, false);
2633  return -1;
2634  }
2635 
2636  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2637  return keepalive ? 1 : -1;
2638 
2639  }
2640 
2641  default:
2642  break;
2643 
2644  }
2645 
2646 
2647  switch (xrdresp) {
2648  case kXR_error:
2649  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2650  httpErrorBody.c_str(), httpErrorBody.length(), false);
2651  return -1;
2652  break;
2653 
2654  default:
2655 
2656  break;
2657  }
2658 
2659 
2660  return 0;
2661 }
2662 
2663 int
2664 XrdHttpReq::sendFooterError(const std::string &extra_text) {
2665  if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2666  // A trailer header is appropriate in this case; this is signified by
2667  // a chunk with size zero, then the trailer, then a crlf.
2668  //
2669  // We only send the status trailer when explicitly requested; otherwise a
2670  // "normal" HTTP client might simply see a short response and think it's a
2671  // success
2672 
2673  if (prot->ChunkRespHeader(0))
2674  return -1;
2675 
2676  std::stringstream ss;
2677 
2678  ss << httpStatusCode;
2679  if (!httpErrorBody.empty()) {
2680  std::string_view statusView(httpErrorBody);
2681  // Remove trailing newline; this is not valid in a trailer value
2682  // and causes incorrect framing of the response, confusing clients.
2683  if (statusView[statusView.size() - 1] == '\n') {
2684  ss << ": " << statusView.substr(0, statusView.size() - 1);
2685  } else {
2686  ss << ": " << httpErrorBody;
2687  }
2688  }
2689 
2690  if (!extra_text.empty())
2691  ss << ": " << extra_text;
2692  TRACEI(REQ, ss.str());
2693  ss << "\r\n";
2694 
2695  const auto header = "X-Transfer-Status: " + ss.str();
2696  if (prot->SendData(header.c_str(), header.size()))
2697  return -1;
2698 
2699  if (prot->ChunkRespFooter())
2700  return -1;
2701 
2702  return keepalive ? 1 : -1;
2703  } else {
2704  TRACEI(REQ, "Failure during response: " << httpStatusCode << ": " << httpErrorBody << (extra_text.empty() ? "" : (": " + extra_text)));
2705  return -1;
2706 
2707  }
2708 }
2709 
2710 void XrdHttpReq::addAgeHeader(std::string &headers) {
2711  long object_age = time(NULL) - filemodtime;
2712  headers += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2713 }
2714 
2716 
2717  TRACE(REQ, " XrdHttpReq request ended.");
2718 
2719  //if (xmlbody) xmlFreeDoc(xmlbody);
2721  readClosing = false;
2722  closeAfterError = false;
2723  writtenbytes = 0;
2724  etext.clear();
2725  redirdest = "";
2726 
2727  // // Here we should deallocate this
2728  // const struct iovec *iovP //!< pointer to data array
2729  // int iovN, //!< array count
2730  // int iovL, //!< byte count
2731  // bool final //!< true -> final result
2732 
2733 
2734  //xmlbody = 0;
2735  depth = 0;
2738  ralist.clear();
2739  ralist.shrink_to_fit();
2740 
2741  request = rtUnset;
2742  resource = "";
2743  allheaders.clear();
2744 
2745  // Reset the state of the request's digest request.
2746  m_req_digest.clear();
2747  m_digest_header.clear();
2748  m_req_cksum = nullptr;
2749 
2751  m_user_agent = "";
2752  m_origin = "";
2753 
2754  headerok = false;
2755  keepalive = true;
2756  length = 0;
2757  filesize = 0;
2758  depth = 0;
2759  sendcontinue = false;
2760 
2761  m_transfer_encoding_chunked = false;
2762  m_current_chunk_size = -1;
2763  m_current_chunk_offset = 0;
2764 
2765  m_trailer_headers = false;
2766  m_status_trailer = false;
2767 
2769  reqstate = 0;
2770 
2771  memset(&xrdreq, 0, sizeof (xrdreq));
2772  memset(&xrdresp, 0, sizeof (xrdresp));
2774 
2775  etext.clear();
2776  redirdest = "";
2777 
2778  stringresp = "";
2779 
2780  host = "";
2781  destination = "";
2782  hdr2cgistr = "";
2783  m_appended_hdr2cgistr = false;
2784  m_appended_asize = false;
2785 
2786  iovP = 0;
2787  iovN = 0;
2788  iovL = 0;
2789 
2790 
2791  if (opaque) delete(opaque);
2792  opaque = 0;
2793 
2794  fopened = false;
2795 
2796  final = false;
2797 
2798  mScitag = -1;
2799 
2800  httpStatusCode = -1;
2801  httpErrorCode = "";
2802  httpErrorBody = "";
2803 
2804 }
2805 
2806 void XrdHttpReq::getfhandle() {
2807 
2808  memcpy(fhandle, iovP[0].iov_base, 4);
2809  TRACEI(REQ, "fhandle:" <<
2810  (int) fhandle[0] << ":" << (int) fhandle[1] << ":" << (int) fhandle[2] << ":" << (int) fhandle[3]);
2811 
2812 }
2813 
2814 void XrdHttpReq::getReadResponse(XrdHttpIOList &received) {
2815  received.clear();
2816 
2817  if (ntohs(xrdreq.header.requestid) == kXR_readv) {
2818  readahead_list *l;
2819  char *p;
2820  kXR_int32 len;
2821 
2822  // Cycle on all the data that is coming from the server
2823  for (int i = 0; i < iovN; i++) {
2824 
2825  for (p = (char *) iovP[i].iov_base; p < (char *) iovP[i].iov_base + iovP[i].iov_len;) {
2826  l = (readahead_list *) p;
2827  len = ntohl(l->rlen);
2828 
2829  received.emplace_back(p+sizeof(readahead_list), -1, len);
2830 
2831  p += sizeof (readahead_list);
2832  p += len;
2833 
2834  }
2835  }
2836  return;
2837  }
2838 
2839  // kXR_read result
2840  for (int i = 0; i < iovN; i++) {
2841  received.emplace_back((char*)iovP[i].iov_base, -1, iovP[i].iov_len);
2842  }
2843 
2844 }
2845 
2846 int XrdHttpReq::sendReadResponsesMultiRanges(const XrdHttpIOList &received) {
2847 
2848  if (received.size() == 0) {
2849  bool start, finish;
2850  if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2851  return -1;
2852  }
2853  return 0;
2854  }
2855 
2856  // user is expecting multiple ranges, we must be prepared to send an
2857  // individual header for each and format it according to the http rules
2858 
2859  struct rinfo {
2860  bool start;
2861  bool finish;
2862  const XrdOucIOVec2 *ci;
2864  std::string st_header;
2865  std::string fin_header;
2866  };
2867 
2868  // report each received byte chunk to the range handler and record the details
2869  // of original user range it related to and if starts a range or finishes all.
2870  // also sum the total of the headers and data which need to be sent to the user,
2871  // in case we need it for chunked transfer encoding
2872  std::vector<rinfo> rvec;
2873  off_t sum_len = 0;
2874 
2875  rvec.reserve(received.size());
2876 
2877  for(const auto &rcv: received) {
2878  rinfo rentry;
2879  bool start, finish;
2881 
2882  if (readRangeHandler.NotifyReadResult(rcv.size, &ur, start, finish) < 0) {
2883  return -1;
2884  }
2885  rentry.ur = ur;
2886  rentry.start = start;
2887  rentry.finish = finish;
2888  rentry.ci = &rcv;
2889 
2890  if (start) {
2891  std::string s = buildPartialHdr(ur->start,
2892  ur->end,
2893  filesize,
2894  (char *) "123456");
2895 
2896  rentry.st_header = s;
2897  sum_len += s.size();
2898  }
2899 
2900  sum_len += rcv.size;
2901 
2902  if (finish) {
2903  std::string s = buildPartialHdrEnd((char *) "123456");
2904  rentry.fin_header = s;
2905  sum_len += s.size();
2906  }
2907 
2908  rvec.push_back(rentry);
2909  }
2910 
2911 
2912  // Send chunked encoding header
2913  if (m_transfer_encoding_chunked && m_trailer_headers) {
2914  prot->ChunkRespHeader(sum_len);
2915  }
2916 
2917  // send the user the headers / data
2918  for(const auto &rentry: rvec) {
2919 
2920  if (rentry.start) {
2921  TRACEI(REQ, "Sending multipart: " << rentry.ur->start << "-" << rentry.ur->end);
2922  if (prot->SendData((char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2923  return -1;
2924  }
2925  }
2926 
2927  // Send all the data we have
2928  if (prot->SendData((char *) rentry.ci->data, rentry.ci->size)) {
2929  return -1;
2930  }
2931 
2932  if (rentry.finish) {
2933  if (prot->SendData((char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2934  return -1;
2935  }
2936  }
2937  }
2938 
2939  // Send chunked encoding footer
2940  if (m_transfer_encoding_chunked && m_trailer_headers) {
2941  prot->ChunkRespFooter();
2942  }
2943 
2944  return 0;
2945 }
2946 
2947 int XrdHttpReq::sendReadResponseSingleRange(const XrdHttpIOList &received) {
2948  // single range http transfer
2949 
2950  if (received.size() == 0) {
2951  bool start, finish;
2952  if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2953  return -1;
2954  }
2955  return 0;
2956  }
2957 
2958  off_t sum = 0;
2959  // notify the range handler and return if error
2960  for(const auto &rcv: received) {
2961  bool start, finish;
2962  if (readRangeHandler.NotifyReadResult(rcv.size, nullptr, start, finish) < 0) {
2963  return -1;
2964  }
2965  sum += rcv.size;
2966  }
2967 
2968  // Send chunked encoding header
2969  if (m_transfer_encoding_chunked && m_trailer_headers) {
2970  prot->ChunkRespHeader(sum);
2971  }
2972  for(const auto &rcv: received) {
2973  if (prot->SendData((char *) rcv.data, rcv.size)) return -1;
2974  }
2975  if (m_transfer_encoding_chunked && m_trailer_headers) {
2976  prot->ChunkRespFooter();
2977  }
2978  return 0;
2979 }
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.
int parseURL(char *url, char *host, int &port, char **path)
Definition: XrdHttpUtils.cc:77
std::string itos(long i)
void Tobase64(const unsigned char *input, int length, char *out)
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:2017
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:2715
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