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