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