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