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 
589 
590  // Here we put back the opaque info, if any
591  if (vardata) {
592  char *newvardata = quote(vardata);
593  redirdest += "?&";
594  redirdest += newvardata;
595  free(newvardata);
596  }
597 
598  // Shall we put also the opaque data of the request? Maybe not
599  //int l;
600  //if (opaque && opaque->Env(l))
601  // redirdest += opaque->Env(l);
602 
603 
604  time_t timenow = 0;
605  if (!prot->isdesthttps && prot->ishttps) {
606  // If the destination is not https, then we suppose that it
607  // will need this token to fill its authorization info
608  timenow = time(0);
609  calcHashes(hash, this->resource.c_str(), (kXR_int16) request,
610  &prot->SecEntity,
611  timenow,
612  prot->secretkey);
613  }
614 
615  if (hash[0]) {
616  appendOpaque(redirdest, &prot->SecEntity, hash, timenow);
617  } else
618  appendOpaque(redirdest, 0, 0, 0);
619 
620 
621  TRACE(REQ, " XrdHttpReq::Redir Redirecting to " << obfuscateAuth(redirdest.c_str()).c_str());
622 
623  if (request != rtGET)
624  prot->SendSimpleResp(307, NULL, (char *) redirdest.c_str(), 0, 0, keepalive);
625  else
626  prot->SendSimpleResp(302, NULL, (char *) redirdest.c_str(), 0, 0, keepalive);
627 
628  bool ret_keepalive = keepalive; // reset() clears keepalive
629  reset();
630  return ret_keepalive;
631 };
632 
633 
634 void XrdHttpReq::appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow) {
635 
636  int l = 0;
637  char * p = 0;
638  if (opaque)
639  p = opaque->Env(l);
640 
641  if (hdr2cgistr.empty() && (l < 2) && !hash) return;
642 
643  // this works in most cases, except if the url already contains the xrdhttp tokens
644  s = s + "?";
645  if (!hdr2cgistr.empty()) {
646  char *s1 = quote(hdr2cgistr.c_str());
647  if (s1) {
648  s += s1;
649  free(s1);
650  }
651  }
652  if (p && (l > 1)) {
653  char *s1 = quote(p+1);
654  if (s1) {
655  if (!hdr2cgistr.empty()) {
656  s = s + "&";
657  }
658  s = s + s1;
659  free(s1);
660  }
661  }
662 
663 
664 
665  if (hash) {
666  if (l > 1) s += "&";
667  s += "xrdhttptk=";
668  s += hash;
669 
670  s += "&xrdhttptime=";
671  char buf[256];
672  sprintf(buf, "%lld", (long long) tnow);
673  s += buf;
674 
675  if (secent) {
676  if (secent->name) {
677  s += "&xrdhttpname=";
678  char *s1 = quote(secent->name);
679  if (s1) {
680  s += s1;
681  free(s1);
682  }
683  }
684 
685  if (secent->vorg) {
686  s += "&xrdhttpvorg=";
687  char *s1 = quote(secent->vorg);
688  if (s1) {
689  s += s1;
690  free(s1);
691  }
692  }
693 
694  if (secent->host) {
695  s += "&xrdhttphost=";
696  char *s1 = quote(secent->host);
697  if (s1) {
698  s += s1;
699  free(s1);
700  }
701  }
702 
703  if (secent->moninfo) {
704  s += "&xrdhttpdn=";
705  char *s1 = quote(secent->moninfo);
706  if (s1) {
707  s += s1;
708  free(s1);
709  }
710  }
711 
712  if (secent->role) {
713  s += "&xrdhttprole=";
714  char *s1 = quote(secent->role);
715  if (s1) {
716  s += s1;
717  free(s1);
718  }
719  }
720 
721  if (secent->grps) {
722  s += "&xrdhttpgrps=";
723  char *s1 = quote(secent->grps);
724  if (s1) {
725  s += s1;
726  free(s1);
727  }
728  }
729 
730  if (secent->endorsements) {
731  s += "&xrdhttpendorsements=";
732  char *s1 = quote(secent->endorsements);
733  if (s1) {
734  s += s1;
735  free(s1);
736  }
737  }
738 
739  if (secent->credslen) {
740  s += "&xrdhttpcredslen=";
741  char buf[16];
742  sprintf(buf, "%d", secent->credslen);
743  char *s1 = quote(buf);
744  if (s1) {
745  s += s1;
746  free(s1);
747  }
748  }
749 
750  if (secent->credslen) {
751  if (secent->creds) {
752  s += "&xrdhttpcreds=";
753  // Apparently this string might be not 0-terminated (!)
754  char *zerocreds = strndup(secent->creds, secent->credslen);
755  if (zerocreds) {
756  char *s1 = quote(zerocreds);
757  if (s1) {
758  s += s1;
759  free(s1);
760  }
761  free(zerocreds);
762  }
763  }
764  }
765 
766  }
767  }
768 
769 }
770 
771 
772 // Sanitize the resource from the http[s]://[host]/ questionable prefix
773 // https://github.com/xrootd/xrootd/issues/1675
774 void XrdHttpReq::sanitizeResourcePfx() {
775 
776  if (resource.beginswith("https://")) {
777  // Find the slash that follows the hostname, and keep it
778  int p = resource.find('/', 8);
780  return;
781  }
782 
783  if (resource.beginswith("http://")) {
784  // Find the slash that follows the hostname, and keep it
785  int p = resource.find('/', 7);
787  return;
788  }
789 }
790 
791 void XrdHttpReq::addCgi(const std::string &key, const std::string &value) {
792  if (hdr2cgistr.length() > 0) {
793  hdr2cgistr.append("&");
794  }
795  hdr2cgistr.append(key);
796  hdr2cgistr.append("=");
797  hdr2cgistr.append(value);
798 }
799 
800 
801 // Parse a resource line:
802 // - sanitize
803 // - extracts the opaque info from the given url
804 // - sanitize the resource from http[s]://[host]/ questionable prefix
805 void XrdHttpReq::parseResource(char *res) {
806 
807 
808 
809 
810  // Look for the first '?'
811  char *p = strchr(res, '?');
812 
813  // Not found, then it's just a filename
814  if (!p) {
815  resource.assign(res, 0);
816 
817  // Some poor client implementations may inject a http[s]://[host]/ prefix
818  // to the resource string. Here we choose to ignore it as a protection measure
819  sanitizeResourcePfx();
820 
821  char *buf = unquote((char *)resource.c_str());
822  resource.assign(buf, 0);
823  resourceplusopaque.assign(buf, 0);
824  free(buf);
825 
826  // Sanitize the resource string, removing double slashes
827  int pos = 0;
828  do {
829  pos = resource.find("//", pos);
830  if (pos != STR_NPOS)
831  resource.erase(pos, 1);
832  } while (pos != STR_NPOS);
833 
834  return;
835  }
836 
837  // Whatever comes before '?' is a filename
838 
839  int cnt = p - res; // Number of chars to copy
840  resource.assign(res, 0, cnt - 1);
841 
842  // Some poor client implementations may inject a http[s]://[host]/ prefix
843  // to the resource string. Here we choose to ignore it as a protection measure
844  sanitizeResourcePfx();
845 
846  char *buf = unquote((char *)resource.c_str());
847  resource.assign(buf, 0);
848  free(buf);
849 
850  // Sanitize the resource string, removing double slashes
851  int pos = 0;
852  do {
853  pos = resource.find("//", pos);
854  if (pos != STR_NPOS)
855  resource.erase(pos, 1);
856  } while (pos != STR_NPOS);
857 
859  // Whatever comes after is opaque data to be parsed
860  if (strlen(p) > 1) {
861  buf = unquote(p + 1);
862  opaque = new XrdOucEnv(buf);
864  resourceplusopaque.append(p + 1);
865  free(buf);
866  }
867 
868 
869 
870 }
871 
872 // Map an XRootD error code to an appropriate HTTP status code and message
873 // The variables httpStatusCode and httpStatusText will be populated
874 
875 void XrdHttpReq::mapXrdErrorToHttpStatus() {
876  // Set default HTTP status values for an error case
877  httpStatusCode = 500;
878  httpStatusText = "Unrecognized error";
879 
880  // Do error mapping
881  if (xrdresp == kXR_error) {
882  switch (xrderrcode) {
883  case kXR_AuthFailed:
884  httpStatusCode = 401; httpStatusText = "Unauthorized";
885  break;
886  case kXR_NotAuthorized:
887  httpStatusCode = 403; httpStatusText = "Operation not permitted";
888  break;
889  case kXR_NotFound:
890  httpStatusCode = 404; httpStatusText = "File not found";
891  break;
892  case kXR_Unsupported:
893  httpStatusCode = 405; httpStatusText = "Operation not supported";
894  break;
895  case kXR_FileLocked:
896  httpStatusCode = 423; httpStatusText = "Resource is a locked";
897  break;
898  case kXR_isDirectory:
899  httpStatusCode = 409; httpStatusText = "Resource is a directory";
900  break;
901  case kXR_ItExists:
902  if(request != ReqType::rtDELETE) {
903  httpStatusCode = 409; httpStatusText = "File already exists";
904  } else {
905  // In the case the XRootD layer returns a kXR_ItExists after a deletion
906  // was submitted, we return a 405 status code with the error message set by
907  // the XRootD layer
908  httpStatusCode = 405;
909  }
910  break;
911  case kXR_InvalidRequest:
912  httpStatusCode = 405; httpStatusText = "Method is not allowed";
913  break;
914  case kXR_noserver:
915  httpStatusCode = 502; httpStatusText = "Bad Gateway";
916  break;
917  case kXR_TimerExpired:
918  httpStatusCode = 504; httpStatusText = "Gateway timeout";
919  break;
920  default:
921  break;
922  }
923 
924  if (!etext.empty()) httpStatusText = etext;
925 
926  TRACEI(REQ, "PostProcessHTTPReq mapping Xrd error [" << xrderrcode
927  << "] to status code [" << httpStatusCode << "]");
928 
929  httpStatusText += "\n";
930  } else {
931  httpStatusCode = 200;
932  httpStatusText = "OK";
933  }
934 }
935 
937 
938  kXR_int32 l;
939 
940  // State variable for tracking the query parameter search
941  // - 0: Indicates we've not yet searched the URL for '?'
942  // - 1: Indicates we have a '?' and hence query parameters
943  // - 2: Indicates we do *not* have '?' present -- no query parameters
944  int query_param_status = 0;
945  if (!m_appended_asize) {
946  m_appended_asize = true;
947  if (request == rtPUT && length) {
948  if (query_param_status == 0) {
949  query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
950  }
951  resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
952  query_param_status = 1;
953  auto length_str = std::to_string(length);
954  resourceplusopaque.append("oss.asize=");
955  resourceplusopaque.append(length_str.c_str());
956  if (!opaque) {
957  opaque = new XrdOucEnv();
958  }
959  opaque->Put("oss.asize", length_str.c_str());
960  }
961  }
962 
964  if (!m_appended_hdr2cgistr && !hdr2cgistr.empty()) {
965  if (query_param_status == 0) {
966  query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
967  }
968  resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
969 
970  char *q = quote(hdr2cgistr.c_str());
972  if (TRACING(TRACE_DEBUG)) {
973  // The obfuscation of "authz" will only be done if the server http.header2cgi config contains something that maps a header to this "authz" cgi.
974  // Unfortunately the obfuscation code will be called no matter what is configured in http.header2cgi.
975  std::string header2cgistrObf = obfuscateAuth(hdr2cgistr);
976 
977  TRACEI(DEBUG, "Appended header fields to opaque info: '"
978  << header2cgistrObf.c_str() << "'");
979 
980  }
981  // We assume that anything appended to the CGI str should also
982  // apply to the destination in case of a MOVE.
983  if (strchr(destination.c_str(), '?')) destination.append("&");
984  else destination.append("?");
985  destination.append(q);
986 
987  free(q);
988  m_appended_hdr2cgistr = true;
989  }
990 
991  // Verify if we have an external handler for this request
992  if (reqstate == 0) {
993  XrdHttpExtHandler *exthandler = prot->FindMatchingExtHandler(*this);
994  if (exthandler) {
995  XrdHttpExtReq xreq(this, prot);
996  int r = exthandler->ProcessReq(xreq);
997  reset();
998  if (!r) return 1; // All went fine, response sent
999  if (r < 0) return -1; // There was a hard error... close the connection
1000 
1001  return 1; // There was an error and a response was sent
1002  }
1003  }
1004 
1005  //
1006  // Here we process the request locally
1007  //
1008 
1009  switch (request) {
1010  case XrdHttpReq::rtUnset:
1011  case XrdHttpReq::rtUnknown:
1012  {
1013  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request unknown", 0, false);
1014  reset();
1015  return -1;
1016  }
1018  {
1019  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed", 0, false);
1020  reset();
1021  return -1;
1022  }
1023  case XrdHttpReq::rtHEAD:
1024  {
1025  if (reqstate == 0) {
1026  // Always start with Stat; in the case of a checksum request, we'll have a follow-up query
1027  if (prot->doStat((char *) resourceplusopaque.c_str())) {
1028  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1029  return -1;
1030  }
1031  return 0;
1032  } else {
1033  const char *opaque = strchr(resourceplusopaque.c_str(), '?');
1034  // Note that doChksum requires that the memory stays alive until the callback is invoked.
1036 
1038  if(!m_req_cksum) {
1039  // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1040  prot->SendSimpleResp(403, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1041  return -1;
1042  }
1043  if (!opaque) {
1044  m_resource_with_digest += "?cks.type=";
1046  } else {
1047  m_resource_with_digest += "&cks.type=";
1049  }
1050  if (prot->doChksum(m_resource_with_digest) < 0) {
1051  // In this case, the Want-Digest header was set and PostProcess gave the go-ahead to do a checksum.
1052  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to create initial checksum request.", 0, false);
1053  return -1;
1054  }
1055  return 1;
1056  }
1057  }
1058  case XrdHttpReq::rtGET:
1059  {
1060  int retval = keepalive ? 1 : -1; // reset() clears keepalive
1061 
1062  if (resource.beginswith("/static/")) {
1063 
1064  // This is a request for a /static resource
1065  // If we have to use the embedded ones then we return the ones in memory as constants
1066 
1067  // The sysadmin can always redirect the request to another host that
1068  // contains his static resources
1069 
1070  // We also allow xrootd to preread from the local disk all the files
1071  // that have to be served as static resources.
1072 
1073  if (prot->embeddedstatic) {
1074 
1075  // Default case: the icon and the css of the HTML rendering of XrdHttp
1076  if (resource == "/static/css/xrdhttp.css") {
1077  prot->SendSimpleResp(200, NULL, NULL, (char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len, keepalive);
1078  reset();
1079  return retval;
1080  }
1081  if (resource == "/static/icons/xrdhttp.ico") {
1082  prot->SendSimpleResp(200, NULL, NULL, (char *) favicon_ico, favicon_ico_len, keepalive);
1083  reset();
1084  return retval;
1085  }
1086 
1087  }
1088 
1089  // If we are here then none of the embedded resources match (or they are disabled)
1090  // We may have to redirect to a host that is supposed to serve the static resources
1091  if (prot->staticredir) {
1092 
1093  XrdOucString s = "Location: ";
1094  s.append(prot->staticredir);
1095 
1096  if (s.endswith('/'))
1097  s.erasefromend(1);
1098 
1099  s.append(resource);
1100  appendOpaque(s, 0, 0, 0);
1101 
1102  prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1103  return -1;
1104 
1105 
1106  } else {
1107 
1108  // We lookup the requested path in a hash containing the preread files
1109  if (prot->staticpreload) {
1111  if (mydata) {
1112  prot->SendSimpleResp(200, NULL, NULL, (char *) mydata->data, mydata->len, keepalive);
1113  reset();
1114  return retval;
1115  }
1116  }
1117 
1118  }
1119 
1120 
1121  }
1122 
1123  // The reqstate parameter basically moves us through a simple state machine.
1124  // To optimize things, we start off by opening the file; if it turns out to be a directory, then
1125  // we close the file handle and switch to doing a HTML-based rendering of the directory. This
1126  // avoids needing to always to do "stat" first to determine the next step (since the file-open also
1127  // does a "stat").
1128  // - 0: Perform an open on the resource
1129  // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
1130  // - 2: Perform a close (for dirlist only)
1131  // - 3: Perform a dirlist.
1132  // - 4+: Reads from file; if at end, perform a close.
1133  switch (reqstate) {
1134  case 0: // Open the path for reading.
1135  {
1136  memset(&xrdreq, 0, sizeof (ClientRequest));
1137  xrdreq.open.requestid = htons(kXR_open);
1138  l = resourceplusopaque.length() + 1;
1139  xrdreq.open.dlen = htonl(l);
1140  xrdreq.open.mode = 0;
1142 
1143  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1144  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1145  return -1;
1146  }
1147 
1148  // Prepare to chunk up the request
1149  writtenbytes = 0;
1150 
1151  // We want to be invoked again after this request is finished
1152  return 0;
1153  }
1154  case 1: // Checksum request
1155  if (!(fileflags & kXR_isDir) && !m_req_digest.empty()) {
1156  // In this case, the Want-Digest header was set.
1157  bool has_opaque = strchr(resourceplusopaque.c_str(), '?');
1158  // Note that doChksum requires that the memory stays alive until the callback is invoked.
1160  if(!m_req_cksum) {
1161  // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1162  prot->SendSimpleResp(403, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1163  return -1;
1164  }
1166  if (has_opaque) {
1167  m_resource_with_digest += "&cks.type=";
1169  } else {
1170  m_resource_with_digest += "?cks.type=";
1172  }
1173  if (prot->doChksum(m_resource_with_digest) < 0) {
1174  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to start internal checksum request to satisfy Want-Digest header.", 0, false);
1175  return -1;
1176  }
1177  return 0;
1178  } else {
1179  TRACEI(DEBUG, "No checksum requested; skipping to request state 2");
1180  reqstate += 1;
1181  }
1182  // fallthrough
1183  case 2: // Close file handle for directory
1184  if ((fileflags & kXR_isDir) && fopened) {
1185  memset(&xrdreq, 0, sizeof (ClientRequest));
1186  xrdreq.close.requestid = htons(kXR_close);
1187  memcpy(xrdreq.close.fhandle, fhandle, 4);
1188 
1189  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1190  mapXrdErrorToHttpStatus();
1191  sendFooterError("Could not run close request on the bridge");
1192  return -1;
1193  }
1194  return 0;
1195  } else {
1196  reqstate += 1;
1197  }
1198  // fallthrough
1199  case 3: // List directory
1200  if (fileflags & kXR_isDir) {
1201  if (prot->listdeny) {
1202  prot->SendSimpleResp(503, NULL, NULL, (char *) "Listings are disabled.", 0, false);
1203  return -1;
1204  }
1205 
1206  if (prot->listredir) {
1207  XrdOucString s = "Location: ";
1208  s.append(prot->listredir);
1209 
1210  if (s.endswith('/'))
1211  s.erasefromend(1);
1212 
1213  s.append(resource);
1214  appendOpaque(s, 0, 0, 0);
1215 
1216  prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1217  return -1;
1218  }
1219 
1220  std::string res;
1221  res = resourceplusopaque.c_str();
1222 
1223  // --------- DIRLIST
1224  memset(&xrdreq, 0, sizeof (ClientRequest));
1227  l = res.length() + 1;
1228  xrdreq.dirlist.dlen = htonl(l);
1229 
1230  if (!prot->Bridge->Run((char *) &xrdreq, (char *) res.c_str(), l)) {
1231  mapXrdErrorToHttpStatus();
1232  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(), false);
1233  sendFooterError("Could not run listing request on the bridge");
1234  return -1;
1235  }
1236 
1237  // We don't want to be invoked again after this request is finished
1238  return 1;
1239  }
1240  else {
1241  reqstate += 1;
1242  }
1243  // fallthrough
1244  case 4:
1245  {
1246  auto retval = ReturnGetHeaders();
1247  if (retval) {
1248  return retval;
1249  }
1250  }
1251  // fallthrough
1252  default: // Read() or Close(); reqstate is 4+
1253  {
1254  const XrdHttpIOList &readChunkList = readRangeHandler.NextReadList();
1255 
1256  // Close() if we have finished, otherwise read the next chunk
1257 
1258  // --------- CLOSE
1259  if ( readChunkList.empty() )
1260  {
1261 
1262  memset(&xrdreq, 0, sizeof (ClientRequest));
1263  xrdreq.close.requestid = htons(kXR_close);
1264  memcpy(xrdreq.close.fhandle, fhandle, 4);
1265 
1266  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1267  TRACEI(REQ, " Failed to run close request on the bridge.");
1268  // Note: we have already completed the request and sent the data to the client.
1269  // Hence, there's no need to send an error. However, since the bridge is potentially
1270  // in a bad state, we close the TCP socket to force the client to reconnect.
1271  return -1;
1272  }
1273 
1274  // We have finished
1275  readClosing = true;
1276  return 1;
1277 
1278  }
1279  // --------- READ or READV
1280 
1281  if ( readChunkList.size() == 1 ) {
1282  // Use a read request for single range
1283 
1284  long l;
1285  long long offs;
1286 
1287  // --------- READ
1288  memset(&xrdreq, 0, sizeof (xrdreq));
1289  xrdreq.read.requestid = htons(kXR_read);
1290  memcpy(xrdreq.read.fhandle, fhandle, 4);
1291  xrdreq.read.dlen = 0;
1292 
1293  offs = readChunkList[0].offset;
1294  l = readChunkList[0].size;
1295 
1296  xrdreq.read.offset = htonll(offs);
1297  xrdreq.read.rlen = htonl(l);
1298 
1299  // If we are using HTTPS or if the client requested trailers, or if the
1300  // read concerns a multirange reponse, disable sendfile
1301  // (in the latter two cases, the extra framing is only done in PostProcessHTTPReq)
1302  if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1304  if (!prot->Bridge->setSF((kXR_char *) fhandle, false)) {
1305  TRACE(REQ, " XrdBridge::SetSF(false) failed.");
1306 
1307  }
1308  }
1309 
1310 
1311 
1312  if (l <= 0) {
1313  if (l < 0) {
1314  TRACE(ALL, " Data sizes mismatch.");
1315  return -1;
1316  }
1317  else {
1318  TRACE(ALL, " No more bytes to send.");
1319  reset();
1320  return 1;
1321  }
1322  }
1323 
1324  if ((offs >= filesize) || (offs+l > filesize)) {
1325  httpStatusCode = 416;
1326  httpStatusText = "Range Not Satisfiable";
1327  std::stringstream ss;
1328  ss << "Requested range " << l << "@" << offs << " is past the end of file (" << filesize << ")";
1329  sendFooterError(ss.str());
1330  return -1;
1331  }
1332 
1333  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1334  mapXrdErrorToHttpStatus();
1335  sendFooterError("Could not run read request on the bridge");
1336  return -1;
1337  }
1338  } else {
1339  // --------- READV
1340 
1341  length = ReqReadV(readChunkList);
1342 
1343  if (!prot->Bridge->Run((char *) &xrdreq, (char *) &ralist[0], length)) {
1344  mapXrdErrorToHttpStatus();
1345  sendFooterError("Could not run ReadV request on the bridge");
1346  return -1;
1347  }
1348 
1349  }
1350 
1351  // We want to be invoked again after this request is finished
1352  return 0;
1353  } // case 3+
1354 
1355  } // switch (reqstate)
1356 
1357 
1358  } // case XrdHttpReq::rtGET
1359 
1360  case XrdHttpReq::rtPUT:
1361  {
1362  //if (prot->ishttps) {
1363  //prot->SendSimpleResp(501, NULL, NULL, (char *) "HTTPS not supported yet for direct writing. Sorry.", 0);
1364  //return -1;
1365  //}
1366 
1367  if (!fopened) {
1368 
1369  // --------- OPEN for write!
1370  memset(&xrdreq, 0, sizeof (ClientRequest));
1371  xrdreq.open.requestid = htons(kXR_open);
1372  l = resourceplusopaque.length() + 1;
1373  xrdreq.open.dlen = htonl(l);
1374  xrdreq.open.mode = htons(kXR_ur | kXR_uw | kXR_gw | kXR_gr | kXR_or);
1375  if (! XrdHttpProtocol::usingEC)
1377  else
1379 
1380  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1381  prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, keepalive);
1382  return -1;
1383  }
1384 
1385 
1386  // We want to be invoked again after this request is finished
1387  // Only if there is data to fetch from the socket or there will
1388  // never be more data
1389  if (prot->BuffUsed() > 0 || (length == 0 && !sendcontinue))
1390  return 0;
1391 
1392  return 1;
1393 
1394  } else {
1395 
1396  if (m_transfer_encoding_chunked) {
1397  if (m_current_chunk_size == m_current_chunk_offset) {
1398  // Chunk has been consumed; we now must process the CRLF.
1399  // Note that we don't support trailer headers.
1400  if (prot->BuffUsed() < 2) return 1;
1401  if (prot->myBuffStart[0] != '\r' || prot->myBuffStart[1] != '\n') {
1402  prot->SendSimpleResp(400, NULL, NULL, (char *) "Invalid trailing chunk encoding.", 0, keepalive);
1403  return -1;
1404  }
1405  prot->BuffConsume(2);
1406  if (m_current_chunk_size == 0) {
1407  // All data has been sent. Turn off chunk processing and
1408  // set the bytes written and length appropriately; on next callback,
1409  // we will hit the close() block below.
1410  m_transfer_encoding_chunked = false;
1411  length = writtenbytes;
1412  return ProcessHTTPReq();
1413  }
1414  m_current_chunk_size = -1;
1415  m_current_chunk_offset = 0;
1416  // If there is more data, we try to process the next chunk; otherwise, return
1417  if (!prot->BuffUsed()) return 1;
1418  }
1419  if (-1 == m_current_chunk_size) {
1420 
1421  // Parse out the next chunk size.
1422  long long idx = 0;
1423  bool found_newline = false;
1424  // Set a maximum size of chunk we will allow
1425  // Nginx sets this to "NGX_MAX_OFF_T_VALUE", which is 9223372036854775807 (a some crazy number)
1426  // We set it to 1TB, which is 1099511627776
1427  // This is to prevent a malicious client from sending a very large chunk size
1428  // or a malformed chunk request.
1429  // 1TB in base-16 is 0x40000000000, so only allow 11 characters, plus the CRLF
1430  long long max_chunk_size_chars = std::min(static_cast<long long>(prot->BuffUsed()), static_cast<long long>(13));
1431  for (; idx < max_chunk_size_chars; idx++) {
1432  if (prot->myBuffStart[idx] == '\n') {
1433  found_newline = true;
1434  break;
1435  }
1436  }
1437  // If we found a new line, but it is the first character in the buffer (no chunk length)
1438  // or if the previous character is not a CR.
1439  if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] != '\r')) {
1440  prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1441  TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1442  return -1;
1443  }
1444  if (found_newline) {
1445  char *endptr = NULL;
1446  std::string line_contents(prot->myBuffStart, idx);
1447  long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1448  // Chunk sizes can be followed by trailer information or CRLF
1449  if (*endptr != ';' && *endptr != '\r') {
1450  prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1451  TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1452  return -1;
1453  }
1454  m_current_chunk_size = chunk_contents;
1455  m_current_chunk_offset = 0;
1456  prot->BuffConsume(idx + 1);
1457  TRACE(REQ, "XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size << " bytes");
1458  } else {
1459  // Need more data!
1460  return 1;
1461  }
1462  }
1463 
1464  if (m_current_chunk_size == 0) {
1465  // All data has been sent. Invoke this routine again immediately to process CRLF
1466  return ProcessHTTPReq();
1467  } else {
1468  // At this point, we have a chunk size defined and should consume payload data
1469  memset(&xrdreq, 0, sizeof (xrdreq));
1470  xrdreq.write.requestid = htons(kXR_write);
1471  memcpy(xrdreq.write.fhandle, fhandle, 4);
1472 
1473  long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1474  long long bytes_to_write = std::min(static_cast<long long>(prot->BuffUsed()),
1475  chunk_bytes_remaining);
1476 
1477  xrdreq.write.offset = htonll(writtenbytes);
1478  xrdreq.write.dlen = htonl(bytes_to_write);
1479 
1480  TRACEI(REQ, "XrdHTTP PUT: Writing chunk of size " << bytes_to_write << " starting with '" << *(prot->myBuffStart) << "'" << " with " << chunk_bytes_remaining << " bytes remaining in the chunk");
1481  if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_write)) {
1482  mapXrdErrorToHttpStatus();
1483  sendFooterError("Could not run write request on the bridge");
1484  return -1;
1485  }
1486  // If there are more bytes in the buffer, then immediately call us after the
1487  // write is finished; otherwise, wait for data.
1488  return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1489  }
1490  } else if (writtenbytes < length) {
1491 
1492 
1493  // --------- WRITE
1494  memset(&xrdreq, 0, sizeof (xrdreq));
1495  xrdreq.write.requestid = htons(kXR_write);
1496  memcpy(xrdreq.write.fhandle, fhandle, 4);
1497 
1498  long long bytes_to_read = std::min(static_cast<long long>(prot->BuffUsed()),
1499  length - writtenbytes);
1500 
1501  xrdreq.write.offset = htonll(writtenbytes);
1502  xrdreq.write.dlen = htonl(bytes_to_read);
1503 
1504  TRACEI(REQ, "Writing " << bytes_to_read);
1505  if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_read)) {
1506  mapXrdErrorToHttpStatus();
1507  sendFooterError("Could not run write request on the bridge");
1508  return -1;
1509  }
1510 
1511  if (writtenbytes + prot->BuffUsed() >= length)
1512  // Trigger an immediate recall after this request has finished
1513  return 0;
1514  else
1515  // We want to be invoked again after this request is finished
1516  // only if there is pending data
1517  return 1;
1518 
1519 
1520 
1521  } else {
1522 
1523  // --------- CLOSE
1524  memset(&xrdreq, 0, sizeof (ClientRequest));
1525  xrdreq.close.requestid = htons(kXR_close);
1526  memcpy(xrdreq.close.fhandle, fhandle, 4);
1527 
1528 
1529  if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1530  mapXrdErrorToHttpStatus();
1531  sendFooterError("Could not run close request on the bridge");
1532  return -1;
1533  }
1534 
1535  // We have finished
1536  return 1;
1537 
1538  }
1539 
1540  }
1541 
1542  break;
1543 
1544  }
1545  case XrdHttpReq::rtOPTIONS:
1546  {
1547  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);
1548  bool ret_keepalive = keepalive; // reset() clears keepalive
1549  reset();
1550  return ret_keepalive ? 1 : -1;
1551  }
1552  case XrdHttpReq::rtDELETE:
1553  {
1554 
1555 
1556  switch (reqstate) {
1557 
1558  case 0: // Stat()
1559  {
1560 
1561 
1562  // --------- STAT is always the first step
1563  memset(&xrdreq, 0, sizeof (ClientRequest));
1564  xrdreq.stat.requestid = htons(kXR_stat);
1565  std::string s = resourceplusopaque.c_str();
1566 
1567 
1568  l = resourceplusopaque.length() + 1;
1569  xrdreq.stat.dlen = htonl(l);
1570 
1571  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1572  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1573  return -1;
1574  }
1575 
1576  // We need to be invoked again to complete the request
1577  return 0;
1578  }
1579  default:
1580 
1581  if (fileflags & kXR_isDir) {
1582  // --------- RMDIR
1583  memset(&xrdreq, 0, sizeof (ClientRequest));
1584  xrdreq.rmdir.requestid = htons(kXR_rmdir);
1585 
1586  std::string s = resourceplusopaque.c_str();
1587 
1588  l = s.length() + 1;
1589  xrdreq.rmdir.dlen = htonl(l);
1590 
1591  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1592  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rmdir request.", 0, false);
1593  return -1;
1594  }
1595  } else {
1596  // --------- DELETE
1597  memset(&xrdreq, 0, sizeof (ClientRequest));
1598  xrdreq.rm.requestid = htons(kXR_rm);
1599 
1600  std::string s = resourceplusopaque.c_str();
1601 
1602  l = s.length() + 1;
1603  xrdreq.rm.dlen = htonl(l);
1604 
1605  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1606  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rm request.", 0, false);
1607  return -1;
1608  }
1609  }
1610 
1611 
1612  // We don't want to be invoked again after this request is finished
1613  return 1;
1614 
1615  }
1616 
1617 
1618 
1619  }
1620  case XrdHttpReq::rtPATCH:
1621  {
1622  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported yet.", 0, false);
1623 
1624  return -1;
1625  }
1627  {
1628 
1629 
1630 
1631  switch (reqstate) {
1632 
1633  case 0: // Stat() and add the current item to the list of the things to send
1634  {
1635 
1636  if (length > 0) {
1637  TRACE(REQ, "Reading request body " << length << " bytes.");
1638  char *p = 0;
1639  // We have to specifically read all the request body
1640 
1641  if (prot->BuffgetData(length, &p, true) < length) {
1642  prot->SendSimpleResp(501, NULL, NULL, (char *) "Error in getting the PROPFIND request body.", 0, false);
1643  return -1;
1644  }
1645 
1646  if ((depth > 1) || (depth < 0)) {
1647  prot->SendSimpleResp(501, NULL, NULL, (char *) "Invalid depth value.", 0, false);
1648  return -1;
1649  }
1650 
1651 
1652  parseBody(p, length);
1653  }
1654 
1655 
1656  // --------- STAT is always the first step
1657  memset(&xrdreq, 0, sizeof (ClientRequest));
1658  xrdreq.stat.requestid = htons(kXR_stat);
1659  std::string s = resourceplusopaque.c_str();
1660 
1661 
1662  l = resourceplusopaque.length() + 1;
1663  xrdreq.stat.dlen = htonl(l);
1664 
1665  if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1666  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1667  return -1;
1668  }
1669 
1670 
1671  if (depth == 0) {
1672  // We don't need to be invoked again
1673  return 1;
1674  } else
1675  // We need to be invoked again to complete the request
1676  return 0;
1677 
1678 
1679 
1680  break;
1681  }
1682 
1683  default: // Dirlist()
1684  {
1685 
1686  // --------- DIRLIST
1687  memset(&xrdreq, 0, sizeof (ClientRequest));
1689 
1690  std::string s = resourceplusopaque.c_str();
1692  //s += "?xrd.dirstat=1";
1693 
1694  l = s.length() + 1;
1695  xrdreq.dirlist.dlen = htonl(l);
1696 
1697  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1698  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1699  return -1;
1700  }
1701 
1702  // We don't want to be invoked again after this request is finished
1703  return 1;
1704  }
1705  }
1706 
1707 
1708  break;
1709  }
1710  case XrdHttpReq::rtMKCOL:
1711  {
1712 
1713  // --------- MKDIR
1714  memset(&xrdreq, 0, sizeof (ClientRequest));
1715  xrdreq.mkdir.requestid = htons(kXR_mkdir);
1716 
1717  std::string s = resourceplusopaque.c_str();
1719 
1720  l = s.length() + 1;
1721  xrdreq.mkdir.dlen = htonl(l);
1722 
1723  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1724  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1725  return -1;
1726  }
1727 
1728  // We don't want to be invoked again after this request is finished
1729  return 1;
1730  }
1731  case XrdHttpReq::rtMOVE:
1732  {
1733 
1734  // --------- MOVE
1735  memset(&xrdreq, 0, sizeof (ClientRequest));
1736  xrdreq.mv.requestid = htons(kXR_mv);
1737 
1738  std::string s = resourceplusopaque.c_str();
1739  s += " ";
1740 
1741  char buf[256];
1742  char *ppath;
1743  int port = 0;
1744  if (parseURL((char *) destination.c_str(), buf, port, &ppath)) {
1745  prot->SendSimpleResp(501, NULL, NULL, (char *) "Cannot parse destination url.", 0, false);
1746  return -1;
1747  }
1748 
1749  char buf2[256];
1750  strcpy(buf2, host.c_str());
1751  char *pos = strchr(buf2, ':');
1752  if (pos) *pos = '\0';
1753 
1754  // If we are a redirector we enforce that the host field is equal to
1755  // whatever was written in the destination url
1756  //
1757  // If we are a data server instead we cannot enforce anything, we will
1758  // just ignore the host part of the destination
1759  if ((prot->myRole == kXR_isManager) && strcmp(buf, buf2)) {
1760  prot->SendSimpleResp(501, NULL, NULL, (char *) "Only in-place renaming is supported for MOVE.", 0, false);
1761  return -1;
1762  }
1763 
1764 
1765 
1766 
1767  s += ppath;
1768 
1769  l = s.length() + 1;
1770  xrdreq.mv.dlen = htonl(l);
1772 
1773  if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1774  prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1775  return -1;
1776  }
1777 
1778  // We don't want to be invoked again after this request is finished
1779  return 1;
1780 
1781  }
1782  default:
1783  {
1784  prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported.", 0, false);
1785  return -1;
1786  }
1787 
1788  }
1789 
1790  return 1;
1791 }
1792 
1793 
1794 int
1795 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1796  if (iovN > 0) {
1797  if (xrdresp == kXR_error) {
1798  prot->SendSimpleResp(httpStatusCode, NULL, NULL, "Failed to determine checksum", 0, false);
1799  return -1;
1800  }
1801 
1802  TRACEI(REQ, "Checksum for HEAD " << resource.c_str() << " "
1803  << reinterpret_cast<char *>(iovP[0].iov_base) << "="
1804  << reinterpret_cast<char *>(iovP[iovN-1].iov_base));
1805 
1806  bool convert_to_base64 = m_req_cksum->needsBase64Padding();
1807  char *digest_value = reinterpret_cast<char *>(iovP[iovN-1].iov_base);
1808  if (convert_to_base64) {
1809  size_t digest_length = strlen(digest_value);
1810  unsigned char *digest_binary_value = (unsigned char *)malloc(digest_length);
1811  if (!Fromhexdigest(reinterpret_cast<unsigned char *>(digest_value), digest_length, digest_binary_value)) {
1812  prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to convert checksum hexdigest to base64.", 0, false);
1813  free(digest_binary_value);
1814  return -1;
1815  }
1816  char *digest_base64_value = (char *)malloc(digest_length + 1);
1817  // Binary length is precisely half the size of the hex-encoded digest_value; hence, divide length by 2.
1818  Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1819  free(digest_binary_value);
1820  digest_value = digest_base64_value;
1821  }
1822 
1823  digest_header = "Digest: ";
1824  digest_header += m_req_cksum->getHttpName();
1825  digest_header += "=";
1826  digest_header += digest_value;
1827  if (convert_to_base64) {free(digest_value);}
1828  return 0;
1829  } else {
1830  prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(), false);
1831  return -1;
1832  }
1833 }
1834 
1835 int
1836 XrdHttpReq::PostProcessListing(bool final_) {
1837 
1838  if (xrdresp == kXR_error) {
1839  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1840  httpStatusText.c_str(), httpStatusText.length(), false);
1841  return -1;
1842  }
1843 
1844  if (stringresp.empty()) {
1845  // Start building the HTML response
1846  stringresp = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1847  "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1848  "<head>\n"
1849  "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1850  "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1851  "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1852 
1853  stringresp += "<title>";
1854  stringresp += resource.c_str();
1855  stringresp += "</title>\n";
1856 
1857  stringresp += "</head>\n"
1858  "<body>\n";
1859 
1860  char *estr = escapeXML(resource.c_str());
1861 
1862  stringresp += "<h1>Listing of: ";
1863  stringresp += estr;
1864  stringresp += "</h1>\n";
1865 
1866  free(estr);
1867 
1868  stringresp += "<div id=\"header\">";
1869 
1870  stringresp += "<table id=\"ft\">\n"
1871  "<thead><tr>\n"
1872  "<th class=\"mode\">Mode</th>"
1873  "<th class=\"flags\">Flags</th>"
1874  "<th class=\"size\">Size</th>"
1875  "<th class=\"datetime\">Modified</th>"
1876  "<th class=\"name\">Name</th>"
1877  "</tr></thead>\n";
1878  }
1879 
1880  // Now parse the answer building the entries vector
1881  if (iovN > 0) {
1882  char *startp = (char *) iovP[0].iov_base, *endp = 0;
1883  char entry[1024];
1884  DirListInfo e;
1885  while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) {
1886  // Find the filename, it comes before the \n
1887  if ((endp = (char *) strchr((const char*) startp, '\n'))) {
1888  strncpy(entry, (char *) startp, endp - startp);
1889  entry[endp - startp] = 0;
1890  e.path = entry;
1891 
1892  endp++;
1893 
1894  // Now parse the stat info
1895  TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry
1896  << " stat=" << endp);
1897 
1898  long dummyl;
1899  sscanf(endp, "%ld %lld %ld %ld",
1900  &dummyl,
1901  &e.size,
1902  &e.flags,
1903  &e.modtime);
1904  } else
1905  strcpy(entry, (char *) startp);
1906 
1907  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
1908  // The entry is filled. <td class="ft-file"><a href="file1.txt">file1.txt</a></td>
1909  std::string p = "<tr>"
1910  "<td class=\"mode\">";
1911 
1912  if (e.flags & kXR_isDir) p += "d";
1913  else p += "-";
1914 
1915  if (e.flags & kXR_other) p += "o";
1916  else p += "-";
1917 
1918  if (e.flags & kXR_offline) p += "O";
1919  else p += "-";
1920 
1921  if (e.flags & kXR_readable) p += "r";
1922  else p += "-";
1923 
1924  if (e.flags & kXR_writable) p += "w";
1925  else p += "-";
1926 
1927  if (e.flags & kXR_xset) p += "x";
1928  else p += "-";
1929 
1930  p += "</td>";
1931  p += "<td class=\"mode\">" + itos(e.flags) + "</td>"
1932  "<td class=\"size\">" + itos(e.size) + "</td>"
1933  "<td class=\"datetime\">" + ISOdatetime(e.modtime) + "</td>"
1934  "<td class=\"name\">"
1935  "<a href=\"";
1936 
1937  if (resource != "/") {
1938 
1939  char *estr = escapeXML(resource.c_str());
1940 
1941  p += estr;
1942  if (!p.empty() && p[p.size() - 1] != '/')
1943  p += "/";
1944 
1945  free(estr);
1946  }
1947  std::unique_ptr<char, decltype(&free)> estr(escapeXML(e.path.c_str()), &free);
1948  p += estr.get();
1949  if (e.flags & kXR_isDir) p += "/";
1950  p += "\">";
1951  p += estr.get();
1952  if (e.flags & kXR_isDir) p += "/";
1953  p += "</a></td></tr>";
1954 
1955  stringresp += p;
1956  }
1957 
1958  if (endp) {
1959  char *pp = (char *)strchr((const char *)endp, '\n');
1960  if (pp) startp = pp+1;
1961  else break;
1962  } else break;
1963 
1964  }
1965  }
1966 
1967  // If this was the last bunch of entries, send the buffer and empty it immediately
1968  if (final_) {
1969  stringresp += "</table></div><br><br><hr size=1>"
1970  "<p><span id=\"requestby\">Request by ";
1971 
1972  if (prot->SecEntity.name)
1973  stringresp += prot->SecEntity.name;
1974  else
1975  stringresp += prot->Link->ID;
1976 
1977  if (prot->SecEntity.vorg ||
1978  prot->SecEntity.name ||
1979  prot->SecEntity.moninfo ||
1980  prot->SecEntity.role)
1981  stringresp += " (";
1982 
1983  if (prot->SecEntity.vorg) {
1984  stringresp += " VO: ";
1985  stringresp += prot->SecEntity.vorg;
1986  }
1987 
1988  if (prot->SecEntity.moninfo) {
1989  stringresp += " DN: ";
1990  stringresp += prot->SecEntity.moninfo;
1991  } else
1992  if (prot->SecEntity.name) {
1993  stringresp += " DN: ";
1994  stringresp += prot->SecEntity.name;
1995  }
1996 
1997  if (prot->SecEntity.role) {
1998  stringresp += " Role: ";
1999  stringresp += prot->SecEntity.role;
2000  if (prot->SecEntity.endorsements) {
2001  stringresp += " (";
2003  stringresp += ") ";
2004  }
2005  }
2006 
2007  if (prot->SecEntity.vorg ||
2008  prot->SecEntity.moninfo ||
2009  prot->SecEntity.role)
2010  stringresp += " )";
2011 
2012  if (prot->SecEntity.host) {
2013  stringresp += " ( ";
2014  stringresp += prot->SecEntity.host;
2015  stringresp += " )";
2016  }
2017 
2018  stringresp += "</span></p>\n";
2019  stringresp += "<p>Powered by XrdHTTP ";
2020  stringresp += XrdVSTRING;
2021  stringresp += " (CERN IT-SDC)</p>\n";
2022 
2023  prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive);
2024  stringresp.clear();
2025  return keepalive ? 1 : -1;
2026  }
2027 
2028  return 0;
2029 }
2030 
2031 int
2032 XrdHttpReq::ReturnGetHeaders() {
2033  std::string responseHeader;
2034  if (!m_digest_header.empty()) {
2035  responseHeader = m_digest_header;
2036  }
2037  if (fileflags & kXR_cachersp) {
2038  if (!responseHeader.empty()) {
2039  responseHeader += "\r\n";
2040  }
2041  addAgeHeader(responseHeader);
2042  }
2043 
2045  if (uranges.empty() && readRangeHandler.getError()) {
2046  prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false);
2047  return -1;
2048  }
2049 
2050  if (readRangeHandler.isFullFile()) {
2051  // Full file.
2052  TRACEI(REQ, "Sending full file: " << filesize);
2053  if (m_transfer_encoding_chunked && m_trailer_headers) {
2054  prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive);
2055  } else {
2056  prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive);
2057  }
2058  return 0;
2059  }
2060 
2062  // Possibly with zero sized file but should have been included
2063  // in the FullFile case above
2064  if (uranges.size() != 1)
2065  return -1;
2066 
2067  // Only one range to return to the user
2068  char buf[64];
2069  const off_t cnt = uranges[0].end - uranges[0].start + 1;
2070 
2071  XrdOucString s = "Content-Range: bytes ";
2072  sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize);
2073  s += buf;
2074  if (!responseHeader.empty()) {
2075  s += "\r\n";
2076  s += responseHeader.c_str();
2077  }
2078 
2079  if (m_transfer_encoding_chunked && m_trailer_headers) {
2080  prot->StartChunkedResp(206, NULL, (char *)s.c_str(), -1, keepalive);
2081  } else {
2082  prot->SendSimpleResp(206, NULL, (char *)s.c_str(), NULL, cnt, keepalive);
2083  }
2084  return 0;
2085  }
2086 
2087  // Multiple reads to perform, compose and send the header
2088  off_t cnt = 0;
2089  for (auto &ur : uranges) {
2090  cnt += ur.end - ur.start + 1;
2091 
2092  cnt += buildPartialHdr(ur.start,
2093  ur.end,
2094  filesize,
2095  (char *) "123456").size();
2096 
2097  }
2098  cnt += buildPartialHdrEnd((char *) "123456").size();
2099  std::string header = "Content-Type: multipart/byteranges; boundary=123456";
2100  if (!m_digest_header.empty()) {
2101  header += "\n";
2102  header += m_digest_header;
2103  }
2104  if (fileflags & kXR_cachersp) {
2105  if (!header.empty()) {
2106  header += "\r\n";
2107  }
2108  addAgeHeader(header);
2109  }
2110 
2111  if (m_transfer_encoding_chunked && m_trailer_headers) {
2112  prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive);
2113  } else {
2114  prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive);
2115  }
2116  return 0;
2117 }
2118 
2119 // This is invoked by the callbacks, after something has happened in the bridge
2120 
2121 int XrdHttpReq::PostProcessHTTPReq(bool final_) {
2122 
2123  TRACEI(REQ, "PostProcessHTTPReq req: " << request << " reqstate: " << reqstate << " final_:" << final_);
2124  mapXrdErrorToHttpStatus();
2125 
2126  if(xrdreq.set.requestid == htons(kXR_set)) {
2127  // We have set the user agent, if it fails we return a 500 error, otherwise the callback is successful --> we continue
2128  if(xrdresp != kXR_ok) {
2129  prot->SendSimpleResp(500, nullptr, nullptr, "Could not set user agent.", 0, false);
2130  return -1;
2131  }
2132  return 0;
2133  }
2134 
2135  switch (request) {
2136  case XrdHttpReq::rtUnknown:
2137  {
2138  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 1", 0, false);
2139  return -1;
2140  }
2142  {
2143  prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 2", 0, false);
2144  return -1;
2145  }
2146  case XrdHttpReq::rtHEAD:
2147  {
2148  if (xrdresp != kXR_ok) {
2149  // NOTE that HEAD MUST NOT return a body, even in the case of failure.
2150  prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, false);
2151  return -1;
2152  } else if (reqstate == 0) {
2153  if (iovN > 0) {
2154  std::string response_headers;
2155 
2156  // Now parse the stat info
2157  TRACEI(REQ, "Stat for HEAD " << resource.c_str()
2158  << " stat=" << (char *) iovP[0].iov_base);
2159 
2160  long dummyl;
2161  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2162  &dummyl,
2163  &filesize,
2164  &fileflags,
2165  &filemodtime);
2166 
2167  if (m_req_digest.size()) {
2168  return 0;
2169  } else {
2170  if (fileflags & kXR_cachersp) {
2171  addAgeHeader(response_headers);
2172  response_headers += "\r\n";
2173  }
2174  response_headers += "Accept-Ranges: bytes";
2175  prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2176  return keepalive ? 1 : -1;
2177  }
2178  }
2179 
2180  prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, keepalive);
2181  bool ret_keepalive = keepalive; // reset() clears keepalive
2182  reset();
2183  return ret_keepalive ? 1 : -1;
2184  } else { // We requested a checksum and now have its response.
2185  if (iovN > 0) {
2186  std::string response_headers;
2187  int response = PostProcessChecksum(response_headers);
2188  if (-1 == response) {
2189  return -1;
2190  }
2191  if (!response_headers.empty()) {response_headers += "\r\n";}
2192  if (fileflags & kXR_cachersp) {
2193  addAgeHeader(response_headers);
2194  response_headers += "\r\n";
2195  }
2196  response_headers += "Accept-Ranges: bytes";
2197  prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2198  return keepalive ? 1 : -1;
2199  } else {
2200  prot->SendSimpleResp(500, NULL, NULL, "Underlying filesystem failed to calculate checksum.", 0, false);
2201  return -1;
2202  }
2203  }
2204  }
2205  case XrdHttpReq::rtGET:
2206  {
2207  // To duplicate the state diagram from the rtGET request state
2208  // - 0: Perform an open request
2209  // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
2210  // - 2: Perform a close (for directory listings only)
2211  // - 3: Perform a dirlist
2212  // - 4+: Reads from file; if at end, perform a close.
2213  switch (reqstate) {
2214  case 0: // open
2215  {
2216  if (xrdresp == kXR_ok) {
2217  fopened = true;
2218  getfhandle();
2219 
2220  // Always try to parse response. In the case of a caching proxy, the open
2221  // will have created the file in cache
2222  if (iovP[1].iov_len > 1) {
2223  TRACEI(REQ, "Stat for GET " << resource.c_str()
2224  << " stat=" << (char *) iovP[1].iov_base);
2225 
2226  long dummyl;
2227  sscanf((const char *) iovP[1].iov_base, "%ld %lld %ld %ld",
2228  &dummyl,
2229  &filesize,
2230  &fileflags,
2231  &filemodtime);
2232 
2233  // If this is a directory, bail out early; we will close the file handle
2234  // and then issue a directory listing.
2235  if (fileflags & kXR_isDir) {
2236  return 0;
2237  }
2238 
2240 
2241  // As above: if the client specified a response size, we use that.
2242  // Otherwise, utilize the filesize
2243  if (!length) {
2244  length = filesize;
2245  }
2246  }
2247  else {
2248  TRACEI(ALL, "GET returned no STAT information. Internal error?");
2249  prot->SendSimpleResp(500, NULL, NULL, "Storage system did not return stat info.", 0, false);
2250  return -1;
2251  }
2252  return 0;
2253  } else if (xrderrcode == kXR_isDirectory) { // This is a directory; trigger directory-handling topic.
2254  fileflags = kXR_isDir;
2255  return 0;
2256  } else { // xrdresp indicates an error occurred
2257 
2258  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2259  httpStatusText.c_str(), httpStatusText.length(), false);
2260  return -1;
2261  }
2262  // Case should not be reachable
2263  return -1;
2264  } // end open
2265  case 1: // checksum was requested and now we have its response.
2266  {
2267  return PostProcessChecksum(m_digest_header);
2268  }
2269  case 2: // close file handle in case of the directory
2270  {
2271  if (xrdresp != kXR_ok) {
2272  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2273  httpStatusText.c_str(), httpStatusText.length(), false);
2274  return -1;
2275  }
2276  return 0;
2277  }
2278  case 3: // handle the directory listing response
2279  {
2280  return PostProcessListing(final_);
2281  }
2282  default: //read or readv, followed by a close.
2283  {
2284  // If we are postprocessing a close, potentially send out informational trailers
2285  if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing)
2286  {
2288  if (rrerror) {
2289  httpStatusCode = rrerror.httpRetCode;
2290  httpStatusText = rrerror.errMsg;
2291  }
2292 
2293  if (m_transfer_encoding_chunked && m_trailer_headers) {
2294  if (prot->ChunkRespHeader(0))
2295  return -1;
2296 
2297  const std::string crlf = "\r\n";
2298  std::stringstream ss;
2299  ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpStatusText << crlf;
2300 
2301  const auto header = ss.str();
2302  if (prot->SendData(header.c_str(), header.size()))
2303  return -1;
2304 
2305  if (prot->ChunkRespFooter())
2306  return -1;
2307  }
2308 
2309  if (rrerror) return -1;
2310  return keepalive ? 1 : -1;
2311  }
2312 
2313  // On error, we can only send out a message if trailers are enabled and the
2314  // status response in trailer behavior is requested.
2315  if (xrdresp == kXR_error) {
2316  sendFooterError("");
2317  return -1;
2318  }
2319 
2320 
2321  TRACEI(REQ, "Got data vectors to send:" << iovN);
2322 
2323  XrdHttpIOList received;
2324  getReadResponse(received);
2325 
2326  int rc;
2328  rc = sendReadResponseSingleRange(received);
2329  } else {
2330  rc = sendReadResponsesMultiRanges(received);
2331  }
2332  if (rc) {
2333  // make sure readRangeHandler will trigger close
2334  // of file after next NextReadList().
2336  }
2337 
2338  return 0;
2339  } // end read or readv
2340 
2341  } // switch reqstate
2342  break;
2343  } // case GET
2344 
2345  case XrdHttpReq::rtPUT:
2346  {
2347  if (!fopened) {
2348 
2349  if (xrdresp != kXR_ok) {
2350 
2351  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2352  httpStatusText.c_str(), httpStatusText.length(), keepalive);
2353  return -1;
2354  }
2355 
2356  getfhandle();
2357  fopened = true;
2358 
2359  // We try to completely fill up our buffer before flushing
2360  prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2361 
2362  if (sendcontinue) {
2363  prot->SendSimpleResp(100, NULL, NULL, 0, 0, keepalive);
2364  return 0;
2365  }
2366 
2367  break;
2368  } else {
2369 
2370 
2371  // If we are here it's too late to send a proper error message...
2372  if (xrdresp == kXR_error) return -1;
2373 
2374  if (ntohs(xrdreq.header.requestid) == kXR_write) {
2375  int l = ntohl(xrdreq.write.dlen);
2376 
2377  // Consume the written bytes
2378  prot->BuffConsume(ntohl(xrdreq.write.dlen));
2379  writtenbytes += l;
2380 
2381  // Update the chunk offset
2382  if (m_transfer_encoding_chunked) {
2383  m_current_chunk_offset += l;
2384  }
2385 
2386  // We try to completely fill up our buffer before flushing
2387  prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2388 
2389  return 0;
2390  }
2391 
2392  if (ntohs(xrdreq.header.requestid) == kXR_close) {
2393  if (xrdresp == kXR_ok) {
2394  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2395  return keepalive ? 1 : -1;
2396  } else {
2397  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2398  httpStatusText.c_str(), httpStatusText.length(), keepalive);
2399  return -1;
2400  }
2401  }
2402 
2403 
2404  }
2405 
2406 
2407 
2408 
2409 
2410  break;
2411  }
2412 
2413 
2414 
2415  case XrdHttpReq::rtDELETE:
2416  {
2417 
2418  if (xrdresp != kXR_ok) {
2419  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2420  httpStatusText.c_str(), httpStatusText.length(), keepalive);
2421  return -1;
2422  }
2423 
2424 
2425 
2426 
2427  switch (reqstate) {
2428 
2429  case 0: // response to stat()
2430  {
2431  if (iovN > 0) {
2432 
2433  // Now parse the stat info
2434  TRACEI(REQ, "Stat for removal " << resource.c_str()
2435  << " stat=" << (char *) iovP[0].iov_base);
2436 
2437  long dummyl;
2438  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2439  &dummyl,
2440  &filesize,
2441  &fileflags,
2442  &filemodtime);
2443  }
2444 
2445  return 0;
2446  }
2447  default: // response to rm
2448  {
2449  if (xrdresp == kXR_ok) {
2450  prot->SendSimpleResp(200, NULL, NULL, (char *) ":-)", 0, keepalive);
2451  return keepalive ? 1 : -1;
2452  }
2453  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2454  httpStatusText.c_str(), httpStatusText.length(), keepalive);
2455  return -1;
2456  }
2457  }
2458 
2459 
2460  }
2461 
2463  {
2464 
2465  if (xrdresp == kXR_error) {
2466  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2467  httpStatusText.c_str(), httpStatusText.length(), false);
2468  return -1;
2469  }
2470 
2471  switch (reqstate) {
2472 
2473  case 0: // response to stat()
2474  {
2475  DirListInfo e;
2476  e.size = 0;
2477  e.flags = 0;
2478 
2479  // Now parse the answer building the entries vector
2480  if (iovN > 0) {
2481  e.path = resource.c_str();
2482 
2483  // Now parse the stat info
2484  TRACEI(REQ, "Collection " << resource.c_str()
2485  << " stat=" << (char *) iovP[0].iov_base);
2486 
2487  long dummyl;
2488  sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2489  &dummyl,
2490  &e.size,
2491  &e.flags,
2492  &e.modtime);
2493 
2494  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2495  /* The entry is filled. */
2496 
2497 
2498  std::string p;
2499  stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2500 
2501  char *estr = escapeXML(e.path.c_str());
2502 
2503  stringresp += "<D:href>";
2504  stringresp += estr;
2505  stringresp += "</D:href>\n";
2506 
2507  free(estr);
2508 
2509  stringresp += "<D:propstat>\n<D:prop>\n";
2510 
2511  // Now add the properties that we have to add
2512 
2513  // File size
2514  stringresp += "<lp1:getcontentlength>";
2515  stringresp += itos(e.size);
2516  stringresp += "</lp1:getcontentlength>\n";
2517 
2518 
2519 
2520  stringresp += "<lp1:getlastmodified>";
2522  stringresp += "</lp1:getlastmodified>\n";
2523 
2524 
2525 
2526  if (e.flags & kXR_isDir) {
2527  stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2528  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2529  } else {
2530  stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2531  }
2532 
2533  if (e.flags & kXR_xset) {
2534  stringresp += "<lp1:executable>T</lp1:executable>\n";
2535  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2536  } else {
2537  stringresp += "<lp1:executable>F</lp1:executable>\n";
2538  }
2539 
2540 
2541 
2542  stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2543 
2544 
2545  }
2546 
2547 
2548  }
2549 
2550  // If this was the last bunch of entries, send the buffer and empty it immediately
2551  if ((depth == 0) || !(e.flags & kXR_isDir)) {
2552  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";
2553  stringresp.insert(0, s);
2554  stringresp += "</D:multistatus>\n";
2555  prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2556  (char *) stringresp.c_str(), stringresp.length(), keepalive);
2557  stringresp.clear();
2558  return keepalive ? 1 : -1;
2559  }
2560 
2561  break;
2562  }
2563  default: // response to dirlist()
2564  {
2565 
2566 
2567  // Now parse the answer building the entries vector
2568  if (iovN > 0) {
2569  char *startp = (char *) iovP[0].iov_base, *endp = 0;
2570  char entry[1024];
2571  DirListInfo e;
2572 
2573  while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)(iovP[0].iov_len - 1) ) {
2574  // Find the filename, it comes before the \n
2575  if ((endp = (char *) mystrchrnul((const char*) startp, '\n'))) {
2576  strncpy(entry, (char *) startp, endp - startp);
2577  entry[endp - startp] = 0;
2578  e.path = entry;
2579 
2580  endp++;
2581 
2582  // Now parse the stat info
2583  TRACEI(REQ, "Dirlist " <<resource.c_str() <<" entry=" <<entry
2584  << " stat=" << endp);
2585 
2586  long dummyl;
2587  sscanf(endp, "%ld %lld %ld %ld",
2588  &dummyl,
2589  &e.size,
2590  &e.flags,
2591  &e.modtime);
2592  }
2593 
2594 
2595  if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2596  /* The entry is filled.
2597 
2598  <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/" xmlns:lp3="LCGDM:">
2599  <D:href>/dpm/cern.ch/home/testers2.eu-emi.eu/</D:href>
2600  <D:propstat>
2601  <D:prop>
2602  <lp1:getcontentlength>1</lp1:getcontentlength>
2603  <lp1:getlastmodified>Tue, 01 May 2012 02:42:13 GMT</lp1:getlastmodified>
2604  <lp1:resourcetype>
2605  <D:collection/>
2606  </lp1:resourcetype>
2607  </D:prop>
2608  <D:status>HTTP/1.1 200 OK</D:status>
2609  </D:propstat>
2610  </D:response>
2611  */
2612 
2613 
2614  std::string p = resource.c_str();
2615  if (*p.rbegin() != '/') p += "/";
2616 
2617  p += e.path;
2618 
2619  stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2620 
2621  char *estr = escapeXML(p.c_str());
2622  stringresp += "<D:href>";
2623  stringresp += estr;
2624  stringresp += "</D:href>\n";
2625  free(estr);
2626 
2627  stringresp += "<D:propstat>\n<D:prop>\n";
2628 
2629 
2630 
2631  // Now add the properties that we have to add
2632 
2633  // File size
2634  stringresp += "<lp1:getcontentlength>";
2635  stringresp += itos(e.size);
2636  stringresp += "</lp1:getcontentlength>\n";
2637 
2638  stringresp += "<lp1:getlastmodified>";
2640  stringresp += "</lp1:getlastmodified>\n";
2641 
2642  if (e.flags & kXR_isDir) {
2643  stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2644  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2645  } else {
2646  stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2647  }
2648 
2649  if (e.flags & kXR_xset) {
2650  stringresp += "<lp1:executable>T</lp1:executable>\n";
2651  stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2652  } else {
2653  stringresp += "<lp1:executable>F</lp1:executable>\n";
2654  }
2655 
2656  stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2657 
2658 
2659  }
2660 
2661 
2662 
2663  if (endp) {
2664  char *pp = (char *)strchr((const char *)endp, '\n');
2665  if (pp) startp = pp+1;
2666  else break;
2667  } else break;
2668 
2669  }
2670  }
2671 
2672 
2673 
2674  // If this was the last bunch of entries, send the buffer and empty it immediately
2675  if (final_) {
2676  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";
2677  stringresp.insert(0, s);
2678  stringresp += "</D:multistatus>\n";
2679  prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2680  (char *) stringresp.c_str(), stringresp.length(), keepalive);
2681  stringresp.clear();
2682  return keepalive ? 1 : -1;
2683  }
2684 
2685  break;
2686  } // default reqstate
2687  } // switch reqstate
2688 
2689 
2690  break;
2691 
2692  } // case propfind
2693 
2694  case XrdHttpReq::rtMKCOL:
2695  {
2696 
2697  if (xrdresp != kXR_ok) {
2698  if (xrderrcode == kXR_ItExists) {
2699  prot->SendSimpleResp(405, NULL, NULL, (char *) "Method is not allowed; resource already exists.", 0, false);
2700  } else {
2701  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2702  httpStatusText.c_str(), httpStatusText.length(), false);
2703  }
2704  return -1;
2705  }
2706 
2707  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2708  return keepalive ? 1 : -1;
2709 
2710  }
2711  case XrdHttpReq::rtMOVE:
2712  {
2713 
2714  if (xrdresp != kXR_ok) {
2715  prot->SendSimpleResp(httpStatusCode, NULL, NULL, (char *) etext.c_str(), 0, false);
2716  return -1;
2717  }
2718 
2719  prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2720  return keepalive ? 1 : -1;
2721 
2722  }
2723 
2724  default:
2725  break;
2726 
2727  }
2728 
2729 
2730  switch (xrdresp) {
2731  case kXR_error:
2732  prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2733  httpStatusText.c_str(), httpStatusText.length(), false);
2734  return -1;
2735  break;
2736 
2737  default:
2738 
2739  break;
2740  }
2741 
2742 
2743  return 0;
2744 }
2745 
2746 void
2747 XrdHttpReq::sendFooterError(const std::string &extra_text) {
2748  if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2749  // A trailer header is appropriate in this case; this is signified by
2750  // a chunk with size zero, then the trailer, then a crlf.
2751  //
2752  // We only send the status trailer when explicitly requested; otherwise a
2753  // "normal" HTTP client might simply see a short response and think it's a
2754  // success
2755 
2756  if (prot->ChunkRespHeader(0))
2757  return;
2758 
2759  std::stringstream ss;
2760  ss << httpStatusCode << ": " << httpStatusText;
2761  if (!extra_text.empty())
2762  ss << ": " << extra_text;
2763  TRACEI(REQ, ss.str());
2764  ss << "\r\n";
2765 
2766  const auto header = "X-Transfer-Status: " + ss.str();
2767  if (prot->SendData(header.c_str(), header.size()))
2768  return;
2769 
2770  prot->ChunkRespFooter();
2771  } else {
2772  TRACEI(REQ, httpStatusCode << ": " << httpStatusText << (extra_text.empty() ? "" : (": " + extra_text)));
2773  }
2774 }
2775 
2776 void XrdHttpReq::addAgeHeader(std::string &headers) {
2777  long object_age = time(NULL) - filemodtime;
2778  headers += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2779 }
2780 
2782 
2783  TRACE(REQ, " XrdHttpReq request ended.");
2784 
2785  //if (xmlbody) xmlFreeDoc(xmlbody);
2787  readClosing = false;
2788  writtenbytes = 0;
2789  etext.clear();
2790  redirdest = "";
2791 
2792  // // Here we should deallocate this
2793  // const struct iovec *iovP //!< pointer to data array
2794  // int iovN, //!< array count
2795  // int iovL, //!< byte count
2796  // bool final //!< true -> final result
2797 
2798 
2799  //xmlbody = 0;
2800  depth = 0;
2803  ralist.clear();
2804  ralist.shrink_to_fit();
2805 
2806  request = rtUnset;
2807  resource = "";
2808  allheaders.clear();
2809 
2810  // Reset the state of the request's digest request.
2811  m_req_digest.clear();
2812  m_digest_header.clear();
2813  m_req_cksum = nullptr;
2814 
2816  m_user_agent = "";
2817 
2818  headerok = false;
2819  keepalive = true;
2820  length = 0;
2821  filesize = 0;
2822  depth = 0;
2823  sendcontinue = false;
2824 
2825  m_transfer_encoding_chunked = false;
2826  m_current_chunk_size = -1;
2827  m_current_chunk_offset = 0;
2828 
2829  m_trailer_headers = false;
2830  m_status_trailer = false;
2831 
2833  reqstate = 0;
2834 
2835  memset(&xrdreq, 0, sizeof (xrdreq));
2836  memset(&xrdresp, 0, sizeof (xrdresp));
2838 
2839  etext.clear();
2840  redirdest = "";
2841 
2842  stringresp = "";
2843 
2844  host = "";
2845  destination = "";
2846  hdr2cgistr = "";
2847  m_appended_hdr2cgistr = false;
2848  m_appended_asize = false;
2849 
2850  iovP = 0;
2851  iovN = 0;
2852  iovL = 0;
2853 
2854 
2855  if (opaque) delete(opaque);
2856  opaque = 0;
2857 
2858  fopened = false;
2859 
2860  final = false;
2861 
2862  mScitag = -1;
2863 }
2864 
2865 void XrdHttpReq::getfhandle() {
2866 
2867  memcpy(fhandle, iovP[0].iov_base, 4);
2868  TRACEI(REQ, "fhandle:" <<
2869  (int) fhandle[0] << ":" << (int) fhandle[1] << ":" << (int) fhandle[2] << ":" << (int) fhandle[3]);
2870 
2871 }
2872 
2873 void XrdHttpReq::getReadResponse(XrdHttpIOList &received) {
2874  received.clear();
2875 
2876  if (ntohs(xrdreq.header.requestid) == kXR_readv) {
2877  readahead_list *l;
2878  char *p;
2879  kXR_int32 len;
2880 
2881  // Cycle on all the data that is coming from the server
2882  for (int i = 0; i < iovN; i++) {
2883 
2884  for (p = (char *) iovP[i].iov_base; p < (char *) iovP[i].iov_base + iovP[i].iov_len;) {
2885  l = (readahead_list *) p;
2886  len = ntohl(l->rlen);
2887 
2888  received.emplace_back(p+sizeof(readahead_list), -1, len);
2889 
2890  p += sizeof (readahead_list);
2891  p += len;
2892 
2893  }
2894  }
2895  return;
2896  }
2897 
2898  // kXR_read result
2899  for (int i = 0; i < iovN; i++) {
2900  received.emplace_back((char*)iovP[i].iov_base, -1, iovP[i].iov_len);
2901  }
2902 
2903 }
2904 
2905 int XrdHttpReq::sendReadResponsesMultiRanges(const XrdHttpIOList &received) {
2906 
2907  if (received.size() == 0) {
2908  bool start, finish;
2909  if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2910  return -1;
2911  }
2912  return 0;
2913  }
2914 
2915  // user is expecting multiple ranges, we must be prepared to send an
2916  // individual header for each and format it according to the http rules
2917 
2918  struct rinfo {
2919  bool start;
2920  bool finish;
2921  const XrdOucIOVec2 *ci;
2923  std::string st_header;
2924  std::string fin_header;
2925  };
2926 
2927  // report each received byte chunk to the range handler and record the details
2928  // of original user range it related to and if starts a range or finishes all.
2929  // also sum the total of the headers and data which need to be sent to the user,
2930  // in case we need it for chunked transfer encoding
2931  std::vector<rinfo> rvec;
2932  off_t sum_len = 0;
2933 
2934  rvec.reserve(received.size());
2935 
2936  for(const auto &rcv: received) {
2937  rinfo rentry;
2938  bool start, finish;
2940 
2941  if (readRangeHandler.NotifyReadResult(rcv.size, &ur, start, finish) < 0) {
2942  return -1;
2943  }
2944  rentry.ur = ur;
2945  rentry.start = start;
2946  rentry.finish = finish;
2947  rentry.ci = &rcv;
2948 
2949  if (start) {
2950  std::string s = buildPartialHdr(ur->start,
2951  ur->end,
2952  filesize,
2953  (char *) "123456");
2954 
2955  rentry.st_header = s;
2956  sum_len += s.size();
2957  }
2958 
2959  sum_len += rcv.size;
2960 
2961  if (finish) {
2962  std::string s = buildPartialHdrEnd((char *) "123456");
2963  rentry.fin_header = s;
2964  sum_len += s.size();
2965  }
2966 
2967  rvec.push_back(rentry);
2968  }
2969 
2970 
2971  // Send chunked encoding header
2972  if (m_transfer_encoding_chunked && m_trailer_headers) {
2973  prot->ChunkRespHeader(sum_len);
2974  }
2975 
2976  // send the user the headers / data
2977  for(const auto &rentry: rvec) {
2978 
2979  if (rentry.start) {
2980  TRACEI(REQ, "Sending multipart: " << rentry.ur->start << "-" << rentry.ur->end);
2981  if (prot->SendData((char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2982  return -1;
2983  }
2984  }
2985 
2986  // Send all the data we have
2987  if (prot->SendData((char *) rentry.ci->data, rentry.ci->size)) {
2988  return -1;
2989  }
2990 
2991  if (rentry.finish) {
2992  if (prot->SendData((char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2993  return -1;
2994  }
2995  }
2996  }
2997 
2998  // Send chunked encoding footer
2999  if (m_transfer_encoding_chunked && m_trailer_headers) {
3000  prot->ChunkRespFooter();
3001  }
3002 
3003  return 0;
3004 }
3005 
3006 int XrdHttpReq::sendReadResponseSingleRange(const XrdHttpIOList &received) {
3007  // single range http transfer
3008 
3009  if (received.size() == 0) {
3010  bool start, finish;
3011  if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
3012  return -1;
3013  }
3014  return 0;
3015  }
3016 
3017  off_t sum = 0;
3018  // notify the range handler and return if error
3019  for(const auto &rcv: received) {
3020  bool start, finish;
3021  if (readRangeHandler.NotifyReadResult(rcv.size, nullptr, start, finish) < 0) {
3022  return -1;
3023  }
3024  sum += rcv.size;
3025  }
3026 
3027  // Send chunked encoding header
3028  if (m_transfer_encoding_chunked && m_trailer_headers) {
3029  prot->ChunkRespHeader(sum);
3030  }
3031  for(const auto &rcv: received) {
3032  if (prot->SendData((char *) rcv.data, rcv.size)) return -1;
3033  }
3034  if (m_transfer_encoding_chunked && m_trailer_headers) {
3035  prot->ChunkRespFooter();
3036  }
3037  return 0;
3038 }
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)
char * unquote(char *str)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
char * quote(const char *str)
Utility functions for XrdHTTP.
std::vector< XrdOucIOVec2 > XrdHttpIOList
Definition: XrdHttpUtils.hh:95
std::string obfuscateAuth(const std::string &input)
#define STR_NPOS
#define TRACE_DEBUG
Definition: XrdTrace.hh:36
#define TRACE(act, x)
Definition: XrdTrace.hh:63
#define TRACING(x)
Definition: XrdTrace.hh:70
#define TRACEI(act, x)
Definition: XrdTrace.hh:66
XrdHttpChecksumRawPtr getChecksumToRun(const std::string &userDigest) const
bool needsBase64Padding() const
std::string getXRootDConfigDigestName() const
std::string getHttpName() const
virtual int ProcessReq(XrdHttpExtReq &)=0
static char * secretkey
The key used to calculate the url hashes.
static kXR_int32 myRole
Our role.
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static XrdHttpChecksumHandler cksumHandler
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
XrdLink * Link
The link we are bound to.
int doStat(char *fname)
Perform a Stat request.
static bool isdesthttps
True if the redirections must be towards https targets.
static char * listredir
Url to redirect to in the case a listing is requested.
static bool listdeny
If true, any form of listing is denied.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
void reset()
resets this handler
const XrdHttpIOList & NextReadList()
return XrdHttpIOList for sending to read or readv
void ParseContentRange(const char *const line)
parse the line after a "Range: " http request header
int SetFilesize(const off_t sz)
sets the filesize, used during resolving and issuing range requests
void NotifyError()
Force handler to enter error state.
bool isFullFile()
indicates when there were no valid Range head ranges supplied
std::vector< UserRange > UserRangeList
int NotifyReadResult(const ssize_t ret, const UserRange **const urp, bool &start, bool &allend)
Advance internal counters concerning received bytes.
const Error & getError() const
return the Error object
bool isSingleRange()
indicates a single range (implied whole file, or single range) or empty file
const UserRangeList & ListResolvedRanges()
return resolved (i.e. obsolute start and end) byte ranges desired
int reqstate
State machine to talk to the bridge.
Definition: XrdHttpReq.hh: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:936
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:634
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:791
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:2781
virtual bool Error(XrdXrootd::Bridge::Context &info, int ecode, const char *etext)
Definition: XrdHttpReq.cc:511
static const int minTotID
Definition: XrdNetPMark.hh:86
static const int maxTotID
Definition: XrdNetPMark.hh:87
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