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 if (prot->isdesthttps)
557 redirdest = "Location: https://";
558 else
559 redirdest = "Location: http://";
560
561 // port < 0 signals switch to full URL
562 if (port < 0)
563 {
564 if (strncmp(hname, "file://", 7) == 0)
565 {
566 TRACE(REQ, " XrdHttpReq::Redir Switching to file:// ");
567 redirdest = "Location: "; // "file://" already contained in hname
568 }
569 }
570 // Beware, certain Ofs implementations (e.g. EOS) add opaque data directly to the host name
571 // This must be correctly treated here and appended to the opaque info
572 // that we may already have
573 char *pp = strchr((char *)hname, '?');
574 char *vardata = 0;
575 if (pp) {
576 *pp = '\0';
577 redirdest += hname;
578 vardata = pp+1;
579 int varlen = strlen(vardata);
580
581 //Now extract the remaining, vardata points to it
582 while(*vardata == '&' && varlen) {vardata++; varlen--;}
583
584 // Put the question mark back where it was
585 *pp = '?';
586 }
587 else
588 redirdest += hname;
589
590 if (port > 0) {
591 sprintf(buf, ":%d", port);
592 redirdest += buf;
593 }
594
595 redirdest += encode_str(resource.c_str()).c_str();
596
597 // Here we put back the opaque info, if any
598 if (vardata) {
599 redirdest += "?&";
600 redirdest += encode_opaque(vardata).c_str();
601 }
602
603 // Shall we put also the opaque data of the request? Maybe not
604 //int l;
605 //if (opaque && opaque->Env(l))
606 // redirdest += opaque->Env(l);
607
608
609 time_t timenow = 0;
610 if (!prot->isdesthttps && prot->ishttps) {
611 // If the destination is not https, then we suppose that it
612 // will need this token to fill its authorization info
613 timenow = time(0);
614 calcHashes(hash, this->resource.c_str(), (kXR_int16) request,
615 &prot->SecEntity,
616 timenow,
617 prot->secretkey);
618 }
619
620 if (hash[0]) {
621 appendOpaque(redirdest, &prot->SecEntity, hash, timenow);
622 } else
623 appendOpaque(redirdest, 0, 0, 0);
624
625
626 TRACE(REQ, " XrdHttpReq::Redir Redirecting to " << obfuscateAuth(redirdest.c_str()).c_str());
627
628 if (request != rtGET)
629 prot->SendSimpleResp(307, NULL, (char *) redirdest.c_str(), 0, 0, keepalive);
630 else
631 prot->SendSimpleResp(302, NULL, (char *) redirdest.c_str(), 0, 0, keepalive);
632
633 bool ret_keepalive = keepalive; // reset() clears keepalive
634 reset();
635 return ret_keepalive;
636};
637
638
639void XrdHttpReq::appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow) {
640
641 int l = 0;
642 char * p = 0;
643 if (opaque)
644 p = opaque->Env(l);
645
646 if (hdr2cgistr.empty() && (l < 2) && !hash) return;
647
648 // this works in most cases, except if the url already contains the xrdhttp tokens
649 s = s + "?";
650 if (!hdr2cgistr.empty()) {
651 s += encode_opaque(hdr2cgistr).c_str();
652 }
653 if (p && (l > 1)) {
654 if (!hdr2cgistr.empty()) {
655 s = s + "&";
656 }
657 s = s + encode_opaque(p + 1).c_str();
658 }
659
660 if (hash) {
661 if (l > 1) s += "&";
662 s += "xrdhttptk=";
663 s += hash;
664
665 s += "&xrdhttptime=";
666 char buf[256];
667 sprintf(buf, "%lld", (long long) tnow);
668 s += buf;
669
670 if (secent) {
671 if (secent->name) {
672 s += "&xrdhttpname=";
673 s += encode_str(secent->name).c_str();
674 }
675 }
676
677 if (secent->vorg) {
678 s += "&xrdhttpvorg=";
679 s += encode_str(secent->vorg).c_str();
680 }
681
682 if (secent->host) {
683 s += "&xrdhttphost=";
684 s += encode_str(secent->host).c_str();
685 }
686
687 if (secent->moninfo) {
688 s += "&xrdhttpdn=";
689 s += encode_str(secent->moninfo).c_str();
690 }
691
692 if (secent->role) {
693 s += "&xrdhttprole=";
694 s += encode_str(secent->role).c_str();
695 }
696
697 if (secent->grps) {
698 s += "&xrdhttpgrps=";
699 s += encode_str(secent->grps).c_str();
700 }
701
702 if (secent->endorsements) {
703 s += "&xrdhttpendorsements=";
704 s += encode_str(secent->endorsements).c_str();
705 }
706
707 if (secent->credslen) {
708 s += "&xrdhttpcredslen=";
709 char buf[16];
710 sprintf(buf, "%d", secent->credslen);
711 s += encode_str(buf).c_str();
712 }
713
714 if (secent->credslen) {
715 if (secent->creds) {
716 s += "&xrdhttpcreds=";
717 // Apparently this string might be not 0-terminated (!)
718 char *zerocreds = strndup(secent->creds, secent->credslen);
719 if (zerocreds) {
720 s += encode_str(zerocreds).c_str();
721 free(zerocreds);
722 }
723 }
724 }
725 }
726 }
727
728// Sanitize the resource from the http[s]://[host]/ questionable prefix
729// https://github.com/xrootd/xrootd/issues/1675
730void XrdHttpReq::sanitizeResourcePfx() {
731
732 if (resource.beginswith("https://")) {
733 // Find the slash that follows the hostname, and keep it
734 int p = resource.find('/', 8);
736 return;
737 }
738
739 if (resource.beginswith("http://")) {
740 // Find the slash that follows the hostname, and keep it
741 int p = resource.find('/', 7);
742 resource.erasefromstart(p);
743 return;
744 }
745}
746
747void XrdHttpReq::addCgi(const std::string &key, const std::string &value) {
748 if (hdr2cgistr.length() > 0) {
749 hdr2cgistr.append("&");
750 }
751 hdr2cgistr.append(key);
752 hdr2cgistr.append("=");
753 hdr2cgistr.append(value);
754}
755
756
757// Parse a resource line:
758// - sanitize
759// - extracts the opaque info from the given url
760// - sanitize the resource from http[s]://[host]/ questionable prefix
761void XrdHttpReq::parseResource(char *res) {
762
763
764
765
766 // Look for the first '?'
767 char *p = strchr(res, '?');
768
769 // Not found, then it's just a filename
770 if (!p) {
771 resource.assign(res, 0);
772
773 // Some poor client implementations may inject a http[s]://[host]/ prefix
774 // to the resource string. Here we choose to ignore it as a protection measure
775 sanitizeResourcePfx();
776
777 std::string resourceDecoded = decode_str(resource.c_str());
778 resource = resourceDecoded.c_str();
779 resourceplusopaque = resourceDecoded.c_str();
780
781
782 // Sanitize the resource string, removing double slashes
783 int pos = 0;
784 do {
785 pos = resource.find("//", pos);
786 if (pos != STR_NPOS)
787 resource.erase(pos, 1);
788 } while (pos != STR_NPOS);
789
790 return;
791 }
792
793 // Whatever comes before '?' is a filename
794
795 int cnt = p - res; // Number of chars to copy
796 resource.assign(res, 0, cnt - 1);
797
798 // Some poor client implementations may inject a http[s]://[host]/ prefix
799 // to the resource string. Here we choose to ignore it as a protection measure
800 sanitizeResourcePfx();
801
802 resource = decode_str(resource.c_str()).c_str();
803
804 // Sanitize the resource string, removing double slashes
805 int pos = 0;
806 do {
807 pos = resource.find("//", pos);
808 if (pos != STR_NPOS)
809 resource.erase(pos, 1);
810 } while (pos != STR_NPOS);
811
813 // Whatever comes after is opaque data to be parsed
814 if (strlen(p) > 1) {
815 std::string decoded = decode_str(p + 1);
816 opaque = new XrdOucEnv(decoded.c_str());
817 resourceplusopaque.append('?');
818 resourceplusopaque.append(p + 1);
819 }
820}
821
822void XrdHttpReq::generateWebdavErrMsg() {
823
824 // This block is only used when sending an "X-Transfer-Status" trailer response.
825 // We set the body to "OK" so that the trailer becomes "X-Transfer-Status: 200 OK",
826 // indicating a successful transfer.
827 if (xrdresp == kXR_ok) {
828 httpStatusCode = 200;
829 httpErrorBody = "OK";
830 return;
831 }
832
833 // default error
834 httpStatusCode = mapXrdErrToHttp(xrderrcode);
835 httpErrorBody = etext + "\n";
836
837}
838
840
841 kXR_int32 l;
842
843 // State variable for tracking the query parameter search
844 // - 0: Indicates we've not yet searched the URL for '?'
845 // - 1: Indicates we have a '?' and hence query parameters
846 // - 2: Indicates we do *not* have '?' present -- no query parameters
847 int query_param_status = 0;
848 if (!m_appended_asize) {
849 m_appended_asize = true;
850 if (request == rtPUT && length) {
851 if (query_param_status == 0) {
852 query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
853 }
854 resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
855 query_param_status = 1;
856 auto length_str = std::to_string(length);
857 resourceplusopaque.append("oss.asize=");
858 resourceplusopaque.append(length_str.c_str());
859 if (!opaque) {
860 opaque = new XrdOucEnv();
861 }
862 opaque->Put("oss.asize", length_str.c_str());
863 }
864 }
865
867 if (!m_appended_hdr2cgistr && !hdr2cgistr.empty()) {
868 if (query_param_status == 0) {
869 query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
870 }
871 resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
872
873 std::string hdr2cgistrEncoded = encode_opaque(hdr2cgistr);
874 resourceplusopaque.append(hdr2cgistrEncoded.c_str());
875 if (TRACING(TRACE_DEBUG)) {
876 // The obfuscation of "authz" will only be done if the server http.header2cgi config contains something that maps a header to this "authz" cgi.
877 // Unfortunately the obfuscation code will be called no matter what is configured in http.header2cgi.
878 std::string header2cgistrObf = obfuscateAuth(hdr2cgistr);
879
880 TRACEI(DEBUG, "Appended header fields to opaque info: '"
881 << header2cgistrObf.c_str() << "'");
882
883 }
884
886 }
887
888 // Verify if we have an external handler for this request
889 if (reqstate == 0) {
890 XrdHttpExtHandler *exthandler = prot->FindMatchingExtHandler(*this);
891 if (exthandler) {
892 XrdHttpExtReq xreq(this, prot);
893 int r = exthandler->ProcessReq(xreq);
894 reset();
895 if (!r) return 1; // All went fine, response sent
896 if (r < 0) return -1; // There was a hard error... close the connection
897
898 return 1; // There was an error and a response was sent
899 }
900 }
901
902 //
903 // Here we process the request locally
904 //
905
906 switch (request) {
910 generateWebdavErrMsg();
911 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
912 reset();
913 return -1;
914 }
916 {
917 if (reqstate == 0) {
918 // Always start with Stat; in the case of a checksum request, we'll have a follow-up query
919 if (prot->doStat((char *) resourceplusopaque.c_str())) {
920 prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
921 return -1;
922 }
923 return 0;
924 } else {
925 const char *opaque = strchr(resourceplusopaque.c_str(), '?');
926 // Note that doChksum requires that the memory stays alive until the callback is invoked.
928
929 m_req_cksum = prot->cksumHandler.getChecksumToRun(m_req_digest);
930 if(!m_req_cksum) {
931 // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
932 // We should not send body in response to HEAD request
933 prot->SendSimpleResp(HTTP_METHOD_NOT_ALLOWED, NULL, NULL, NULL, 0, false);
934 return -1;
935 }
936 if (!opaque) {
937 m_resource_with_digest += "?cks.type=";
938 m_resource_with_digest += m_req_cksum->getXRootDConfigDigestName().c_str();
939 } else {
940 m_resource_with_digest += "&cks.type=";
941 m_resource_with_digest += m_req_cksum->getXRootDConfigDigestName().c_str();
942 }
943 if (prot->doChksum(m_resource_with_digest) < 0) {
944 // In this case, the Want-Digest header was set and PostProcess gave the go-ahead to do a checksum.
945 prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to create initial checksum request.", 0, false);
946 return -1;
947 }
948 return 1;
949 }
950 }
952 {
953 int retval = keepalive ? 1 : -1; // reset() clears keepalive
954
955 if (resource.beginswith("/static/")) {
956
957 // This is a request for a /static resource
958 // If we have to use the embedded ones then we return the ones in memory as constants
959
960 // The sysadmin can always redirect the request to another host that
961 // contains his static resources
962
963 // We also allow xrootd to preread from the local disk all the files
964 // that have to be served as static resources.
965
966 if (prot->embeddedstatic) {
967
968 // Default case: the icon and the css of the HTML rendering of XrdHttp
969 if (resource == "/static/css/xrdhttp.css") {
970 prot->SendSimpleResp(200, NULL, NULL, (char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len, keepalive);
971 reset();
972 return retval;
973 }
974 if (resource == "/static/icons/xrdhttp.ico") {
975 prot->SendSimpleResp(200, NULL, NULL, (char *) favicon_ico, favicon_ico_len, keepalive);
976 reset();
977 return retval;
978 }
979
980 }
981
982 // If we are here then none of the embedded resources match (or they are disabled)
983 // We may have to redirect to a host that is supposed to serve the static resources
984 if (prot->staticredir) {
985
986 XrdOucString s = "Location: ";
987 s.append(prot->staticredir);
988
989 if (s.endswith('/'))
990 s.erasefromend(1);
991
992 s.append(resource);
993 appendOpaque(s, 0, 0, 0);
994
995 prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
996 return -1;
997
998
999 } else {
1000
1001 // We lookup the requested path in a hash containing the preread files
1002 if (prot->staticpreload) {
1003 XrdHttpProtocol::StaticPreloadInfo *mydata = prot->staticpreload->Find(resource.c_str());
1004 if (mydata) {
1005 prot->SendSimpleResp(200, NULL, NULL, (char *) mydata->data, mydata->len, keepalive);
1006 reset();
1007 return retval;
1008 }
1009 }
1010
1011 }
1012
1013
1014 }
1015
1016 // The reqstate parameter basically moves us through a simple state machine.
1017 // To optimize things, we start off by opening the file; if it turns out to be a directory, then
1018 // we close the file handle and switch to doing a HTML-based rendering of the directory. This
1019 // avoids needing to always to do "stat" first to determine the next step (since the file-open also
1020 // does a "stat").
1021 // - 0: Perform an open on the resource
1022 // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
1023 // - 2: Perform a close (for dirlist only)
1024 // - 3: Perform a dirlist.
1025 // - 4+: Reads from file; if at end, perform a close.
1026 switch (reqstate) {
1027 case 0: // Open the path for reading.
1028 {
1029 memset(&xrdreq, 0, sizeof (ClientRequest));
1030 xrdreq.open.requestid = htons(kXR_open);
1031 l = resourceplusopaque.length() + 1;
1032 xrdreq.open.dlen = htonl(l);
1033 xrdreq.open.mode = 0;
1034 xrdreq.open.options = htons(kXR_retstat | kXR_open_read | ((readRangeHandler.getMaxRanges() <= 1) ? kXR_seqio : 0));
1035
1036 if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1037 prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1038 return -1;
1039 }
1040
1041 // Prepare to chunk up the request
1042 writtenbytes = 0;
1043
1044 // We want to be invoked again after this request is finished
1045 return 0;
1046 }
1047 case 1: // Checksum request
1048 if (!(fileflags & kXR_isDir) && !m_req_digest.empty()) {
1049 // In this case, the Want-Digest header was set.
1050 bool has_opaque = strchr(resourceplusopaque.c_str(), '?');
1051 // Note that doChksum requires that the memory stays alive until the callback is invoked.
1052 m_req_cksum = prot->cksumHandler.getChecksumToRun(m_req_digest);
1053 if(!m_req_cksum) {
1054 // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1055 prot->SendSimpleResp(HTTP_METHOD_NOT_ALLOWED, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1056 return -1;
1057 }
1059 if (has_opaque) {
1060 m_resource_with_digest += "&cks.type=";
1061 m_resource_with_digest += m_req_cksum->getXRootDConfigDigestName().c_str();
1062 } else {
1063 m_resource_with_digest += "?cks.type=";
1064 m_resource_with_digest += m_req_cksum->getXRootDConfigDigestName().c_str();
1065 }
1066 if (prot->doChksum(m_resource_with_digest) < 0) {
1067 prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to start internal checksum request to satisfy Want-Digest header.", 0, false);
1068 return -1;
1069 }
1070 return 0;
1071 } else {
1072 TRACEI(DEBUG, "No checksum requested; skipping to request state 2");
1073 reqstate += 1;
1074 }
1075 // fallthrough
1076 case 2: // Close file handle for directory
1077 if ((fileflags & kXR_isDir) && fopened) {
1078 memset(&xrdreq, 0, sizeof (ClientRequest));
1079 xrdreq.close.requestid = htons(kXR_close);
1080 memcpy(xrdreq.close.fhandle, fhandle, 4);
1081
1082 if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1083 generateWebdavErrMsg();
1084 return sendFooterError("Could not run close request on the bridge");
1085 }
1086 return 0;
1087 } else {
1088 reqstate += 1;
1089 }
1090 // fallthrough
1091 case 3: // List directory
1092 if (fileflags & kXR_isDir) {
1093 if (prot->listdeny) {
1094 prot->SendSimpleResp(503, NULL, NULL, (char *) "Listings are disabled.", 0, false);
1095 return -1;
1096 }
1097
1098 if (prot->listredir) {
1099 XrdOucString s = "Location: ";
1100 s.append(prot->listredir);
1101
1102 if (s.endswith('/'))
1103 s.erasefromend(1);
1104
1105 s.append(resource);
1106 appendOpaque(s, 0, 0, 0);
1107
1108 prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1109 return -1;
1110 }
1111
1112 std::string res;
1113 res = resourceplusopaque.c_str();
1114
1115 // --------- DIRLIST
1116 memset(&xrdreq, 0, sizeof (ClientRequest));
1117 xrdreq.dirlist.requestid = htons(kXR_dirlist);
1118 xrdreq.dirlist.options[0] = kXR_dstat;
1119 l = res.length() + 1;
1120 xrdreq.dirlist.dlen = htonl(l);
1121
1122 if (!prot->Bridge->Run((char *) &xrdreq, (char *) res.c_str(), l)) {
1123 generateWebdavErrMsg();
1124 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1125 sendFooterError("Could not run listing request on the bridge");
1126 return -1;
1127 }
1128
1129 // We don't want to be invoked again after this request is finished
1130 return 1;
1131 }
1132 else {
1133 reqstate += 1;
1134 }
1135 // fallthrough
1136 case 4:
1137 {
1138 auto retval = ReturnGetHeaders();
1139 if (retval) {
1140 return retval;
1141 }
1142 }
1143 // fallthrough
1144 default: // Read() or Close(); reqstate is 4+
1145 {
1146 const XrdHttpIOList &readChunkList = readRangeHandler.NextReadList();
1147
1148 // Close() if we have finished, otherwise read the next chunk
1149
1150 // --------- CLOSE
1151 if ( closeAfterError || readChunkList.empty() )
1152 {
1153
1154 memset(&xrdreq, 0, sizeof (ClientRequest));
1155 xrdreq.close.requestid = htons(kXR_close);
1156 memcpy(xrdreq.close.fhandle, fhandle, 4);
1157
1158 if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1159 TRACEI(REQ, " Failed to run close request on the bridge.");
1160 // Note: we have already completed the request and sent the data to the client.
1161 // Hence, there's no need to send an error. However, since the bridge is potentially
1162 // in a bad state, we close the TCP socket to force the client to reconnect.
1163 return -1;
1164 }
1165
1166 // We have finished
1167 readClosing = true;
1168 return 1;
1169
1170 }
1171 // --------- READ or READV
1172
1173 if ( readChunkList.size() == 1 ) {
1174 // Use a read request for single range
1175
1176 long l;
1177 long long offs;
1178
1179 // --------- READ
1180 memset(&xrdreq, 0, sizeof (xrdreq));
1181 xrdreq.read.requestid = htons(kXR_read);
1182 memcpy(xrdreq.read.fhandle, fhandle, 4);
1183 xrdreq.read.dlen = 0;
1184
1185 offs = readChunkList[0].offset;
1186 l = readChunkList[0].size;
1187
1188 xrdreq.read.offset = htonll(offs);
1189 xrdreq.read.rlen = htonl(l);
1190
1191 // If we are using HTTPS or if the client requested trailers, or if the
1192 // read concerns a multirange reponse, disable sendfile
1193 // (in the latter two cases, the extra framing is only done in PostProcessHTTPReq)
1194 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1195 !readRangeHandler.isSingleRange()) {
1196 if (!prot->Bridge->setSF((kXR_char *) fhandle, false)) {
1197 TRACE(REQ, " XrdBridge::SetSF(false) failed.");
1198
1199 }
1200 }
1201
1202
1203
1204 if (l <= 0) {
1205 if (l < 0) {
1206 TRACE(ALL, " Data sizes mismatch.");
1207 return -1;
1208 }
1209 else {
1210 TRACE(ALL, " No more bytes to send.");
1211 reset();
1212 return 1;
1213 }
1214 }
1215
1216 if ((offs >= filesize) || (offs+l > filesize)) {
1217 httpStatusCode = 416;
1218 httpErrorBody = "Range Not Satisfiable";
1219 std::stringstream ss;
1220 ss << "Requested range " << l << "@" << offs << " is past the end of file (" << filesize << ")";
1221 return sendFooterError(ss.str());
1222 }
1223
1224 if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1225 generateWebdavErrMsg();
1226 return sendFooterError("Could not run read request on the bridge");
1227 }
1228 } else {
1229 // --------- READV
1230
1231 length = ReqReadV(readChunkList);
1232
1233 if (!prot->Bridge->Run((char *) &xrdreq, (char *) &ralist[0], length)) {
1234 generateWebdavErrMsg();
1235 return sendFooterError("Could not run ReadV request on the bridge");
1236 }
1237
1238 }
1239
1240 // We want to be invoked again after this request is finished
1241 return 0;
1242 } // case 3+
1243
1244 } // switch (reqstate)
1245
1246
1247 } // case XrdHttpReq::rtGET
1248
1249 case XrdHttpReq::rtPUT:
1250 {
1251 //if (prot->ishttps) {
1252 //prot->SendSimpleResp(501, NULL, NULL, (char *) "HTTPS not supported yet for direct writing. Sorry.", 0);
1253 //return -1;
1254 //}
1255
1256 if (!fopened) {
1257
1258 // --------- OPEN for write!
1259 memset(&xrdreq, 0, sizeof (ClientRequest));
1260 xrdreq.open.requestid = htons(kXR_open);
1261 l = resourceplusopaque.length() + 1;
1262 xrdreq.open.dlen = htonl(l);
1263 xrdreq.open.mode = htons(kXR_ur | kXR_uw | kXR_gw | kXR_gr | kXR_or);
1264 if (! XrdHttpProtocol::usingEC)
1265 xrdreq.open.options = htons(kXR_mkpath | kXR_open_wrto | kXR_delete);
1266 else
1267 xrdreq.open.options = htons(kXR_mkpath | kXR_open_wrto | kXR_new);
1268
1269 if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1270 prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, keepalive);
1271 return -1;
1272 }
1273
1274
1275 // We want to be invoked again after this request is finished
1276 // Only if there is data to fetch from the socket or there will
1277 // never be more data
1278 if (prot->BuffUsed() > 0 || (length == 0 && !sendcontinue))
1279 return 0;
1280
1281 return 1;
1282
1283 } else {
1284
1285 if (m_transfer_encoding_chunked) {
1286 if (m_current_chunk_size == m_current_chunk_offset) {
1287 // Chunk has been consumed; we now must process the CRLF.
1288 // Note that we don't support trailer headers.
1289 if (prot->BuffUsed() < 2) return 1;
1290 if (prot->myBuffStart[0] != '\r' || prot->myBuffStart[1] != '\n') {
1291 prot->SendSimpleResp(400, NULL, NULL, (char *) "Invalid trailing chunk encoding.", 0, keepalive);
1292 return -1;
1293 }
1294 prot->BuffConsume(2);
1295 if (m_current_chunk_size == 0) {
1296 // All data has been sent. Turn off chunk processing and
1297 // set the bytes written and length appropriately; on next callback,
1298 // we will hit the close() block below.
1299 m_transfer_encoding_chunked = false;
1301 return ProcessHTTPReq();
1302 }
1303 m_current_chunk_size = -1;
1304 m_current_chunk_offset = 0;
1305 // If there is more data, we try to process the next chunk; otherwise, return
1306 if (!prot->BuffUsed()) return 1;
1307 }
1308 if (-1 == m_current_chunk_size) {
1309
1310 // Parse out the next chunk size.
1311 long long idx = 0;
1312 bool found_newline = false;
1313 // Set a maximum size of chunk we will allow
1314 // Nginx sets this to "NGX_MAX_OFF_T_VALUE", which is 9223372036854775807 (a some crazy number)
1315 // We set it to 1TB, which is 1099511627776
1316 // This is to prevent a malicious client from sending a very large chunk size
1317 // or a malformed chunk request.
1318 // 1TB in base-16 is 0x40000000000, so only allow 11 characters, plus the CRLF
1319 long long max_chunk_size_chars = std::min(static_cast<long long>(prot->BuffUsed()), static_cast<long long>(13));
1320 for (; idx < max_chunk_size_chars; idx++) {
1321 if (prot->myBuffStart[idx] == '\n') {
1322 found_newline = true;
1323 break;
1324 }
1325 }
1326 // If we found a new line, but it is the first character in the buffer (no chunk length)
1327 // or if the previous character is not a CR.
1328 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] != '\r')) {
1329 prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1330 TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1331 return -1;
1332 }
1333 if (found_newline) {
1334 char *endptr = NULL;
1335 std::string line_contents(prot->myBuffStart, idx);
1336 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1337 // Chunk sizes can be followed by trailer information or CRLF
1338 if (*endptr != ';' && *endptr != '\r') {
1339 prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1340 TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1341 return -1;
1342 }
1343 m_current_chunk_size = chunk_contents;
1344 m_current_chunk_offset = 0;
1345 prot->BuffConsume(idx + 1);
1346 TRACE(REQ, "XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size << " bytes");
1347 } else {
1348 // Need more data!
1349 return 1;
1350 }
1351 }
1352
1353 if (m_current_chunk_size == 0) {
1354 // All data has been sent. Invoke this routine again immediately to process CRLF
1355 return ProcessHTTPReq();
1356 } else {
1357 // At this point, we have a chunk size defined and should consume payload data
1358 memset(&xrdreq, 0, sizeof (xrdreq));
1359 xrdreq.write.requestid = htons(kXR_write);
1360 memcpy(xrdreq.write.fhandle, fhandle, 4);
1361
1362 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1363 long long bytes_to_write = std::min(static_cast<long long>(prot->BuffUsed()),
1364 chunk_bytes_remaining);
1365
1366 xrdreq.write.offset = htonll(writtenbytes);
1367 xrdreq.write.dlen = htonl(bytes_to_write);
1368
1369 TRACEI(REQ, "XrdHTTP PUT: Writing chunk of size " << bytes_to_write << " starting with '" << *(prot->myBuffStart) << "'" << " with " << chunk_bytes_remaining << " bytes remaining in the chunk");
1370 if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_write)) {
1371 generateWebdavErrMsg();
1372 return sendFooterError("Could not run write request on the bridge");
1373 }
1374 // If there are more bytes in the buffer, then immediately call us after the
1375 // write is finished; otherwise, wait for data.
1376 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1377 }
1378 } else if (writtenbytes < length) {
1379
1380
1381 // --------- WRITE
1382 memset(&xrdreq, 0, sizeof (xrdreq));
1383 xrdreq.write.requestid = htons(kXR_write);
1384 memcpy(xrdreq.write.fhandle, fhandle, 4);
1385
1386 long long bytes_to_read = std::min(static_cast<long long>(prot->BuffUsed()),
1388
1389 xrdreq.write.offset = htonll(writtenbytes);
1390 xrdreq.write.dlen = htonl(bytes_to_read);
1391
1392 TRACEI(REQ, "Writing " << bytes_to_read);
1393 if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_read)) {
1394 generateWebdavErrMsg();
1395 return sendFooterError("Could not run write request on the bridge");
1396 }
1397
1398 if (writtenbytes + prot->BuffUsed() >= length)
1399 // Trigger an immediate recall after this request has finished
1400 return 0;
1401 else
1402 // We want to be invoked again after this request is finished
1403 // only if there is pending data
1404 return 1;
1405
1406
1407
1408 } else {
1409
1410 // --------- CLOSE
1411 memset(&xrdreq, 0, sizeof (ClientRequest));
1412 xrdreq.close.requestid = htons(kXR_close);
1413 memcpy(xrdreq.close.fhandle, fhandle, 4);
1414
1415
1416 if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1417 generateWebdavErrMsg();
1418 return sendFooterError("Could not run close request on the bridge");
1419 }
1420
1421 // We have finished
1422 return 1;
1423
1424 }
1425
1426 }
1427
1428 break;
1429
1430 }
1432 {
1433 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);
1434 bool ret_keepalive = keepalive; // reset() clears keepalive
1435 reset();
1436 return ret_keepalive ? 1 : -1;
1437 }
1439 {
1440
1441
1442 switch (reqstate) {
1443
1444 case 0: // Stat()
1445 {
1446
1447
1448 // --------- STAT is always the first step
1449 memset(&xrdreq, 0, sizeof (ClientRequest));
1450 xrdreq.stat.requestid = htons(kXR_stat);
1451 std::string s = resourceplusopaque.c_str();
1452
1453
1454 l = resourceplusopaque.length() + 1;
1455 xrdreq.stat.dlen = htonl(l);
1456
1457 if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1458 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1459 return -1;
1460 }
1461
1462 // We need to be invoked again to complete the request
1463 return 0;
1464 }
1465 default:
1466
1467 if (fileflags & kXR_isDir) {
1468 // --------- RMDIR
1469 memset(&xrdreq, 0, sizeof (ClientRequest));
1470 xrdreq.rmdir.requestid = htons(kXR_rmdir);
1471
1472 std::string s = resourceplusopaque.c_str();
1473
1474 l = s.length() + 1;
1475 xrdreq.rmdir.dlen = htonl(l);
1476
1477 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1478 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rmdir request.", 0, false);
1479 return -1;
1480 }
1481 } else {
1482 // --------- DELETE
1483 memset(&xrdreq, 0, sizeof (ClientRequest));
1484 xrdreq.rm.requestid = htons(kXR_rm);
1485
1486 std::string s = resourceplusopaque.c_str();
1487
1488 l = s.length() + 1;
1489 xrdreq.rm.dlen = htonl(l);
1490
1491 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1492 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rm request.", 0, false);
1493 return -1;
1494 }
1495 }
1496
1497
1498 // We don't want to be invoked again after this request is finished
1499 return 1;
1500
1501 }
1502
1503
1504
1505 }
1507 {
1508 prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported yet.", 0, false);
1509
1510 return -1;
1511 }
1513 {
1514
1515
1516
1517 switch (reqstate) {
1518
1519 case 0: // Stat() and add the current item to the list of the things to send
1520 {
1521
1522 if (length > 0) {
1523 TRACE(REQ, "Reading request body " << length << " bytes.");
1524 char *p = 0;
1525 // We have to specifically read all the request body
1526
1527 if (prot->BuffgetData(length, &p, true) < length) {
1528 prot->SendSimpleResp(501, NULL, NULL, (char *) "Error in getting the PROPFIND request body.", 0, false);
1529 return -1;
1530 }
1531
1532 if ((depth > 1) || (depth < 0)) {
1533 prot->SendSimpleResp(501, NULL, NULL, (char *) "Invalid depth value.", 0, false);
1534 return -1;
1535 }
1536
1537
1538 parseBody(p, length);
1539 }
1540
1541
1542 // --------- STAT is always the first step
1543 memset(&xrdreq, 0, sizeof (ClientRequest));
1544 xrdreq.stat.requestid = htons(kXR_stat);
1545 std::string s = resourceplusopaque.c_str();
1546
1547
1548 l = resourceplusopaque.length() + 1;
1549 xrdreq.stat.dlen = htonl(l);
1550
1551 if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1552 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1553 return -1;
1554 }
1555
1556
1557 if (depth == 0) {
1558 // We don't need to be invoked again
1559 return 1;
1560 } else
1561 // We need to be invoked again to complete the request
1562 return 0;
1563
1564
1565
1566 break;
1567 }
1568
1569 default: // Dirlist()
1570 {
1571
1572 // --------- DIRLIST
1573 memset(&xrdreq, 0, sizeof (ClientRequest));
1574 xrdreq.dirlist.requestid = htons(kXR_dirlist);
1575
1576 std::string s = resourceplusopaque.c_str();
1577 xrdreq.dirlist.options[0] = kXR_dstat;
1578 //s += "?xrd.dirstat=1";
1579
1580 l = s.length() + 1;
1581 xrdreq.dirlist.dlen = htonl(l);
1582
1583 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1584 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1585 return -1;
1586 }
1587
1588 // We don't want to be invoked again after this request is finished
1589 return 1;
1590 }
1591 }
1592
1593
1594 break;
1595 }
1597 {
1598
1599 // --------- MKDIR
1600 memset(&xrdreq, 0, sizeof (ClientRequest));
1601 xrdreq.mkdir.requestid = htons(kXR_mkdir);
1602
1603 std::string s = resourceplusopaque.c_str();
1604 xrdreq.mkdir.options[0] = (kXR_char) kXR_mkdirpath;
1605
1606 l = s.length() + 1;
1607 xrdreq.mkdir.dlen = htonl(l);
1608
1609 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1610 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1611 return -1;
1612 }
1613
1614 // We don't want to be invoked again after this request is finished
1615 return 1;
1616 }
1617 case XrdHttpReq::rtMOVE:
1618 {
1619 // Incase of a move cgi parameters present in the CGI str
1620 // are appended to the destination in case of a MOVE.
1621 if (resourceplusopaque != "") {
1622 int pos = resourceplusopaque.find("?");
1623 if (pos != STR_NPOS) {
1624 destination.append((destination.find("?") == std::string::npos) ? "?" : "&");
1625 destination.append(resourceplusopaque.c_str() + pos + 1);
1626 }
1627 }
1628
1629 // --------- MOVE
1630 memset(&xrdreq, 0, sizeof (ClientRequest));
1631 xrdreq.mv.requestid = htons(kXR_mv);
1632
1633 std::string s = resourceplusopaque.c_str();
1634 s += " ";
1635
1636 char buf[256];
1637 char *ppath;
1638 int port = 0;
1639 if (parseURL((char *) destination.c_str(), buf, port, &ppath)) {
1640 prot->SendSimpleResp(501, NULL, NULL, (char *) "Cannot parse destination url.", 0, false);
1641 return -1;
1642 }
1643
1644 char buf2[256];
1645 strcpy(buf2, host.c_str());
1646 char *pos = strchr(buf2, ':');
1647 if (pos) *pos = '\0';
1648
1649 // If we are a redirector we enforce that the host field is equal to
1650 // whatever was written in the destination url
1651 //
1652 // If we are a data server instead we cannot enforce anything, we will
1653 // just ignore the host part of the destination
1654 if ((prot->myRole == kXR_isManager) && strcmp(buf, buf2)) {
1655 prot->SendSimpleResp(501, NULL, NULL, (char *) "Only in-place renaming is supported for MOVE.", 0, false);
1656 return -1;
1657 }
1658
1659
1660
1661
1662 s += ppath;
1663
1664 l = s.length() + 1;
1665 xrdreq.mv.dlen = htonl(l);
1666 xrdreq.mv.arg1len = htons(resourceplusopaque.length());
1667
1668 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1669 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1670 return -1;
1671 }
1672
1673 // We don't want to be invoked again after this request is finished
1674 return 1;
1675
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 // The correct response code is 201 (Created) but Pelican clients before 7.12.2 do not treat 201 as success, only 200.
2315 prot->SendSimpleResp(200, NULL, NULL, (char *) ":-)", 0, keepalive);
2316 return keepalive ? 1 : -1;
2317 } else {
2318 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2319 return -1;
2320 }
2321 }
2322 }
2323
2324
2325
2326
2327
2328 break;
2329 }
2330
2331
2332
2334 {
2335
2336 if (xrdresp != kXR_ok) {
2337 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2338 httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2339 return -1;
2340 }
2341
2342
2343
2344
2345 switch (reqstate) {
2346
2347 case 0: // response to stat()
2348 {
2349 if (iovN > 0) {
2350
2351 // Now parse the stat info
2352 TRACEI(REQ, "Stat for removal " << resource.c_str()
2353 << " stat=" << (char *) iovP[0].iov_base);
2354
2355 long dummyl;
2356 sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2357 &dummyl,
2358 &filesize,
2359 &fileflags,
2360 &filemodtime);
2361 }
2362
2363 return 0;
2364 }
2365 default: // response to rm
2366 {
2367 if (xrdresp == kXR_ok) {
2368 prot->SendSimpleResp(200, NULL, NULL, (char *) ":-)", 0, keepalive);
2369 return keepalive ? 1 : -1;
2370 }
2371 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2372 httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2373 return -1;
2374 }
2375 }
2376
2377
2378 }
2379
2381 {
2382
2383 if (xrdresp == kXR_error) {
2384 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2385 httpErrorBody.c_str(), httpErrorBody.length(), false);
2386 return -1;
2387 }
2388
2389 switch (reqstate) {
2390
2391 case 0: // response to stat()
2392 {
2393 DirListInfo e;
2394 e.size = 0;
2395 e.flags = 0;
2396
2397 // Now parse the answer building the entries vector
2398 if (iovN > 0) {
2399 e.path = resource.c_str();
2400
2401 // Now parse the stat info
2402 TRACEI(REQ, "Collection " << resource.c_str()
2403 << " stat=" << (char *) iovP[0].iov_base);
2404
2405 long dummyl;
2406 sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2407 &dummyl,
2408 &e.size,
2409 &e.flags,
2410 &e.modtime);
2411
2412 if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2413 /* The entry is filled. */
2414
2415
2416 std::string p;
2417 stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2418
2419 char *estr = escapeXML(e.path.c_str());
2420
2421 stringresp += "<D:href>";
2422 stringresp += estr;
2423 stringresp += "</D:href>\n";
2424
2425 free(estr);
2426
2427 stringresp += "<D:propstat>\n<D:prop>\n";
2428
2429 // Now add the properties that we have to add
2430
2431 // File size
2432 stringresp += "<lp1:getcontentlength>";
2433 stringresp += itos(e.size);
2434 stringresp += "</lp1:getcontentlength>\n";
2435
2436
2437
2438 stringresp += "<lp1:getlastmodified>";
2440 stringresp += "</lp1:getlastmodified>\n";
2441
2442
2443
2444 if (e.flags & kXR_isDir) {
2445 stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2446 stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2447 } else {
2448 stringresp += "<lp1:resourcetype/>\n";
2449 stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2450 }
2451
2452 if (e.flags & kXR_xset) {
2453 stringresp += "<lp1:executable>T</lp1:executable>\n";
2454 stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2455 } else {
2456 stringresp += "<lp1:executable>F</lp1:executable>\n";
2457 }
2458
2459
2460
2461 stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2462
2463
2464 }
2465
2466
2467 }
2468
2469 // If this was the last bunch of entries, send the buffer and empty it immediately
2470 if ((depth == 0) || !(e.flags & kXR_isDir)) {
2471 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";
2472 stringresp.insert(0, s);
2473 stringresp += "</D:multistatus>\n";
2474 prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2475 (char *) stringresp.c_str(), stringresp.length(), keepalive);
2476 stringresp.clear();
2477 return keepalive ? 1 : -1;
2478 }
2479
2480 break;
2481 }
2482 default: // response to dirlist()
2483 {
2484
2485
2486 // Now parse the answer building the entries vector
2487 if (iovN > 0) {
2488 char *startp = (char *) iovP[0].iov_base, *endp = 0;
2489 char entry[1024];
2490 DirListInfo e;
2491
2492 while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)(iovP[0].iov_len - 1) ) {
2493 // Find the filename, it comes before the \n
2494 if ((endp = (char *) mystrchrnul((const char*) startp, '\n'))) {
2495 strncpy(entry, (char *) startp, endp - startp);
2496 entry[endp - startp] = 0;
2497 e.path = entry;
2498
2499 endp++;
2500
2501 // Now parse the stat info
2502 TRACEI(REQ, "Dirlist " <<resource.c_str() <<" entry=" <<entry
2503 << " stat=" << endp);
2504
2505 long dummyl;
2506 sscanf(endp, "%ld %lld %ld %ld",
2507 &dummyl,
2508 &e.size,
2509 &e.flags,
2510 &e.modtime);
2511 }
2512
2513
2514 if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2515 /* The entry is filled.
2516
2517 <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/" xmlns:lp3="LCGDM:">
2518 <D:href>/dpm/cern.ch/home/testers2.eu-emi.eu/</D:href>
2519 <D:propstat>
2520 <D:prop>
2521 <lp1:getcontentlength>1</lp1:getcontentlength>
2522 <lp1:getlastmodified>Tue, 01 May 2012 02:42:13 GMT</lp1:getlastmodified>
2523 <lp1:resourcetype>
2524 <D:collection/>
2525 </lp1:resourcetype>
2526 </D:prop>
2527 <D:status>HTTP/1.1 200 OK</D:status>
2528 </D:propstat>
2529 </D:response>
2530 */
2531
2532
2533 std::string p = resource.c_str();
2534 if (*p.rbegin() != '/') p += "/";
2535
2536 p += e.path;
2537
2538 stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2539
2540 char *estr = escapeXML(p.c_str());
2541 stringresp += "<D:href>";
2542 stringresp += estr;
2543 stringresp += "</D:href>\n";
2544 free(estr);
2545
2546 stringresp += "<D:propstat>\n<D:prop>\n";
2547
2548
2549
2550 // Now add the properties that we have to add
2551
2552 // File size
2553 stringresp += "<lp1:getcontentlength>";
2554 stringresp += itos(e.size);
2555 stringresp += "</lp1:getcontentlength>\n";
2556
2557 stringresp += "<lp1:getlastmodified>";
2559 stringresp += "</lp1:getlastmodified>\n";
2560
2561 if (e.flags & kXR_isDir) {
2562 stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2563 stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2564 } else {
2565 stringresp += "<lp1:resourcetype/>\n";
2566 stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2567 }
2568
2569 if (e.flags & kXR_xset) {
2570 stringresp += "<lp1:executable>T</lp1:executable>\n";
2571 stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2572 } else {
2573 stringresp += "<lp1:executable>F</lp1:executable>\n";
2574 }
2575
2576 stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2577
2578
2579 }
2580
2581
2582
2583 if (endp) {
2584 char *pp = (char *)strchr((const char *)endp, '\n');
2585 if (pp) startp = pp+1;
2586 else break;
2587 } else break;
2588
2589 }
2590 }
2591
2592
2593
2594 // If this was the last bunch of entries, send the buffer and empty it immediately
2595 if (final_) {
2596 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";
2597 stringresp.insert(0, s);
2598 stringresp += "</D:multistatus>\n";
2599 prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2600 (char *) stringresp.c_str(), stringresp.length(), keepalive);
2601 stringresp.clear();
2602 return keepalive ? 1 : -1;
2603 }
2604
2605 break;
2606 } // default reqstate
2607 } // switch reqstate
2608
2609
2610 break;
2611
2612 } // case propfind
2613
2615 {
2616
2617 if (xrdresp != kXR_ok) {
2618 if (xrderrcode == kXR_ItExists) {
2619 prot->SendSimpleResp(405, NULL, NULL, (char *) "Method is not allowed; resource already exists.", 0, false);
2620 } else {
2621 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2622 httpErrorBody.c_str(), httpErrorBody.length(), false);
2623 }
2624 return -1;
2625 }
2626
2627 prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2628 return keepalive ? 1 : -1;
2629
2630 }
2631 case XrdHttpReq::rtMOVE:
2632 {
2633
2634 if (xrdresp != kXR_ok) {
2635 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (char *) etext.c_str(), 0, false);
2636 return -1;
2637 }
2638
2639 prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2640 return keepalive ? 1 : -1;
2641
2642 }
2643
2644 default:
2645 break;
2646
2647 }
2648
2649
2650 switch (xrdresp) {
2651 case kXR_error:
2652 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2653 httpErrorBody.c_str(), httpErrorBody.length(), false);
2654 return -1;
2655 break;
2656
2657 default:
2658
2659 break;
2660 }
2661
2662
2663 return 0;
2664}
2665
2666int
2667XrdHttpReq::sendFooterError(const std::string &extra_text) {
2668 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2669 // A trailer header is appropriate in this case; this is signified by
2670 // a chunk with size zero, then the trailer, then a crlf.
2671 //
2672 // We only send the status trailer when explicitly requested; otherwise a
2673 // "normal" HTTP client might simply see a short response and think it's a
2674 // success
2675
2676 if (prot->ChunkRespHeader(0))
2677 return -1;
2678
2679 std::stringstream ss;
2680
2681 ss << httpStatusCode;
2682 if (!httpErrorBody.empty()) {
2683 std::string_view statusView(httpErrorBody);
2684 // Remove trailing newline; this is not valid in a trailer value
2685 // and causes incorrect framing of the response, confusing clients.
2686 if (statusView[statusView.size() - 1] == '\n') {
2687 ss << ": " << statusView.substr(0, statusView.size() - 1);
2688 } else {
2689 ss << ": " << httpErrorBody;
2690 }
2691 }
2692
2693 if (!extra_text.empty())
2694 ss << ": " << extra_text;
2695 TRACEI(REQ, ss.str());
2696 ss << "\r\n";
2697
2698 const auto header = "X-Transfer-Status: " + ss.str();
2699 if (prot->SendData(header.c_str(), header.size()))
2700 return -1;
2701
2702 if (prot->ChunkRespFooter())
2703 return -1;
2704
2705 return keepalive ? 1 : -1;
2706 } else {
2707 TRACEI(REQ, "Failure during response: " << httpStatusCode << ": " << httpErrorBody << (extra_text.empty() ? "" : (": " + extra_text)));
2708 return -1;
2709
2710 }
2711}
2712
2713void XrdHttpReq::addAgeHeader(std::string &headers) {
2714 long object_age = time(NULL) - filemodtime;
2715 headers += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2716}
2717
2719
2720 TRACE(REQ, " XrdHttpReq request ended.");
2721
2722 //if (xmlbody) xmlFreeDoc(xmlbody);
2723 readRangeHandler.reset();
2724 readClosing = false;
2725 closeAfterError = false;
2726 writtenbytes = 0;
2727 etext.clear();
2728 redirdest = "";
2729
2730 // // Here we should deallocate this
2731 // const struct iovec *iovP //!< pointer to data array
2732 // int iovN, //!< array count
2733 // int iovL, //!< byte count
2734 // bool final //!< true -> final result
2735
2736
2737 //xmlbody = 0;
2738 depth = 0;
2741 ralist.clear();
2742 ralist.shrink_to_fit();
2743
2744 request = rtUnset;
2745 resource = "";
2746 allheaders.clear();
2747
2748 // Reset the state of the request's digest request.
2749 m_req_digest.clear();
2750 m_digest_header.clear();
2751 m_req_cksum = nullptr;
2752
2754 m_user_agent = "";
2755 m_origin = "";
2756
2757 headerok = false;
2758 keepalive = true;
2759 length = 0;
2760 filesize = 0;
2761 depth = 0;
2762 sendcontinue = false;
2763
2764 m_transfer_encoding_chunked = false;
2765 m_current_chunk_size = -1;
2766 m_current_chunk_offset = 0;
2767
2768 m_trailer_headers = false;
2769 m_status_trailer = false;
2770
2772 reqstate = 0;
2773
2774 memset(&xrdreq, 0, sizeof (xrdreq));
2775 memset(&xrdresp, 0, sizeof (xrdresp));
2777
2778 etext.clear();
2779 redirdest = "";
2780
2781 stringresp = "";
2782
2783 host = "";
2784 destination = "";
2785 hdr2cgistr = "";
2786 m_appended_hdr2cgistr = false;
2787 m_appended_asize = false;
2788
2789 iovP = 0;
2790 iovN = 0;
2791 iovL = 0;
2792
2793
2794 if (opaque) delete(opaque);
2795 opaque = 0;
2796
2797 fopened = false;
2798
2799 final = false;
2800
2801 mScitag = -1;
2802
2803 httpStatusCode = -1;
2804 httpErrorCode = "";
2805 httpErrorBody = "";
2806
2807}
2808
2809void XrdHttpReq::getfhandle() {
2810
2811 memcpy(fhandle, iovP[0].iov_base, 4);
2812 TRACEI(REQ, "fhandle:" <<
2813 (int) fhandle[0] << ":" << (int) fhandle[1] << ":" << (int) fhandle[2] << ":" << (int) fhandle[3]);
2814
2815}
2816
2817void XrdHttpReq::getReadResponse(XrdHttpIOList &received) {
2818 received.clear();
2819
2820 if (ntohs(xrdreq.header.requestid) == kXR_readv) {
2821 readahead_list *l;
2822 char *p;
2823 kXR_int32 len;
2824
2825 // Cycle on all the data that is coming from the server
2826 for (int i = 0; i < iovN; i++) {
2827
2828 for (p = (char *) iovP[i].iov_base; p < (char *) iovP[i].iov_base + iovP[i].iov_len;) {
2829 l = (readahead_list *) p;
2830 len = ntohl(l->rlen);
2831
2832 received.emplace_back(p+sizeof(readahead_list), -1, len);
2833
2834 p += sizeof (readahead_list);
2835 p += len;
2836
2837 }
2838 }
2839 return;
2840 }
2841
2842 // kXR_read result
2843 for (int i = 0; i < iovN; i++) {
2844 received.emplace_back((char*)iovP[i].iov_base, -1, iovP[i].iov_len);
2845 }
2846
2847}
2848
2849int XrdHttpReq::sendReadResponsesMultiRanges(const XrdHttpIOList &received) {
2850
2851 if (received.size() == 0) {
2852 bool start, finish;
2853 if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2854 return -1;
2855 }
2856 return 0;
2857 }
2858
2859 // user is expecting multiple ranges, we must be prepared to send an
2860 // individual header for each and format it according to the http rules
2861
2862 struct rinfo {
2863 bool start;
2864 bool finish;
2865 const XrdOucIOVec2 *ci;
2866 const XrdHttpReadRangeHandler::UserRange *ur;
2867 std::string st_header;
2868 std::string fin_header;
2869 };
2870
2871 // report each received byte chunk to the range handler and record the details
2872 // of original user range it related to and if starts a range or finishes all.
2873 // also sum the total of the headers and data which need to be sent to the user,
2874 // in case we need it for chunked transfer encoding
2875 std::vector<rinfo> rvec;
2876 off_t sum_len = 0;
2877
2878 rvec.reserve(received.size());
2879
2880 for(const auto &rcv: received) {
2881 rinfo rentry;
2882 bool start, finish;
2883 const XrdHttpReadRangeHandler::UserRange *ur;
2884
2885 if (readRangeHandler.NotifyReadResult(rcv.size, &ur, start, finish) < 0) {
2886 return -1;
2887 }
2888 rentry.ur = ur;
2889 rentry.start = start;
2890 rentry.finish = finish;
2891 rentry.ci = &rcv;
2892
2893 if (start) {
2894 std::string s = buildPartialHdr(ur->start,
2895 ur->end,
2896 filesize,
2897 (char *) "123456");
2898
2899 rentry.st_header = s;
2900 sum_len += s.size();
2901 }
2902
2903 sum_len += rcv.size;
2904
2905 if (finish) {
2906 std::string s = buildPartialHdrEnd((char *) "123456");
2907 rentry.fin_header = s;
2908 sum_len += s.size();
2909 }
2910
2911 rvec.push_back(rentry);
2912 }
2913
2914
2915 // Send chunked encoding header
2916 if (m_transfer_encoding_chunked && m_trailer_headers) {
2917 prot->ChunkRespHeader(sum_len);
2918 }
2919
2920 // send the user the headers / data
2921 for(const auto &rentry: rvec) {
2922
2923 if (rentry.start) {
2924 TRACEI(REQ, "Sending multipart: " << rentry.ur->start << "-" << rentry.ur->end);
2925 if (prot->SendData((char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2926 return -1;
2927 }
2928 }
2929
2930 // Send all the data we have
2931 if (prot->SendData((char *) rentry.ci->data, rentry.ci->size)) {
2932 return -1;
2933 }
2934
2935 if (rentry.finish) {
2936 if (prot->SendData((char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2937 return -1;
2938 }
2939 }
2940 }
2941
2942 // Send chunked encoding footer
2943 if (m_transfer_encoding_chunked && m_trailer_headers) {
2944 prot->ChunkRespFooter();
2945 }
2946
2947 return 0;
2948}
2949
2950int XrdHttpReq::sendReadResponseSingleRange(const XrdHttpIOList &received) {
2951 // single range http transfer
2952
2953 if (received.size() == 0) {
2954 bool start, finish;
2955 if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2956 return -1;
2957 }
2958 return 0;
2959 }
2960
2961 off_t sum = 0;
2962 // notify the range handler and return if error
2963 for(const auto &rcv: received) {
2964 bool start, finish;
2965 if (readRangeHandler.NotifyReadResult(rcv.size, nullptr, start, finish) < 0) {
2966 return -1;
2967 }
2968 sum += rcv.size;
2969 }
2970
2971 // Send chunked encoding header
2972 if (m_transfer_encoding_chunked && m_trailer_headers) {
2973 prot->ChunkRespHeader(sum);
2974 }
2975 for(const auto &rcv: received) {
2976 if (prot->SendData((char *) rcv.data, rcv.size)) return -1;
2977 }
2978 if (m_transfer_encoding_chunked && m_trailer_headers) {
2979 prot->ChunkRespFooter();
2980 }
2981 return 0;
2982}
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.
int parseURL(char *url, char *host, int &port, char **path)
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)