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