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