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