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