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