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