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