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
822
823}
824
825void XrdHttpReq::sendWebdavErrorMessage(
826 XResponseType xrdresp, XErrorCode xrderrcode, XrdHttpReq::ReqType httpVerb,
827 XRequestTypes xrdOperation, std::string etext, const char *desc,
828 const char *header_to_add, bool keepalive) {
829 int code{0};
830 std::string errCode{"Unknown"};
831 std::string statusText;
832
833 switch (httpVerb) {
835 if (xrdOperation == kXR_open) {
837 code = 409;
838 errCode = "8.1";
839 } else if (xrderrcode == kXR_NoSpace) {
840 code = 507;
841 errCode = "8.3.1";
842 } else if (xrderrcode == kXR_overQuota) {
843 code = 507;
844 errCode = "8.3.2";
845 } else if (xrderrcode == kXR_NotAuthorized) {
846 code = 403;
847 errCode = "9.3";
848 }
849 } else if (xrdOperation == kXR_write) {
850 if (xrderrcode == kXR_NoSpace) {
851 code = 507;
852 errCode = "8.4.1";
853 } else if (xrderrcode == kXR_overQuota) {
854 code = 507;
855 errCode = "8.4.2";
856 }
857 }
858 break;
859 default:
860 break;
861 }
862
863 // Remove the if at the end of project completion
864 // Till then status text defaults to as set by mapXrdResponseToHttpStatus
865 if (code != 0) {
866 httpStatusCode = code;
867 httpErrorCode = errCode;
868 httpErrorBody = "ERROR: " + errCode + ": " + etext + "\n";
869
870 prot->SendSimpleResp(httpStatusCode, desc, header_to_add,
871 httpErrorBody.c_str(), httpErrorBody.length(),
872 keepalive);
873 }
874}
875
876// Map an XRootD error code to an appropriate HTTP status code and message
877// The variables httpStatusCode and httpErrorBody will be populated
878
879void XrdHttpReq::mapXrdErrorToHttpStatus() {
880 // Set default HTTP status values for an error case
881 httpStatusCode = 500;
882 httpErrorBody = "Unrecognized error";
883
884 // Do error mapping
885 if (xrdresp == kXR_error) {
886 switch (xrderrcode) {
887 case kXR_AuthFailed:
888 httpStatusCode = 401; httpErrorBody = "Unauthorized";
889 break;
891 httpStatusCode = 403; httpErrorBody = "Operation not permitted";
892 break;
893 case kXR_NotFound:
894 httpStatusCode = 404; httpErrorBody = "File not found";
895 break;
896 case kXR_Unsupported:
897 httpStatusCode = 405; httpErrorBody = "Operation not supported";
898 break;
899 case kXR_FileLocked:
900 httpStatusCode = 423; httpErrorBody = "Resource is a locked";
901 break;
902 case kXR_isDirectory:
903 httpStatusCode = 409; httpErrorBody = "Resource is a directory";
904 break;
905 case kXR_ItExists:
907 httpStatusCode = 409; httpErrorBody = "File already exists";
908 } else {
909 // In the case the XRootD layer returns a kXR_ItExists after a deletion
910 // was submitted, we return a 405 status code with the error message set by
911 // the XRootD layer
912 httpStatusCode = 405;
913 }
914 break;
916 httpStatusCode = 405; httpErrorBody = "Method is not allowed";
917 break;
918 case kXR_noserver:
919 httpStatusCode = 502; httpErrorBody = "Bad Gateway";
920 break;
921 case kXR_TimerExpired:
922 httpStatusCode = 504; httpErrorBody = "Gateway timeout";
923 break;
924 default:
925 break;
926 }
927
928 if (!etext.empty()) httpErrorBody = etext;
929
930 TRACEI(REQ, "PostProcessHTTPReq mapping Xrd error [" << xrderrcode
931 << "] to status code [" << httpStatusCode << "]");
932
933 httpErrorBody += "\n";
934 } else {
935 httpStatusCode = 200;
936 httpErrorBody = "OK";
937 }
938}
939
941
942 kXR_int32 l;
943
944 // State variable for tracking the query parameter search
945 // - 0: Indicates we've not yet searched the URL for '?'
946 // - 1: Indicates we have a '?' and hence query parameters
947 // - 2: Indicates we do *not* have '?' present -- no query parameters
948 int query_param_status = 0;
949 if (!m_appended_asize) {
950 m_appended_asize = true;
951 if (request == rtPUT && length) {
952 if (query_param_status == 0) {
953 query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
954 }
955 resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
956 query_param_status = 1;
957 auto length_str = std::to_string(length);
958 resourceplusopaque.append("oss.asize=");
959 resourceplusopaque.append(length_str.c_str());
960 if (!opaque) {
961 opaque = new XrdOucEnv();
962 }
963 opaque->Put("oss.asize", length_str.c_str());
964 }
965 }
966
968 if (!m_appended_hdr2cgistr && !hdr2cgistr.empty()) {
969 if (query_param_status == 0) {
970 query_param_status = strchr(resourceplusopaque.c_str(), '?') ? 1 : 2;
971 }
972 resourceplusopaque.append((query_param_status == 1) ? '&' : '?');
973
974 std::string hdr2cgistrEncoded = encode_opaque(hdr2cgistr);
975 resourceplusopaque.append(hdr2cgistrEncoded.c_str());
976 if (TRACING(TRACE_DEBUG)) {
977 // The obfuscation of "authz" will only be done if the server http.header2cgi config contains something that maps a header to this "authz" cgi.
978 // Unfortunately the obfuscation code will be called no matter what is configured in http.header2cgi.
979 std::string header2cgistrObf = obfuscateAuth(hdr2cgistr);
980
981 TRACEI(DEBUG, "Appended header fields to opaque info: '"
982 << header2cgistrObf.c_str() << "'");
983
984 }
985
987 }
988
989 // Verify if we have an external handler for this request
990 if (reqstate == 0) {
991 XrdHttpExtHandler *exthandler = prot->FindMatchingExtHandler(*this);
992 if (exthandler) {
993 XrdHttpExtReq xreq(this, prot);
994 int r = exthandler->ProcessReq(xreq);
995 reset();
996 if (!r) return 1; // All went fine, response sent
997 if (r < 0) return -1; // There was a hard error... close the connection
998
999 return 1; // There was an error and a response was sent
1000 }
1001 }
1002
1003 //
1004 // Here we process the request locally
1005 //
1006
1007 switch (request) {
1010 {
1011 prot->SendSimpleResp(400, NULL, NULL, (char *) "Request unknown", 0, false);
1012 reset();
1013 return -1;
1014 }
1016 {
1017 prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed", 0, false);
1018 reset();
1019 return -1;
1020 }
1021 case XrdHttpReq::rtHEAD:
1022 {
1023 if (reqstate == 0) {
1024 // Always start with Stat; in the case of a checksum request, we'll have a follow-up query
1025 if (prot->doStat((char *) resourceplusopaque.c_str())) {
1026 prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1027 return -1;
1028 }
1029 return 0;
1030 } else {
1031 const char *opaque = strchr(resourceplusopaque.c_str(), '?');
1032 // Note that doChksum requires that the memory stays alive until the callback is invoked.
1034
1035 m_req_cksum = prot->cksumHandler.getChecksumToRun(m_req_digest);
1036 if(!m_req_cksum) {
1037 // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1038 prot->SendSimpleResp(403, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1039 return -1;
1040 }
1041 if (!opaque) {
1042 m_resource_with_digest += "?cks.type=";
1043 m_resource_with_digest += m_req_cksum->getXRootDConfigDigestName().c_str();
1044 } else {
1045 m_resource_with_digest += "&cks.type=";
1046 m_resource_with_digest += m_req_cksum->getXRootDConfigDigestName().c_str();
1047 }
1048 if (prot->doChksum(m_resource_with_digest) < 0) {
1049 // In this case, the Want-Digest header was set and PostProcess gave the go-ahead to do a checksum.
1050 prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to create initial checksum request.", 0, false);
1051 return -1;
1052 }
1053 return 1;
1054 }
1055 }
1056 case XrdHttpReq::rtGET:
1057 {
1058 int retval = keepalive ? 1 : -1; // reset() clears keepalive
1059
1060 if (resource.beginswith("/static/")) {
1061
1062 // This is a request for a /static resource
1063 // If we have to use the embedded ones then we return the ones in memory as constants
1064
1065 // The sysadmin can always redirect the request to another host that
1066 // contains his static resources
1067
1068 // We also allow xrootd to preread from the local disk all the files
1069 // that have to be served as static resources.
1070
1071 if (prot->embeddedstatic) {
1072
1073 // Default case: the icon and the css of the HTML rendering of XrdHttp
1074 if (resource == "/static/css/xrdhttp.css") {
1075 prot->SendSimpleResp(200, NULL, NULL, (char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len, keepalive);
1076 reset();
1077 return retval;
1078 }
1079 if (resource == "/static/icons/xrdhttp.ico") {
1080 prot->SendSimpleResp(200, NULL, NULL, (char *) favicon_ico, favicon_ico_len, keepalive);
1081 reset();
1082 return retval;
1083 }
1084
1085 }
1086
1087 // If we are here then none of the embedded resources match (or they are disabled)
1088 // We may have to redirect to a host that is supposed to serve the static resources
1089 if (prot->staticredir) {
1090
1091 XrdOucString s = "Location: ";
1092 s.append(prot->staticredir);
1093
1094 if (s.endswith('/'))
1095 s.erasefromend(1);
1096
1097 s.append(resource);
1098 appendOpaque(s, 0, 0, 0);
1099
1100 prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1101 return -1;
1102
1103
1104 } else {
1105
1106 // We lookup the requested path in a hash containing the preread files
1107 if (prot->staticpreload) {
1108 XrdHttpProtocol::StaticPreloadInfo *mydata = prot->staticpreload->Find(resource.c_str());
1109 if (mydata) {
1110 prot->SendSimpleResp(200, NULL, NULL, (char *) mydata->data, mydata->len, keepalive);
1111 reset();
1112 return retval;
1113 }
1114 }
1115
1116 }
1117
1118
1119 }
1120
1121 // The reqstate parameter basically moves us through a simple state machine.
1122 // To optimize things, we start off by opening the file; if it turns out to be a directory, then
1123 // we close the file handle and switch to doing a HTML-based rendering of the directory. This
1124 // avoids needing to always to do "stat" first to determine the next step (since the file-open also
1125 // does a "stat").
1126 // - 0: Perform an open on the resource
1127 // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
1128 // - 2: Perform a close (for dirlist only)
1129 // - 3: Perform a dirlist.
1130 // - 4+: Reads from file; if at end, perform a close.
1131 switch (reqstate) {
1132 case 0: // Open the path for reading.
1133 {
1134 memset(&xrdreq, 0, sizeof (ClientRequest));
1135 xrdreq.open.requestid = htons(kXR_open);
1136 l = resourceplusopaque.length() + 1;
1137 xrdreq.open.dlen = htonl(l);
1138 xrdreq.open.mode = 0;
1139 xrdreq.open.options = htons(kXR_retstat | kXR_open_read | ((readRangeHandler.getMaxRanges() <= 1) ? kXR_seqio : 0));
1140
1141 if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1142 prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, false);
1143 return -1;
1144 }
1145
1146 // Prepare to chunk up the request
1147 writtenbytes = 0;
1148
1149 // We want to be invoked again after this request is finished
1150 return 0;
1151 }
1152 case 1: // Checksum request
1153 if (!(fileflags & kXR_isDir) && !m_req_digest.empty()) {
1154 // In this case, the Want-Digest header was set.
1155 bool has_opaque = strchr(resourceplusopaque.c_str(), '?');
1156 // Note that doChksum requires that the memory stays alive until the callback is invoked.
1157 m_req_cksum = prot->cksumHandler.getChecksumToRun(m_req_digest);
1158 if(!m_req_cksum) {
1159 // No HTTP IANA checksums have been configured by the server admin, return a "METHOD_NOT_ALLOWED" error
1160 prot->SendSimpleResp(403, NULL, NULL, (char *) "No HTTP-IANA compatible checksums have been configured.", 0, false);
1161 return -1;
1162 }
1164 if (has_opaque) {
1165 m_resource_with_digest += "&cks.type=";
1166 m_resource_with_digest += m_req_cksum->getXRootDConfigDigestName().c_str();
1167 } else {
1168 m_resource_with_digest += "?cks.type=";
1169 m_resource_with_digest += m_req_cksum->getXRootDConfigDigestName().c_str();
1170 }
1171 if (prot->doChksum(m_resource_with_digest) < 0) {
1172 prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to start internal checksum request to satisfy Want-Digest header.", 0, false);
1173 return -1;
1174 }
1175 return 0;
1176 } else {
1177 TRACEI(DEBUG, "No checksum requested; skipping to request state 2");
1178 reqstate += 1;
1179 }
1180 // fallthrough
1181 case 2: // Close file handle for directory
1182 if ((fileflags & kXR_isDir) && fopened) {
1183 memset(&xrdreq, 0, sizeof (ClientRequest));
1184 xrdreq.close.requestid = htons(kXR_close);
1185 memcpy(xrdreq.close.fhandle, fhandle, 4);
1186
1187 if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1188 mapXrdErrorToHttpStatus();
1189 return sendFooterError("Could not run close request on the bridge");
1190 }
1191 return 0;
1192 } else {
1193 reqstate += 1;
1194 }
1195 // fallthrough
1196 case 3: // List directory
1197 if (fileflags & kXR_isDir) {
1198 if (prot->listdeny) {
1199 prot->SendSimpleResp(503, NULL, NULL, (char *) "Listings are disabled.", 0, false);
1200 return -1;
1201 }
1202
1203 if (prot->listredir) {
1204 XrdOucString s = "Location: ";
1205 s.append(prot->listredir);
1206
1207 if (s.endswith('/'))
1208 s.erasefromend(1);
1209
1210 s.append(resource);
1211 appendOpaque(s, 0, 0, 0);
1212
1213 prot->SendSimpleResp(302, NULL, (char *) s.c_str(), 0, 0, false);
1214 return -1;
1215 }
1216
1217 std::string res;
1218 res = resourceplusopaque.c_str();
1219
1220 // --------- DIRLIST
1221 memset(&xrdreq, 0, sizeof (ClientRequest));
1222 xrdreq.dirlist.requestid = htons(kXR_dirlist);
1223 xrdreq.dirlist.options[0] = kXR_dstat;
1224 l = res.length() + 1;
1225 xrdreq.dirlist.dlen = htonl(l);
1226
1227 if (!prot->Bridge->Run((char *) &xrdreq, (char *) res.c_str(), l)) {
1228 mapXrdErrorToHttpStatus();
1229 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1230 sendFooterError("Could not run listing request on the bridge");
1231 return -1;
1232 }
1233
1234 // We don't want to be invoked again after this request is finished
1235 return 1;
1236 }
1237 else {
1238 reqstate += 1;
1239 }
1240 // fallthrough
1241 case 4:
1242 {
1243 auto retval = ReturnGetHeaders();
1244 if (retval) {
1245 return retval;
1246 }
1247 }
1248 // fallthrough
1249 default: // Read() or Close(); reqstate is 4+
1250 {
1251 const XrdHttpIOList &readChunkList = readRangeHandler.NextReadList();
1252
1253 // Close() if we have finished, otherwise read the next chunk
1254
1255 // --------- CLOSE
1256 if ( closeAfterError || readChunkList.empty() )
1257 {
1258
1259 memset(&xrdreq, 0, sizeof (ClientRequest));
1260 xrdreq.close.requestid = htons(kXR_close);
1261 memcpy(xrdreq.close.fhandle, fhandle, 4);
1262
1263 if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1264 TRACEI(REQ, " Failed to run close request on the bridge.");
1265 // Note: we have already completed the request and sent the data to the client.
1266 // Hence, there's no need to send an error. However, since the bridge is potentially
1267 // in a bad state, we close the TCP socket to force the client to reconnect.
1268 return -1;
1269 }
1270
1271 // We have finished
1272 readClosing = true;
1273 return 1;
1274
1275 }
1276 // --------- READ or READV
1277
1278 if ( readChunkList.size() == 1 ) {
1279 // Use a read request for single range
1280
1281 long l;
1282 long long offs;
1283
1284 // --------- READ
1285 memset(&xrdreq, 0, sizeof (xrdreq));
1286 xrdreq.read.requestid = htons(kXR_read);
1287 memcpy(xrdreq.read.fhandle, fhandle, 4);
1288 xrdreq.read.dlen = 0;
1289
1290 offs = readChunkList[0].offset;
1291 l = readChunkList[0].size;
1292
1293 xrdreq.read.offset = htonll(offs);
1294 xrdreq.read.rlen = htonl(l);
1295
1296 // If we are using HTTPS or if the client requested trailers, or if the
1297 // read concerns a multirange reponse, disable sendfile
1298 // (in the latter two cases, the extra framing is only done in PostProcessHTTPReq)
1299 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1300 !readRangeHandler.isSingleRange()) {
1301 if (!prot->Bridge->setSF((kXR_char *) fhandle, false)) {
1302 TRACE(REQ, " XrdBridge::SetSF(false) failed.");
1303
1304 }
1305 }
1306
1307
1308
1309 if (l <= 0) {
1310 if (l < 0) {
1311 TRACE(ALL, " Data sizes mismatch.");
1312 return -1;
1313 }
1314 else {
1315 TRACE(ALL, " No more bytes to send.");
1316 reset();
1317 return 1;
1318 }
1319 }
1320
1321 if ((offs >= filesize) || (offs+l > filesize)) {
1322 httpStatusCode = 416;
1323 httpErrorBody = "Range Not Satisfiable";
1324 std::stringstream ss;
1325 ss << "Requested range " << l << "@" << offs << " is past the end of file (" << filesize << ")";
1326 return sendFooterError(ss.str());
1327 }
1328
1329 if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1330 mapXrdErrorToHttpStatus();
1331 return sendFooterError("Could not run read request on the bridge");
1332 }
1333 } else {
1334 // --------- READV
1335
1336 length = ReqReadV(readChunkList);
1337
1338 if (!prot->Bridge->Run((char *) &xrdreq, (char *) &ralist[0], length)) {
1339 mapXrdErrorToHttpStatus();
1340 return sendFooterError("Could not run ReadV request on the bridge");
1341 }
1342
1343 }
1344
1345 // We want to be invoked again after this request is finished
1346 return 0;
1347 } // case 3+
1348
1349 } // switch (reqstate)
1350
1351
1352 } // case XrdHttpReq::rtGET
1353
1354 case XrdHttpReq::rtPUT:
1355 {
1356 //if (prot->ishttps) {
1357 //prot->SendSimpleResp(501, NULL, NULL, (char *) "HTTPS not supported yet for direct writing. Sorry.", 0);
1358 //return -1;
1359 //}
1360
1361 if (!fopened) {
1362
1363 // --------- OPEN for write!
1364 memset(&xrdreq, 0, sizeof (ClientRequest));
1365 xrdreq.open.requestid = htons(kXR_open);
1366 l = resourceplusopaque.length() + 1;
1367 xrdreq.open.dlen = htonl(l);
1368 xrdreq.open.mode = htons(kXR_ur | kXR_uw | kXR_gw | kXR_gr | kXR_or);
1369 if (! XrdHttpProtocol::usingEC)
1370 xrdreq.open.options = htons(kXR_mkpath | kXR_open_wrto | kXR_delete);
1371 else
1372 xrdreq.open.options = htons(kXR_mkpath | kXR_open_wrto | kXR_new);
1373
1374 if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1375 prot->SendSimpleResp(404, NULL, NULL, (char *) "Could not run request.", 0, keepalive);
1376 return -1;
1377 }
1378
1379
1380 // We want to be invoked again after this request is finished
1381 // Only if there is data to fetch from the socket or there will
1382 // never be more data
1383 if (prot->BuffUsed() > 0 || (length == 0 && !sendcontinue))
1384 return 0;
1385
1386 return 1;
1387
1388 } else {
1389
1390 if (m_transfer_encoding_chunked) {
1391 if (m_current_chunk_size == m_current_chunk_offset) {
1392 // Chunk has been consumed; we now must process the CRLF.
1393 // Note that we don't support trailer headers.
1394 if (prot->BuffUsed() < 2) return 1;
1395 if (prot->myBuffStart[0] != '\r' || prot->myBuffStart[1] != '\n') {
1396 prot->SendSimpleResp(400, NULL, NULL, (char *) "Invalid trailing chunk encoding.", 0, keepalive);
1397 return -1;
1398 }
1399 prot->BuffConsume(2);
1400 if (m_current_chunk_size == 0) {
1401 // All data has been sent. Turn off chunk processing and
1402 // set the bytes written and length appropriately; on next callback,
1403 // we will hit the close() block below.
1404 m_transfer_encoding_chunked = false;
1406 return ProcessHTTPReq();
1407 }
1408 m_current_chunk_size = -1;
1409 m_current_chunk_offset = 0;
1410 // If there is more data, we try to process the next chunk; otherwise, return
1411 if (!prot->BuffUsed()) return 1;
1412 }
1413 if (-1 == m_current_chunk_size) {
1414
1415 // Parse out the next chunk size.
1416 long long idx = 0;
1417 bool found_newline = false;
1418 // Set a maximum size of chunk we will allow
1419 // Nginx sets this to "NGX_MAX_OFF_T_VALUE", which is 9223372036854775807 (a some crazy number)
1420 // We set it to 1TB, which is 1099511627776
1421 // This is to prevent a malicious client from sending a very large chunk size
1422 // or a malformed chunk request.
1423 // 1TB in base-16 is 0x40000000000, so only allow 11 characters, plus the CRLF
1424 long long max_chunk_size_chars = std::min(static_cast<long long>(prot->BuffUsed()), static_cast<long long>(13));
1425 for (; idx < max_chunk_size_chars; idx++) {
1426 if (prot->myBuffStart[idx] == '\n') {
1427 found_newline = true;
1428 break;
1429 }
1430 }
1431 // If we found a new line, but it is the first character in the buffer (no chunk length)
1432 // or if the previous character is not a CR.
1433 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] != '\r')) {
1434 prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1435 TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1436 return -1;
1437 }
1438 if (found_newline) {
1439 char *endptr = NULL;
1440 std::string line_contents(prot->myBuffStart, idx);
1441 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1442 // Chunk sizes can be followed by trailer information or CRLF
1443 if (*endptr != ';' && *endptr != '\r') {
1444 prot->SendSimpleResp(400, NULL, NULL, (char *)"Invalid chunked encoding", 0, false);
1445 TRACE(REQ, "XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1446 return -1;
1447 }
1448 m_current_chunk_size = chunk_contents;
1449 m_current_chunk_offset = 0;
1450 prot->BuffConsume(idx + 1);
1451 TRACE(REQ, "XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size << " bytes");
1452 } else {
1453 // Need more data!
1454 return 1;
1455 }
1456 }
1457
1458 if (m_current_chunk_size == 0) {
1459 // All data has been sent. Invoke this routine again immediately to process CRLF
1460 return ProcessHTTPReq();
1461 } else {
1462 // At this point, we have a chunk size defined and should consume payload data
1463 memset(&xrdreq, 0, sizeof (xrdreq));
1464 xrdreq.write.requestid = htons(kXR_write);
1465 memcpy(xrdreq.write.fhandle, fhandle, 4);
1466
1467 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1468 long long bytes_to_write = std::min(static_cast<long long>(prot->BuffUsed()),
1469 chunk_bytes_remaining);
1470
1471 xrdreq.write.offset = htonll(writtenbytes);
1472 xrdreq.write.dlen = htonl(bytes_to_write);
1473
1474 TRACEI(REQ, "XrdHTTP PUT: Writing chunk of size " << bytes_to_write << " starting with '" << *(prot->myBuffStart) << "'" << " with " << chunk_bytes_remaining << " bytes remaining in the chunk");
1475 if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_write)) {
1476 mapXrdErrorToHttpStatus();
1477 return sendFooterError("Could not run write request on the bridge");
1478 }
1479 // If there are more bytes in the buffer, then immediately call us after the
1480 // write is finished; otherwise, wait for data.
1481 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1482 }
1483 } else if (writtenbytes < length) {
1484
1485
1486 // --------- WRITE
1487 memset(&xrdreq, 0, sizeof (xrdreq));
1488 xrdreq.write.requestid = htons(kXR_write);
1489 memcpy(xrdreq.write.fhandle, fhandle, 4);
1490
1491 long long bytes_to_read = std::min(static_cast<long long>(prot->BuffUsed()),
1493
1494 xrdreq.write.offset = htonll(writtenbytes);
1495 xrdreq.write.dlen = htonl(bytes_to_read);
1496
1497 TRACEI(REQ, "Writing " << bytes_to_read);
1498 if (!prot->Bridge->Run((char *) &xrdreq, prot->myBuffStart, bytes_to_read)) {
1499 mapXrdErrorToHttpStatus();
1500 return sendFooterError("Could not run write request on the bridge");
1501 }
1502
1503 if (writtenbytes + prot->BuffUsed() >= length)
1504 // Trigger an immediate recall after this request has finished
1505 return 0;
1506 else
1507 // We want to be invoked again after this request is finished
1508 // only if there is pending data
1509 return 1;
1510
1511
1512
1513 } else {
1514
1515 // --------- CLOSE
1516 memset(&xrdreq, 0, sizeof (ClientRequest));
1517 xrdreq.close.requestid = htons(kXR_close);
1518 memcpy(xrdreq.close.fhandle, fhandle, 4);
1519
1520
1521 if (!prot->Bridge->Run((char *) &xrdreq, 0, 0)) {
1522 mapXrdErrorToHttpStatus();
1523 return sendFooterError("Could not run close request on the bridge");
1524 }
1525
1526 // We have finished
1527 return 1;
1528
1529 }
1530
1531 }
1532
1533 break;
1534
1535 }
1537 {
1538 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);
1539 bool ret_keepalive = keepalive; // reset() clears keepalive
1540 reset();
1541 return ret_keepalive ? 1 : -1;
1542 }
1544 {
1545
1546
1547 switch (reqstate) {
1548
1549 case 0: // Stat()
1550 {
1551
1552
1553 // --------- STAT is always the first step
1554 memset(&xrdreq, 0, sizeof (ClientRequest));
1555 xrdreq.stat.requestid = htons(kXR_stat);
1556 std::string s = resourceplusopaque.c_str();
1557
1558
1559 l = resourceplusopaque.length() + 1;
1560 xrdreq.stat.dlen = htonl(l);
1561
1562 if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1563 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1564 return -1;
1565 }
1566
1567 // We need to be invoked again to complete the request
1568 return 0;
1569 }
1570 default:
1571
1572 if (fileflags & kXR_isDir) {
1573 // --------- RMDIR
1574 memset(&xrdreq, 0, sizeof (ClientRequest));
1575 xrdreq.rmdir.requestid = htons(kXR_rmdir);
1576
1577 std::string s = resourceplusopaque.c_str();
1578
1579 l = s.length() + 1;
1580 xrdreq.rmdir.dlen = htonl(l);
1581
1582 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1583 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rmdir request.", 0, false);
1584 return -1;
1585 }
1586 } else {
1587 // --------- DELETE
1588 memset(&xrdreq, 0, sizeof (ClientRequest));
1589 xrdreq.rm.requestid = htons(kXR_rm);
1590
1591 std::string s = resourceplusopaque.c_str();
1592
1593 l = s.length() + 1;
1594 xrdreq.rm.dlen = htonl(l);
1595
1596 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1597 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run rm request.", 0, false);
1598 return -1;
1599 }
1600 }
1601
1602
1603 // We don't want to be invoked again after this request is finished
1604 return 1;
1605
1606 }
1607
1608
1609
1610 }
1612 {
1613 prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported yet.", 0, false);
1614
1615 return -1;
1616 }
1618 {
1619
1620
1621
1622 switch (reqstate) {
1623
1624 case 0: // Stat() and add the current item to the list of the things to send
1625 {
1626
1627 if (length > 0) {
1628 TRACE(REQ, "Reading request body " << length << " bytes.");
1629 char *p = 0;
1630 // We have to specifically read all the request body
1631
1632 if (prot->BuffgetData(length, &p, true) < length) {
1633 prot->SendSimpleResp(501, NULL, NULL, (char *) "Error in getting the PROPFIND request body.", 0, false);
1634 return -1;
1635 }
1636
1637 if ((depth > 1) || (depth < 0)) {
1638 prot->SendSimpleResp(501, NULL, NULL, (char *) "Invalid depth value.", 0, false);
1639 return -1;
1640 }
1641
1642
1643 parseBody(p, length);
1644 }
1645
1646
1647 // --------- STAT is always the first step
1648 memset(&xrdreq, 0, sizeof (ClientRequest));
1649 xrdreq.stat.requestid = htons(kXR_stat);
1650 std::string s = resourceplusopaque.c_str();
1651
1652
1653 l = resourceplusopaque.length() + 1;
1654 xrdreq.stat.dlen = htonl(l);
1655
1656 if (!prot->Bridge->Run((char *) &xrdreq, (char *) resourceplusopaque.c_str(), l)) {
1657 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1658 return -1;
1659 }
1660
1661
1662 if (depth == 0) {
1663 // We don't need to be invoked again
1664 return 1;
1665 } else
1666 // We need to be invoked again to complete the request
1667 return 0;
1668
1669
1670
1671 break;
1672 }
1673
1674 default: // Dirlist()
1675 {
1676
1677 // --------- DIRLIST
1678 memset(&xrdreq, 0, sizeof (ClientRequest));
1679 xrdreq.dirlist.requestid = htons(kXR_dirlist);
1680
1681 std::string s = resourceplusopaque.c_str();
1682 xrdreq.dirlist.options[0] = kXR_dstat;
1683 //s += "?xrd.dirstat=1";
1684
1685 l = s.length() + 1;
1686 xrdreq.dirlist.dlen = htonl(l);
1687
1688 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1689 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1690 return -1;
1691 }
1692
1693 // We don't want to be invoked again after this request is finished
1694 return 1;
1695 }
1696 }
1697
1698
1699 break;
1700 }
1702 {
1703
1704 // --------- MKDIR
1705 memset(&xrdreq, 0, sizeof (ClientRequest));
1706 xrdreq.mkdir.requestid = htons(kXR_mkdir);
1707
1708 std::string s = resourceplusopaque.c_str();
1709 xrdreq.mkdir.options[0] = (kXR_char) kXR_mkdirpath;
1710
1711 l = s.length() + 1;
1712 xrdreq.mkdir.dlen = htonl(l);
1713
1714 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1715 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1716 return -1;
1717 }
1718
1719 // We don't want to be invoked again after this request is finished
1720 return 1;
1721 }
1722 case XrdHttpReq::rtMOVE:
1723 {
1724 // Incase of a move cgi parameters present in the CGI str
1725 // are appended to the destination in case of a MOVE.
1726 if (resourceplusopaque != "") {
1727 int pos = resourceplusopaque.find("?");
1728 if (pos != STR_NPOS) {
1729 destination.append((destination.find("?") == std::string::npos) ? "?" : "&");
1730 destination.append(resourceplusopaque.c_str() + pos + 1);
1731 }
1732 }
1733
1734 // --------- MOVE
1735 memset(&xrdreq, 0, sizeof (ClientRequest));
1736 xrdreq.mv.requestid = htons(kXR_mv);
1737
1738 std::string s = resourceplusopaque.c_str();
1739 s += " ";
1740
1741 char buf[256];
1742 char *ppath;
1743 int port = 0;
1744 if (parseURL((char *) destination.c_str(), buf, port, &ppath)) {
1745 prot->SendSimpleResp(501, NULL, NULL, (char *) "Cannot parse destination url.", 0, false);
1746 return -1;
1747 }
1748
1749 char buf2[256];
1750 strcpy(buf2, host.c_str());
1751 char *pos = strchr(buf2, ':');
1752 if (pos) *pos = '\0';
1753
1754 // If we are a redirector we enforce that the host field is equal to
1755 // whatever was written in the destination url
1756 //
1757 // If we are a data server instead we cannot enforce anything, we will
1758 // just ignore the host part of the destination
1759 if ((prot->myRole == kXR_isManager) && strcmp(buf, buf2)) {
1760 prot->SendSimpleResp(501, NULL, NULL, (char *) "Only in-place renaming is supported for MOVE.", 0, false);
1761 return -1;
1762 }
1763
1764
1765
1766
1767 s += ppath;
1768
1769 l = s.length() + 1;
1770 xrdreq.mv.dlen = htonl(l);
1771 xrdreq.mv.arg1len = htons(resourceplusopaque.length());
1772
1773 if (!prot->Bridge->Run((char *) &xrdreq, (char *) s.c_str(), l)) {
1774 prot->SendSimpleResp(501, NULL, NULL, (char *) "Could not run request.", 0, false);
1775 return -1;
1776 }
1777
1778 // We don't want to be invoked again after this request is finished
1779 return 1;
1780
1781 }
1782 default:
1783 {
1784 prot->SendSimpleResp(501, NULL, NULL, (char *) "Request not supported.", 0, false);
1785 return -1;
1786 }
1787
1788 }
1789
1790 return 1;
1791}
1792
1793
1794int
1795XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1796 if (iovN > 0) {
1797 if (xrdresp == kXR_error) {
1798 prot->SendSimpleResp(httpStatusCode, NULL, NULL, "Failed to determine checksum", 0, false);
1799 return -1;
1800 }
1801
1802 TRACEI(REQ, "Checksum for HEAD " << resource.c_str() << " "
1803 << reinterpret_cast<char *>(iovP[0].iov_base) << "="
1804 << reinterpret_cast<char *>(iovP[iovN-1].iov_base));
1805
1806 bool convert_to_base64 = m_req_cksum->needsBase64Padding();
1807 char *digest_value = reinterpret_cast<char *>(iovP[iovN-1].iov_base);
1808 if (convert_to_base64) {
1809 size_t digest_length = strlen(digest_value);
1810 unsigned char *digest_binary_value = (unsigned char *)malloc(digest_length);
1811 if (!Fromhexdigest(reinterpret_cast<unsigned char *>(digest_value), digest_length, digest_binary_value)) {
1812 prot->SendSimpleResp(500, NULL, NULL, (char *) "Failed to convert checksum hexdigest to base64.", 0, false);
1813 free(digest_binary_value);
1814 return -1;
1815 }
1816 char *digest_base64_value = (char *)malloc(digest_length + 1);
1817 // Binary length is precisely half the size of the hex-encoded digest_value; hence, divide length by 2.
1818 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1819 free(digest_binary_value);
1820 digest_value = digest_base64_value;
1821 }
1822
1823 digest_header = "Digest: ";
1824 digest_header += m_req_cksum->getHttpName();
1825 digest_header += "=";
1826 digest_header += digest_value;
1827 if (convert_to_base64) {free(digest_value);}
1828 return 0;
1829 } else {
1830 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(), false);
1831 return -1;
1832 }
1833}
1834
1835int
1836XrdHttpReq::PostProcessListing(bool final_) {
1837
1838 if (xrdresp == kXR_error) {
1839 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1840 httpErrorBody.c_str(), httpErrorBody.length(), false);
1841 return -1;
1842 }
1843
1844 if (stringresp.empty()) {
1845 // Start building the HTML response
1846 stringresp = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1847 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1848 "<head>\n"
1849 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1850 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1851 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1852
1853 stringresp += "<title>";
1854 stringresp += resource.c_str();
1855 stringresp += "</title>\n";
1856
1857 stringresp += "</head>\n"
1858 "<body>\n";
1859
1860 char *estr = escapeXML(resource.c_str());
1861
1862 stringresp += "<h1>Listing of: ";
1863 stringresp += estr;
1864 stringresp += "</h1>\n";
1865
1866 free(estr);
1867
1868 stringresp += "<div id=\"header\">";
1869
1870 stringresp += "<table id=\"ft\">\n"
1871 "<thead><tr>\n"
1872 "<th class=\"mode\">Mode</th>"
1873 "<th class=\"flags\">Flags</th>"
1874 "<th class=\"size\">Size</th>"
1875 "<th class=\"datetime\">Modified</th>"
1876 "<th class=\"name\">Name</th>"
1877 "</tr></thead>\n";
1878 }
1879
1880 // Now parse the answer building the entries vector
1881 if (iovN > 0) {
1882 char *startp = (char *) iovP[0].iov_base, *endp = 0;
1883 char entry[1024];
1884 DirListInfo e;
1885 while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)( iovP[0].iov_len - 1) ) {
1886 // Find the filename, it comes before the \n
1887 if ((endp = (char *) strchr((const char*) startp, '\n'))) {
1888 strncpy(entry, (char *) startp, endp - startp);
1889 entry[endp - startp] = 0;
1890 e.path = entry;
1891
1892 endp++;
1893
1894 // Now parse the stat info
1895 TRACEI(REQ, "Dirlist " << resource.c_str() << " entry=" << entry
1896 << " stat=" << endp);
1897
1898 long dummyl;
1899 sscanf(endp, "%ld %lld %ld %ld",
1900 &dummyl,
1901 &e.size,
1902 &e.flags,
1903 &e.modtime);
1904 } else
1905 strcpy(entry, (char *) startp);
1906
1907 if (e.path.length() && (e.path != ".") && (e.path != "..")) {
1908 // The entry is filled. <td class="ft-file"><a href="file1.txt">file1.txt</a></td>
1909 std::string p = "<tr>"
1910 "<td class=\"mode\">";
1911
1912 if (e.flags & kXR_isDir) p += "d";
1913 else p += "-";
1914
1915 if (e.flags & kXR_other) p += "o";
1916 else p += "-";
1917
1918 if (e.flags & kXR_offline) p += "O";
1919 else p += "-";
1920
1921 if (e.flags & kXR_readable) p += "r";
1922 else p += "-";
1923
1924 if (e.flags & kXR_writable) p += "w";
1925 else p += "-";
1926
1927 if (e.flags & kXR_xset) p += "x";
1928 else p += "-";
1929
1930 p += "</td>";
1931 p += "<td class=\"mode\">" + itos(e.flags) + "</td>"
1932 "<td class=\"size\">" + itos(e.size) + "</td>"
1933 "<td class=\"datetime\">" + ISOdatetime(e.modtime) + "</td>"
1934 "<td class=\"name\">"
1935 "<a href=\"";
1936
1937 if (resource != "/") {
1938
1939 char *estr = escapeXML(resource.c_str());
1940
1941 p += estr;
1942 if (!p.empty() && p[p.size() - 1] != '/')
1943 p += "/";
1944
1945 free(estr);
1946 }
1947 std::unique_ptr<char, decltype(&free)> estr(escapeXML(e.path.c_str()), &free);
1948 p += estr.get();
1949 if (e.flags & kXR_isDir) p += "/";
1950 p += "\">";
1951 p += estr.get();
1952 if (e.flags & kXR_isDir) p += "/";
1953 p += "</a></td></tr>";
1954
1955 stringresp += p;
1956 }
1957
1958 if (endp) {
1959 char *pp = (char *)strchr((const char *)endp, '\n');
1960 if (pp) startp = pp+1;
1961 else break;
1962 } else break;
1963
1964 }
1965 }
1966
1967 // If this was the last bunch of entries, send the buffer and empty it immediately
1968 if (final_) {
1969 stringresp += "</table></div><br><br><hr size=1>"
1970 "<p><span id=\"requestby\">Request by ";
1971
1972 if (prot->SecEntity.name)
1973 stringresp += prot->SecEntity.name;
1974 else
1975 stringresp += prot->Link->ID;
1976
1977 if (prot->SecEntity.vorg ||
1978 prot->SecEntity.name ||
1979 prot->SecEntity.moninfo ||
1980 prot->SecEntity.role)
1981 stringresp += " (";
1982
1983 if (prot->SecEntity.vorg) {
1984 stringresp += " VO: ";
1985 stringresp += prot->SecEntity.vorg;
1986 }
1987
1988 if (prot->SecEntity.moninfo) {
1989 stringresp += " DN: ";
1990 stringresp += prot->SecEntity.moninfo;
1991 } else
1992 if (prot->SecEntity.name) {
1993 stringresp += " DN: ";
1994 stringresp += prot->SecEntity.name;
1995 }
1996
1997 if (prot->SecEntity.role) {
1998 stringresp += " Role: ";
1999 stringresp += prot->SecEntity.role;
2000 if (prot->SecEntity.endorsements) {
2001 stringresp += " (";
2002 stringresp += prot->SecEntity.endorsements;
2003 stringresp += ") ";
2004 }
2005 }
2006
2007 if (prot->SecEntity.vorg ||
2008 prot->SecEntity.moninfo ||
2009 prot->SecEntity.role)
2010 stringresp += " )";
2011
2012 if (prot->SecEntity.host) {
2013 stringresp += " ( ";
2014 stringresp += prot->SecEntity.host;
2015 stringresp += " )";
2016 }
2017
2018 stringresp += "</span></p>\n";
2019 stringresp += "<p>Powered by XrdHTTP ";
2020 stringresp += XrdVSTRING;
2021 stringresp += " (CERN IT-SDC)</p>\n";
2022
2023 prot->SendSimpleResp(200, NULL, NULL, (char *) stringresp.c_str(), 0, keepalive);
2024 stringresp.clear();
2025 return keepalive ? 1 : -1;
2026 }
2027
2028 return 0;
2029}
2030
2031int
2032XrdHttpReq::ReturnGetHeaders() {
2033 std::string responseHeader;
2034 if (!m_digest_header.empty()) {
2035 responseHeader = m_digest_header;
2036 }
2037 if (fileflags & kXR_cachersp) {
2038 if (!responseHeader.empty()) {
2039 responseHeader += "\r\n";
2040 }
2041 addAgeHeader(responseHeader);
2042 }
2043
2044 const XrdHttpReadRangeHandler::UserRangeList &uranges = readRangeHandler.ListResolvedRanges();
2045 if (uranges.empty() && readRangeHandler.getError()) {
2046 prot->SendSimpleResp(readRangeHandler.getError().httpRetCode, NULL, NULL, readRangeHandler.getError().errMsg.c_str(),0,false);
2047 return -1;
2048 }
2049
2050 if (readRangeHandler.isFullFile()) {
2051 // Full file.
2052 TRACEI(REQ, "Sending full file: " << filesize);
2053 if (m_transfer_encoding_chunked && m_trailer_headers) {
2054 setTransferStatusHeader(responseHeader);
2055 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1, keepalive);
2056 } else {
2057 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL, filesize, keepalive);
2058 }
2059 return 0;
2060 }
2061
2062 if (readRangeHandler.isSingleRange()) {
2063 // Possibly with zero sized file but should have been included
2064 // in the FullFile case above
2065 if (uranges.size() != 1)
2066 return -1;
2067
2068 // Only one range to return to the user
2069 char buf[64];
2070 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2071
2072 std::string header = "Content-Range: bytes ";
2073 sprintf(buf, "%lld-%lld/%lld", (long long int)uranges[0].start, (long long int)uranges[0].end, filesize);
2074 header += buf;
2075 if (!responseHeader.empty()) {
2076 header += "\r\n";
2077 header += responseHeader.c_str();
2078 }
2079
2080 if (m_transfer_encoding_chunked && m_trailer_headers) {
2082 prot->StartChunkedResp(206, NULL, header.empty() ? nullptr : header.c_str(), -1, keepalive);
2083 } else {
2084 prot->SendSimpleResp(206, NULL, header.empty() ? nullptr : header.c_str(), NULL, cnt, keepalive);
2085 }
2086 return 0;
2087 }
2088
2089 // Multiple reads to perform, compose and send the header
2090 off_t cnt = 0;
2091 for (auto &ur : uranges) {
2092 cnt += ur.end - ur.start + 1;
2093
2094 cnt += buildPartialHdr(ur.start,
2095 ur.end,
2096 filesize,
2097 (char *) "123456").size();
2098
2099 }
2100 cnt += buildPartialHdrEnd((char *) "123456").size();
2101 std::string header = "Content-Type: multipart/byteranges; boundary=123456";
2102 if (!m_digest_header.empty()) {
2103 header += "\n";
2104 header += m_digest_header;
2105 }
2106 if (fileflags & kXR_cachersp) {
2107 if (!header.empty()) {
2108 header += "\r\n";
2109 }
2110 addAgeHeader(header);
2111 }
2112
2113 if (m_transfer_encoding_chunked && m_trailer_headers) {
2115 prot->StartChunkedResp(206, NULL, header.c_str(), -1, keepalive);
2116 } else {
2117 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt, keepalive);
2118 }
2119 return 0;
2120}
2121
2122void XrdHttpReq::setTransferStatusHeader(std::string &header) {
2123 if (m_status_trailer) {
2124 if (header.empty()) {
2125 header += "Trailer: X-Transfer-Status";
2126 } else {
2127 header += "\r\nTrailer: X-Transfer-Status";
2128 }
2129 }
2130}
2131
2132// This is invoked by the callbacks, after something has happened in the bridge
2133
2134int XrdHttpReq::PostProcessHTTPReq(bool final_) {
2135
2136 TRACEI(REQ, "PostProcessHTTPReq req: " << request << " reqstate: " << reqstate << " final_:" << final_);
2137 mapXrdErrorToHttpStatus();
2138
2139 if(xrdreq.set.requestid == htons(kXR_set)) {
2140 // We have set the user agent, if it fails we return a 500 error, otherwise the callback is successful --> we continue
2141 if(xrdresp != kXR_ok) {
2142 prot->SendSimpleResp(500, nullptr, nullptr, "Could not set user agent.", 0, false);
2143 return -1;
2144 }
2145 return 0;
2146 }
2147
2148 switch (request) {
2150 {
2151 prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 1", 0, false);
2152 return -1;
2153 }
2155 {
2156 prot->SendSimpleResp(400, NULL, NULL, (char *) "Request malformed 2", 0, false);
2157 return -1;
2158 }
2159 case XrdHttpReq::rtHEAD:
2160 {
2161 if (xrdresp != kXR_ok) {
2162 // NOTE that HEAD MUST NOT return a body, even in the case of failure.
2163 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, false);
2164 return -1;
2165 } else if (reqstate == 0) {
2166 if (iovN > 0) {
2167 std::string response_headers;
2168
2169 // Now parse the stat info
2170 TRACEI(REQ, "Stat for HEAD " << resource.c_str()
2171 << " stat=" << (char *) iovP[0].iov_base);
2172
2173 long dummyl;
2174 sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2175 &dummyl,
2176 &filesize,
2177 &fileflags,
2178 &filemodtime);
2179
2180 if (m_req_digest.size()) {
2181 return 0;
2182 } else {
2183 if (fileflags & kXR_cachersp) {
2184 addAgeHeader(response_headers);
2185 response_headers += "\r\n";
2186 }
2187 response_headers += "Accept-Ranges: bytes";
2188 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2189 return keepalive ? 1 : -1;
2190 }
2191 }
2192
2193 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0, keepalive);
2194 bool ret_keepalive = keepalive; // reset() clears keepalive
2195 reset();
2196 return ret_keepalive ? 1 : -1;
2197 } else { // We requested a checksum and now have its response.
2198 if (iovN > 0) {
2199 std::string response_headers;
2200 int response = PostProcessChecksum(response_headers);
2201 if (-1 == response) {
2202 return -1;
2203 }
2204 if (!response_headers.empty()) {response_headers += "\r\n";}
2205 if (fileflags & kXR_cachersp) {
2206 addAgeHeader(response_headers);
2207 response_headers += "\r\n";
2208 }
2209 response_headers += "Accept-Ranges: bytes";
2210 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL, filesize, keepalive);
2211 return keepalive ? 1 : -1;
2212 } else {
2213 prot->SendSimpleResp(500, NULL, NULL, "Underlying filesystem failed to calculate checksum.", 0, false);
2214 return -1;
2215 }
2216 }
2217 }
2218 case XrdHttpReq::rtGET:
2219 {
2220 // To duplicate the state diagram from the rtGET request state
2221 // - 0: Perform an open request
2222 // - 1: Perform a checksum request on the resource (only if requested in header; otherwise skipped)
2223 // - 2: Perform a close (for directory listings only)
2224 // - 3: Perform a dirlist
2225 // - 4+: Reads from file; if at end, perform a close.
2226 switch (reqstate) {
2227 case 0: // open
2228 {
2229 if (xrdresp == kXR_ok) {
2230 fopened = true;
2231 getfhandle();
2232
2233 // Always try to parse response. In the case of a caching proxy, the open
2234 // will have created the file in cache
2235 if (iovP[1].iov_len > 1) {
2236 TRACEI(REQ, "Stat for GET " << resource.c_str()
2237 << " stat=" << (char *) iovP[1].iov_base);
2238
2239 long dummyl;
2240 sscanf((const char *) iovP[1].iov_base, "%ld %lld %ld %ld",
2241 &dummyl,
2242 &filesize,
2243 &fileflags,
2244 &filemodtime);
2245
2246 // If this is a directory, bail out early; we will close the file handle
2247 // and then issue a directory listing.
2248 if (fileflags & kXR_isDir) {
2249 return 0;
2250 }
2251
2252 readRangeHandler.SetFilesize(filesize);
2253
2254 // As above: if the client specified a response size, we use that.
2255 // Otherwise, utilize the filesize
2256 if (!length) {
2257 length = filesize;
2258 }
2259 }
2260 else {
2261 TRACEI(ALL, "GET returned no STAT information. Internal error?");
2262 prot->SendSimpleResp(500, NULL, NULL, "Storage system did not return stat info.", 0, false);
2263 return -1;
2264 }
2265 return 0;
2266 } else if (xrderrcode == kXR_isDirectory) { // This is a directory; trigger directory-handling topic.
2268 return 0;
2269 } else { // xrdresp indicates an error occurred
2270
2271 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2272 httpErrorBody.c_str(), httpErrorBody.length(), false);
2273 return -1;
2274 }
2275 // Case should not be reachable
2276 return -1;
2277 } // end open
2278 case 1: // checksum was requested and now we have its response.
2279 {
2280 return PostProcessChecksum(m_digest_header);
2281 }
2282 case 2: // close file handle in case of the directory
2283 {
2284 if (xrdresp != kXR_ok) {
2285 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2286 httpErrorBody.c_str(), httpErrorBody.length(), false);
2287 return -1;
2288 }
2289 return 0;
2290 }
2291 case 3: // handle the directory listing response
2292 {
2293 return PostProcessListing(final_);
2294 }
2295 default: //read or readv, followed by a close.
2296 {
2297 // If we are postprocessing a close, potentially send out informational trailers
2298 if ((ntohs(xrdreq.header.requestid) == kXR_close) || readClosing)
2299 {
2300 // If we already sent out an error, then we cannot send any further
2301 // messages
2302 if (closeAfterError) {
2303 TRACEI(REQ, "Close was completed after an error: " << xrdresp);
2304 return xrdresp != kXR_ok ? -1 : 1;
2305 }
2306
2307 const XrdHttpReadRangeHandler::Error &rrerror = readRangeHandler.getError();
2308 if (rrerror) {
2309 httpStatusCode = rrerror.httpRetCode;
2310 httpErrorBody = rrerror.errMsg;
2311 }
2312
2313 if (m_transfer_encoding_chunked && m_trailer_headers) {
2314 if (prot->ChunkRespHeader(0))
2315 return -1;
2316
2317 const std::string crlf = "\r\n";
2318 std::stringstream ss;
2319 ss << "X-Transfer-Status: " << httpStatusCode << ": " << httpErrorBody << crlf;
2320
2321 const auto header = ss.str();
2322 if (prot->SendData(header.c_str(), header.size()))
2323 return -1;
2324
2325 if (prot->ChunkRespFooter())
2326 return -1;
2327 }
2328
2329 if (rrerror) return -1;
2330 return keepalive ? 1 : -1;
2331 }
2332
2333 // On error, we can only send out a message if trailers are enabled and the
2334 // status response in trailer behavior is requested.
2335 if (xrdresp == kXR_error) {
2336 auto rc = sendFooterError("");
2337 if (rc == 1) {
2338 closeAfterError = true;
2339 return 0;
2340 }
2341 return -1;
2342 }
2343
2344
2345 TRACEI(REQ, "Got data vectors to send:" << iovN);
2346
2347 XrdHttpIOList received;
2348 getReadResponse(received);
2349
2350 int rc;
2351 if (readRangeHandler.isSingleRange()) {
2352 rc = sendReadResponseSingleRange(received);
2353 } else {
2354 rc = sendReadResponsesMultiRanges(received);
2355 }
2356 if (rc) {
2357 // make sure readRangeHandler will trigger close
2358 // of file after next NextReadList().
2359 readRangeHandler.NotifyError();
2360 }
2361
2362 return 0;
2363 } // end read or readv
2364
2365 } // switch reqstate
2366 break;
2367 } // case GET
2368
2369 case XrdHttpReq::rtPUT:
2370 {
2371 if (!fopened) {
2372 if (xrdresp != kXR_ok) {
2373 sendWebdavErrorMessage(xrdresp, xrderrcode, XrdHttpReq::rtPUT,
2374 kXR_open, etext, NULL, NULL, keepalive);
2375 return -1;
2376 }
2377
2378 getfhandle();
2379 fopened = true;
2380
2381 // We try to completely fill up our buffer before flushing
2382 prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2383
2384 if (sendcontinue) {
2385 prot->SendSimpleResp(100, NULL, NULL, 0, 0, keepalive);
2386 return 0;
2387 }
2388
2389 break;
2390 } else {
2391
2392
2393 // If we are here it's too late to send a proper error message...
2394 if (xrdresp == kXR_error) return -1;
2395
2396 if (ntohs(xrdreq.header.requestid) == kXR_write) {
2397 int l = ntohl(xrdreq.write.dlen);
2398
2399 // Consume the written bytes
2400 prot->BuffConsume(ntohl(xrdreq.write.dlen));
2401 writtenbytes += l;
2402
2403 // Update the chunk offset
2404 if (m_transfer_encoding_chunked) {
2405 m_current_chunk_offset += l;
2406 }
2407
2408 // We try to completely fill up our buffer before flushing
2409 prot->ResumeBytes = std::min(length - writtenbytes, (long long) prot->BuffAvailable());
2410
2411 return 0;
2412 }
2413
2414 if (ntohs(xrdreq.header.requestid) == kXR_close) {
2415 if (xrdresp == kXR_ok) {
2416 prot->SendSimpleResp(201, NULL, NULL, (char *)":-)", 0, keepalive);
2417 return keepalive ? 1 : -1;
2418 } else {
2419 sendWebdavErrorMessage(xrdresp, xrderrcode, XrdHttpReq::rtPUT,
2420 kXR_close, etext, NULL, NULL, keepalive);
2421 return -1;
2422 }
2423 }
2424 }
2425
2426
2427
2428
2429
2430 break;
2431 }
2432
2433
2434
2436 {
2437
2438 if (xrdresp != kXR_ok) {
2439 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2440 httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2441 return -1;
2442 }
2443
2444
2445
2446
2447 switch (reqstate) {
2448
2449 case 0: // response to stat()
2450 {
2451 if (iovN > 0) {
2452
2453 // Now parse the stat info
2454 TRACEI(REQ, "Stat for removal " << resource.c_str()
2455 << " stat=" << (char *) iovP[0].iov_base);
2456
2457 long dummyl;
2458 sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2459 &dummyl,
2460 &filesize,
2461 &fileflags,
2462 &filemodtime);
2463 }
2464
2465 return 0;
2466 }
2467 default: // response to rm
2468 {
2469 if (xrdresp == kXR_ok) {
2470 prot->SendSimpleResp(200, NULL, NULL, (char *) ":-)", 0, keepalive);
2471 return keepalive ? 1 : -1;
2472 }
2473 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2474 httpErrorBody.c_str(), httpErrorBody.length(), keepalive);
2475 return -1;
2476 }
2477 }
2478
2479
2480 }
2481
2483 {
2484
2485 if (xrdresp == kXR_error) {
2486 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2487 httpErrorBody.c_str(), httpErrorBody.length(), false);
2488 return -1;
2489 }
2490
2491 switch (reqstate) {
2492
2493 case 0: // response to stat()
2494 {
2495 DirListInfo e;
2496 e.size = 0;
2497 e.flags = 0;
2498
2499 // Now parse the answer building the entries vector
2500 if (iovN > 0) {
2501 e.path = resource.c_str();
2502
2503 // Now parse the stat info
2504 TRACEI(REQ, "Collection " << resource.c_str()
2505 << " stat=" << (char *) iovP[0].iov_base);
2506
2507 long dummyl;
2508 sscanf((const char *) iovP[0].iov_base, "%ld %lld %ld %ld",
2509 &dummyl,
2510 &e.size,
2511 &e.flags,
2512 &e.modtime);
2513
2514 if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2515 /* The entry is filled. */
2516
2517
2518 std::string p;
2519 stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2520
2521 char *estr = escapeXML(e.path.c_str());
2522
2523 stringresp += "<D:href>";
2524 stringresp += estr;
2525 stringresp += "</D:href>\n";
2526
2527 free(estr);
2528
2529 stringresp += "<D:propstat>\n<D:prop>\n";
2530
2531 // Now add the properties that we have to add
2532
2533 // File size
2534 stringresp += "<lp1:getcontentlength>";
2535 stringresp += itos(e.size);
2536 stringresp += "</lp1:getcontentlength>\n";
2537
2538
2539
2540 stringresp += "<lp1:getlastmodified>";
2542 stringresp += "</lp1:getlastmodified>\n";
2543
2544
2545
2546 if (e.flags & kXR_isDir) {
2547 stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2548 stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2549 } else {
2550 stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2551 }
2552
2553 if (e.flags & kXR_xset) {
2554 stringresp += "<lp1:executable>T</lp1:executable>\n";
2555 stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2556 } else {
2557 stringresp += "<lp1:executable>F</lp1:executable>\n";
2558 }
2559
2560
2561
2562 stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2563
2564
2565 }
2566
2567
2568 }
2569
2570 // If this was the last bunch of entries, send the buffer and empty it immediately
2571 if ((depth == 0) || !(e.flags & kXR_isDir)) {
2572 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";
2573 stringresp.insert(0, s);
2574 stringresp += "</D:multistatus>\n";
2575 prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2576 (char *) stringresp.c_str(), stringresp.length(), keepalive);
2577 stringresp.clear();
2578 return keepalive ? 1 : -1;
2579 }
2580
2581 break;
2582 }
2583 default: // response to dirlist()
2584 {
2585
2586
2587 // Now parse the answer building the entries vector
2588 if (iovN > 0) {
2589 char *startp = (char *) iovP[0].iov_base, *endp = 0;
2590 char entry[1024];
2591 DirListInfo e;
2592
2593 while ( (size_t)(startp - (char *) iovP[0].iov_base) < (size_t)(iovP[0].iov_len - 1) ) {
2594 // Find the filename, it comes before the \n
2595 if ((endp = (char *) mystrchrnul((const char*) startp, '\n'))) {
2596 strncpy(entry, (char *) startp, endp - startp);
2597 entry[endp - startp] = 0;
2598 e.path = entry;
2599
2600 endp++;
2601
2602 // Now parse the stat info
2603 TRACEI(REQ, "Dirlist " <<resource.c_str() <<" entry=" <<entry
2604 << " stat=" << endp);
2605
2606 long dummyl;
2607 sscanf(endp, "%ld %lld %ld %ld",
2608 &dummyl,
2609 &e.size,
2610 &e.flags,
2611 &e.modtime);
2612 }
2613
2614
2615 if (e.path.length() && (e.path != ".") && (e.path != "..")) {
2616 /* The entry is filled.
2617
2618 <D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/" xmlns:lp3="LCGDM:">
2619 <D:href>/dpm/cern.ch/home/testers2.eu-emi.eu/</D:href>
2620 <D:propstat>
2621 <D:prop>
2622 <lp1:getcontentlength>1</lp1:getcontentlength>
2623 <lp1:getlastmodified>Tue, 01 May 2012 02:42:13 GMT</lp1:getlastmodified>
2624 <lp1:resourcetype>
2625 <D:collection/>
2626 </lp1:resourcetype>
2627 </D:prop>
2628 <D:status>HTTP/1.1 200 OK</D:status>
2629 </D:propstat>
2630 </D:response>
2631 */
2632
2633
2634 std::string p = resource.c_str();
2635 if (*p.rbegin() != '/') p += "/";
2636
2637 p += e.path;
2638
2639 stringresp += "<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2640
2641 char *estr = escapeXML(p.c_str());
2642 stringresp += "<D:href>";
2643 stringresp += estr;
2644 stringresp += "</D:href>\n";
2645 free(estr);
2646
2647 stringresp += "<D:propstat>\n<D:prop>\n";
2648
2649
2650
2651 // Now add the properties that we have to add
2652
2653 // File size
2654 stringresp += "<lp1:getcontentlength>";
2655 stringresp += itos(e.size);
2656 stringresp += "</lp1:getcontentlength>\n";
2657
2658 stringresp += "<lp1:getlastmodified>";
2660 stringresp += "</lp1:getlastmodified>\n";
2661
2662 if (e.flags & kXR_isDir) {
2663 stringresp += "<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2664 stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2665 } else {
2666 stringresp += "<lp1:iscollection>0</lp1:iscollection>\n";
2667 }
2668
2669 if (e.flags & kXR_xset) {
2670 stringresp += "<lp1:executable>T</lp1:executable>\n";
2671 stringresp += "<lp1:iscollection>1</lp1:iscollection>\n";
2672 } else {
2673 stringresp += "<lp1:executable>F</lp1:executable>\n";
2674 }
2675
2676 stringresp += "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2677
2678
2679 }
2680
2681
2682
2683 if (endp) {
2684 char *pp = (char *)strchr((const char *)endp, '\n');
2685 if (pp) startp = pp+1;
2686 else break;
2687 } else break;
2688
2689 }
2690 }
2691
2692
2693
2694 // If this was the last bunch of entries, send the buffer and empty it immediately
2695 if (final_) {
2696 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";
2697 stringresp.insert(0, s);
2698 stringresp += "</D:multistatus>\n";
2699 prot->SendSimpleResp(207, (char *) "Multi-Status", (char *) "Content-Type: text/xml; charset=\"utf-8\"",
2700 (char *) stringresp.c_str(), stringresp.length(), keepalive);
2701 stringresp.clear();
2702 return keepalive ? 1 : -1;
2703 }
2704
2705 break;
2706 } // default reqstate
2707 } // switch reqstate
2708
2709
2710 break;
2711
2712 } // case propfind
2713
2715 {
2716
2717 if (xrdresp != kXR_ok) {
2718 if (xrderrcode == kXR_ItExists) {
2719 prot->SendSimpleResp(405, NULL, NULL, (char *) "Method is not allowed; resource already exists.", 0, false);
2720 } else {
2721 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2722 httpErrorBody.c_str(), httpErrorBody.length(), false);
2723 }
2724 return -1;
2725 }
2726
2727 prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2728 return keepalive ? 1 : -1;
2729
2730 }
2731 case XrdHttpReq::rtMOVE:
2732 {
2733
2734 if (xrdresp != kXR_ok) {
2735 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (char *) etext.c_str(), 0, false);
2736 return -1;
2737 }
2738
2739 prot->SendSimpleResp(201, NULL, NULL, (char *) ":-)", 0, keepalive);
2740 return keepalive ? 1 : -1;
2741
2742 }
2743
2744 default:
2745 break;
2746
2747 }
2748
2749
2750 switch (xrdresp) {
2751 case kXR_error:
2752 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2753 httpErrorBody.c_str(), httpErrorBody.length(), false);
2754 return -1;
2755 break;
2756
2757 default:
2758
2759 break;
2760 }
2761
2762
2763 return 0;
2764}
2765
2766int
2767XrdHttpReq::sendFooterError(const std::string &extra_text) {
2768 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2769 // A trailer header is appropriate in this case; this is signified by
2770 // a chunk with size zero, then the trailer, then a crlf.
2771 //
2772 // We only send the status trailer when explicitly requested; otherwise a
2773 // "normal" HTTP client might simply see a short response and think it's a
2774 // success
2775
2776 if (prot->ChunkRespHeader(0))
2777 return -1;
2778
2779 std::stringstream ss;
2780
2781 ss << httpStatusCode;
2782 if (!httpErrorBody.empty()) {
2783 std::string_view statusView(httpErrorBody);
2784 // Remove trailing newline; this is not valid in a trailer value
2785 // and causes incorrect framing of the response, confusing clients.
2786 if (statusView[statusView.size() - 1] == '\n') {
2787 ss << ": " << statusView.substr(0, statusView.size() - 1);
2788 } else {
2789 ss << ": " << httpErrorBody;
2790 }
2791 }
2792
2793 if (!extra_text.empty())
2794 ss << ": " << extra_text;
2795 TRACEI(REQ, ss.str());
2796 ss << "\r\n";
2797
2798 const auto header = "X-Transfer-Status: " + ss.str();
2799 if (prot->SendData(header.c_str(), header.size()))
2800 return -1;
2801
2802 if (prot->ChunkRespFooter())
2803 return -1;
2804
2805 return keepalive ? 1 : -1;
2806 } else {
2807 TRACEI(REQ, "Failure during response: " << httpStatusCode << ": " << httpErrorBody << (extra_text.empty() ? "" : (": " + extra_text)));
2808 return -1;
2809
2810 }
2811}
2812
2813void XrdHttpReq::addAgeHeader(std::string &headers) {
2814 long object_age = time(NULL) - filemodtime;
2815 headers += std::string("Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2816}
2817
2819
2820 TRACE(REQ, " XrdHttpReq request ended.");
2821
2822 //if (xmlbody) xmlFreeDoc(xmlbody);
2823 readRangeHandler.reset();
2824 readClosing = false;
2825 closeAfterError = false;
2826 writtenbytes = 0;
2827 etext.clear();
2828 redirdest = "";
2829
2830 // // Here we should deallocate this
2831 // const struct iovec *iovP //!< pointer to data array
2832 // int iovN, //!< array count
2833 // int iovL, //!< byte count
2834 // bool final //!< true -> final result
2835
2836
2837 //xmlbody = 0;
2838 depth = 0;
2841 ralist.clear();
2842 ralist.shrink_to_fit();
2843
2844 request = rtUnset;
2845 resource = "";
2846 allheaders.clear();
2847
2848 // Reset the state of the request's digest request.
2849 m_req_digest.clear();
2850 m_digest_header.clear();
2851 m_req_cksum = nullptr;
2852
2854 m_user_agent = "";
2855 m_origin = "";
2856
2857 headerok = false;
2858 keepalive = true;
2859 length = 0;
2860 filesize = 0;
2861 depth = 0;
2862 sendcontinue = false;
2863
2864 m_transfer_encoding_chunked = false;
2865 m_current_chunk_size = -1;
2866 m_current_chunk_offset = 0;
2867
2868 m_trailer_headers = false;
2869 m_status_trailer = false;
2870
2872 reqstate = 0;
2873
2874 memset(&xrdreq, 0, sizeof (xrdreq));
2875 memset(&xrdresp, 0, sizeof (xrdresp));
2877
2878 etext.clear();
2879 redirdest = "";
2880
2881 stringresp = "";
2882
2883 host = "";
2884 destination = "";
2885 hdr2cgistr = "";
2886 m_appended_hdr2cgistr = false;
2887 m_appended_asize = false;
2888
2889 iovP = 0;
2890 iovN = 0;
2891 iovL = 0;
2892
2893
2894 if (opaque) delete(opaque);
2895 opaque = 0;
2896
2897 fopened = false;
2898
2899 final = false;
2900
2901 mScitag = -1;
2902}
2903
2904void XrdHttpReq::getfhandle() {
2905
2906 memcpy(fhandle, iovP[0].iov_base, 4);
2907 TRACEI(REQ, "fhandle:" <<
2908 (int) fhandle[0] << ":" << (int) fhandle[1] << ":" << (int) fhandle[2] << ":" << (int) fhandle[3]);
2909
2910}
2911
2912void XrdHttpReq::getReadResponse(XrdHttpIOList &received) {
2913 received.clear();
2914
2915 if (ntohs(xrdreq.header.requestid) == kXR_readv) {
2916 readahead_list *l;
2917 char *p;
2918 kXR_int32 len;
2919
2920 // Cycle on all the data that is coming from the server
2921 for (int i = 0; i < iovN; i++) {
2922
2923 for (p = (char *) iovP[i].iov_base; p < (char *) iovP[i].iov_base + iovP[i].iov_len;) {
2924 l = (readahead_list *) p;
2925 len = ntohl(l->rlen);
2926
2927 received.emplace_back(p+sizeof(readahead_list), -1, len);
2928
2929 p += sizeof (readahead_list);
2930 p += len;
2931
2932 }
2933 }
2934 return;
2935 }
2936
2937 // kXR_read result
2938 for (int i = 0; i < iovN; i++) {
2939 received.emplace_back((char*)iovP[i].iov_base, -1, iovP[i].iov_len);
2940 }
2941
2942}
2943
2944int XrdHttpReq::sendReadResponsesMultiRanges(const XrdHttpIOList &received) {
2945
2946 if (received.size() == 0) {
2947 bool start, finish;
2948 if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
2949 return -1;
2950 }
2951 return 0;
2952 }
2953
2954 // user is expecting multiple ranges, we must be prepared to send an
2955 // individual header for each and format it according to the http rules
2956
2957 struct rinfo {
2958 bool start;
2959 bool finish;
2960 const XrdOucIOVec2 *ci;
2961 const XrdHttpReadRangeHandler::UserRange *ur;
2962 std::string st_header;
2963 std::string fin_header;
2964 };
2965
2966 // report each received byte chunk to the range handler and record the details
2967 // of original user range it related to and if starts a range or finishes all.
2968 // also sum the total of the headers and data which need to be sent to the user,
2969 // in case we need it for chunked transfer encoding
2970 std::vector<rinfo> rvec;
2971 off_t sum_len = 0;
2972
2973 rvec.reserve(received.size());
2974
2975 for(const auto &rcv: received) {
2976 rinfo rentry;
2977 bool start, finish;
2978 const XrdHttpReadRangeHandler::UserRange *ur;
2979
2980 if (readRangeHandler.NotifyReadResult(rcv.size, &ur, start, finish) < 0) {
2981 return -1;
2982 }
2983 rentry.ur = ur;
2984 rentry.start = start;
2985 rentry.finish = finish;
2986 rentry.ci = &rcv;
2987
2988 if (start) {
2989 std::string s = buildPartialHdr(ur->start,
2990 ur->end,
2991 filesize,
2992 (char *) "123456");
2993
2994 rentry.st_header = s;
2995 sum_len += s.size();
2996 }
2997
2998 sum_len += rcv.size;
2999
3000 if (finish) {
3001 std::string s = buildPartialHdrEnd((char *) "123456");
3002 rentry.fin_header = s;
3003 sum_len += s.size();
3004 }
3005
3006 rvec.push_back(rentry);
3007 }
3008
3009
3010 // Send chunked encoding header
3011 if (m_transfer_encoding_chunked && m_trailer_headers) {
3012 prot->ChunkRespHeader(sum_len);
3013 }
3014
3015 // send the user the headers / data
3016 for(const auto &rentry: rvec) {
3017
3018 if (rentry.start) {
3019 TRACEI(REQ, "Sending multipart: " << rentry.ur->start << "-" << rentry.ur->end);
3020 if (prot->SendData((char *) rentry.st_header.c_str(), rentry.st_header.size())) {
3021 return -1;
3022 }
3023 }
3024
3025 // Send all the data we have
3026 if (prot->SendData((char *) rentry.ci->data, rentry.ci->size)) {
3027 return -1;
3028 }
3029
3030 if (rentry.finish) {
3031 if (prot->SendData((char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
3032 return -1;
3033 }
3034 }
3035 }
3036
3037 // Send chunked encoding footer
3038 if (m_transfer_encoding_chunked && m_trailer_headers) {
3039 prot->ChunkRespFooter();
3040 }
3041
3042 return 0;
3043}
3044
3045int XrdHttpReq::sendReadResponseSingleRange(const XrdHttpIOList &received) {
3046 // single range http transfer
3047
3048 if (received.size() == 0) {
3049 bool start, finish;
3050 if (readRangeHandler.NotifyReadResult(0, nullptr, start, finish) < 0) {
3051 return -1;
3052 }
3053 return 0;
3054 }
3055
3056 off_t sum = 0;
3057 // notify the range handler and return if error
3058 for(const auto &rcv: received) {
3059 bool start, finish;
3060 if (readRangeHandler.NotifyReadResult(rcv.size, nullptr, start, finish) < 0) {
3061 return -1;
3062 }
3063 sum += rcv.size;
3064 }
3065
3066 // Send chunked encoding header
3067 if (m_transfer_encoding_chunked && m_trailer_headers) {
3068 prot->ChunkRespHeader(sum);
3069 }
3070 for(const auto &rcv: received) {
3071 if (prot->SendData((char *) rcv.data, rcv.size)) return -1;
3072 }
3073 if (m_transfer_encoding_chunked && m_trailer_headers) {
3074 prot->ChunkRespFooter();
3075 }
3076 return 0;
3077}
XErrorCode
Definition XProtocol.hh:989
@ kXR_InvalidRequest
Definition XProtocol.hh:996
@ kXR_TimerExpired
@ kXR_ItExists
@ kXR_AuthFailed
@ kXR_NotAuthorized
@ kXR_NotFound
@ kXR_FileLocked
Definition XProtocol.hh:993
@ kXR_noErrorYet
@ kXR_overQuota
@ kXR_NoSpace
Definition XProtocol.hh:999
@ kXR_isDirectory
@ kXR_Unsupported
@ kXR_noserver
#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
XResponseType
Definition XProtocol.hh:898
@ kXR_noResponsesYet
Definition XProtocol.hh:908
@ kXR_ok
Definition XProtocol.hh:899
@ kXR_error
Definition XProtocol.hh:903
@ kXR_dstat
Definition XProtocol.hh:240
XRequestTypes
Definition XProtocol.hh:110
@ 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)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
Utility functions for XrdHTTP.
std::string encode_opaque(const std::string &opaque)
std::string encode_str(const std::string &str)
std::vector< XrdOucIOVec2 > XrdHttpIOList
std::string decode_str(const std::string &str)
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
ReqType
These are the HTTP/DAV requests that we support.
Definition XrdHttpReq.hh:81
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)