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