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