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