39 #include "XrdVersion.hh"
44 #include <arpa/inet.h>
65 #define MAX_TK_LEN 256
66 #define MAX_RESOURCE_LEN 16384
69 #define TRACELINK prot->Link
73 const char *TraceID =
"Req";
76 void trim(std::string &str)
86 memset(&t1, 0,
sizeof (t1));
89 strftime(datebuf, 127,
"%a, %d %b %Y %H:%M:%S GMT", &t1);
90 return (std::string) datebuf;
122 if (!line)
return -1;
125 char *p = strchr((
char *) line, (
int)
':');
141 char *val = line + pos + 1;
144 while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
148 std::string ss = val;
149 if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) !=
"\r\n") {
162 if (!strcasecmp(key,
"connection")) {
164 if (!strcasecmp(val,
"Keep-Alive\r\n")) {
166 }
else if (!strcasecmp(val,
"close\r\n")) {
170 }
else if (!strcasecmp(key,
"host")) {
172 }
else if (!strcasecmp(key,
"range")) {
177 }
else if (!strcasecmp(key,
"content-length")) {
180 }
else if (!strcasecmp(key,
"destination")) {
183 }
else if (!strcasecmp(key,
"want-digest")) {
188 }
else if (!strcasecmp(key,
"depth")) {
190 if (strcmp(val,
"infinity"))
193 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
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")) {
206 }
else if (!strcasecmp(key,
"user-agent")) {
211 auto it = std::find_if(prot->
hdr2cgimap.begin(), prot->
hdr2cgimap.end(),[key](
const auto & item) {
212 return !strcasecmp(key,item.first.c_str());
216 s.assign(val, line+len-val);
229 int XrdHttpReq::parseHost(
char *line) {
235 void XrdHttpReq::parseScitag(
const std::string & val) {
239 std::string scitagS = val;
242 if(scitagS[0] !=
'-') {
244 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
257 addCgi(
"pmark.appname",this->
request == ReqType::rtGET ?
"http-get" :
"http-put");
268 if (!line)
return -1;
271 char *p = strchr((
char *) line, (
int)
' ');
295 char *val = line + pos + 1;
302 p = strchr((
char *) val, (
int)
' ');
316 if (!strcmp(key,
"GET")) {
318 }
else if (!strcmp(key,
"HEAD")) {
320 }
else if (!strcmp(key,
"PUT")) {
322 }
else if (!strcmp(key,
"POST")) {
324 }
else if (!strcmp(key,
"PATCH")) {
326 }
else if (!strcmp(key,
"OPTIONS")) {
328 }
else if (!strcmp(key,
"DELETE")) {
330 }
else if (!strcmp(key,
"PROPFIND")) {
333 }
else if (!strcmp(key,
"MKCOL")) {
336 }
else if (!strcmp(key,
"MOVE")) {
346 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
360 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
367 for (
int i = 0; i < nitems; i++) {
378 void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
385 for (
int i = 0; i < nitems; i++) {
403 for (
const auto &c: cl) {
406 memcpy(&ra.fhandle, this->fhandle, 4);
408 ra.offset = c.offset;
422 clientMarshallReadAheadList(j);
431 std::ostringstream s;
433 s <<
"\r\n--" << token <<
"\r\n";
434 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
435 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
441 std::ostringstream s;
443 s <<
"\r\n--" << token <<
"--\r\n";
456 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
462 this->
final = final_;
464 if (PostProcessHTTPReq(final_))
reset();
478 int rc = info.
Send(0, 0, 0, 0);
479 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
496 TRACE(REQ,
" XrdHttpReq::Done");
502 int r = PostProcessHTTPReq(
true);
505 if (r < 0)
return false;
516 TRACE(REQ,
" XrdHttpReq::Error");
527 auto rc = PostProcessHTTPReq();
538 return rc == 0 ? true :
false;
560 if (strncmp(hname,
"file://", 7) == 0)
562 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
569 char *pp = strchr((
char *)hname,
'?');
575 int varlen = strlen(vardata);
578 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
587 sprintf(buf,
":%d", port);
631 return ret_keepalive;
642 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
661 s +=
"&xrdhttptime=";
663 sprintf(buf,
"%lld", (
long long) tnow);
668 s +=
"&xrdhttpname=";
674 s +=
"&xrdhttpvorg=";
679 s +=
"&xrdhttphost=";
689 s +=
"&xrdhttprole=";
694 s +=
"&xrdhttpgrps=";
699 s +=
"&xrdhttpendorsements=";
704 s +=
"&xrdhttpcredslen=";
706 sprintf(buf,
"%d", secent->
credslen);
712 s +=
"&xrdhttpcreds=";
726 void XrdHttpReq::sanitizeResourcePfx() {
757 void XrdHttpReq::parseResource(
char *res) {
763 char *p = strchr(res,
'?');
771 sanitizeResourcePfx();
796 sanitizeResourcePfx();
824 void XrdHttpReq::mapXrdErrorToHttpStatus() {
826 httpStatusCode = 500;
827 httpStatusText =
"Unrecognized error";
833 httpStatusCode = 401; httpStatusText =
"Unauthorized";
836 httpStatusCode = 403; httpStatusText =
"Operation not permitted";
839 httpStatusCode = 404; httpStatusText =
"File not found";
842 httpStatusCode = 405; httpStatusText =
"Operation not supported";
845 httpStatusCode = 423; httpStatusText =
"Resource is a locked";
848 httpStatusCode = 409; httpStatusText =
"Resource is a directory";
851 if(
request != ReqType::rtDELETE) {
852 httpStatusCode = 409; httpStatusText =
"File already exists";
857 httpStatusCode = 405;
861 httpStatusCode = 405; httpStatusText =
"Method is not allowed";
864 httpStatusCode = 502; httpStatusText =
"Bad Gateway";
867 httpStatusCode = 504; httpStatusText =
"Gateway timeout";
876 <<
"] to status code [" << httpStatusCode <<
"]");
878 httpStatusText +=
"\n";
880 httpStatusCode = 200;
881 httpStatusText =
"OK";
893 int query_param_status = 0;
897 if (query_param_status == 0) {
901 query_param_status = 1;
902 auto length_str = std::to_string(
length);
908 opaque->
Put(
"oss.asize", length_str.c_str());
914 if (query_param_status == 0) {
926 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
927 << header2cgistrObf.c_str() <<
"'");
947 if (r < 0)
return -1;
961 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
967 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
976 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
988 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1000 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1024 if (
resource ==
"/static/css/xrdhttp.css") {
1025 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1029 if (
resource ==
"/static/icons/xrdhttp.ico") {
1030 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1050 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1060 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1092 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1110 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1122 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1127 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1138 mapXrdErrorToHttpStatus();
1139 return sendFooterError(
"Could not run close request on the bridge");
1149 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1163 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1174 l = res.length() + 1;
1178 mapXrdErrorToHttpStatus();
1179 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1180 return sendFooterError(
"Could not run listing request on the bridge");
1192 auto retval = ReturnGetHeaders();
1213 TRACEI(REQ,
" Failed to run close request on the bridge.");
1227 if ( readChunkList.size() == 1 ) {
1239 offs = readChunkList[0].offset;
1240 l = readChunkList[0].size;
1248 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1251 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1260 TRACE(ALL,
" Data sizes mismatch.");
1264 TRACE(ALL,
" No more bytes to send.");
1271 httpStatusCode = 416;
1272 httpStatusText =
"Range Not Satisfiable";
1273 std::stringstream ss;
1274 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1275 return sendFooterError(ss.str());
1279 mapXrdErrorToHttpStatus();
1280 return sendFooterError(
"Could not run read request on the bridge");
1288 mapXrdErrorToHttpStatus();
1289 return sendFooterError(
"Could not run ReadV request on the bridge");
1318 if (! XrdHttpProtocol::usingEC)
1324 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1339 if (m_transfer_encoding_chunked) {
1340 if (m_current_chunk_size == m_current_chunk_offset) {
1343 if (prot->BuffUsed() < 2)
return 1;
1344 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1345 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1348 prot->BuffConsume(2);
1349 if (m_current_chunk_size == 0) {
1353 m_transfer_encoding_chunked =
false;
1357 m_current_chunk_size = -1;
1358 m_current_chunk_offset = 0;
1360 if (!prot->BuffUsed())
return 1;
1362 if (-1 == m_current_chunk_size) {
1366 bool found_newline =
false;
1373 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1374 for (; idx < max_chunk_size_chars; idx++) {
1375 if (prot->myBuffStart[idx] ==
'\n') {
1376 found_newline =
true;
1382 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1383 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1384 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1387 if (found_newline) {
1388 char *endptr = NULL;
1389 std::string line_contents(prot->myBuffStart, idx);
1390 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1392 if (*endptr !=
';' && *endptr !=
'\r') {
1393 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1394 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1397 m_current_chunk_size = chunk_contents;
1398 m_current_chunk_offset = 0;
1399 prot->BuffConsume(idx + 1);
1400 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1407 if (m_current_chunk_size == 0) {
1416 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1417 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1418 chunk_bytes_remaining);
1423 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1424 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1425 mapXrdErrorToHttpStatus();
1426 return sendFooterError(
"Could not run write request on the bridge");
1430 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1440 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1446 TRACEI(REQ,
"Writing " << bytes_to_read);
1447 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1448 mapXrdErrorToHttpStatus();
1449 return sendFooterError(
"Could not run write request on the bridge");
1471 mapXrdErrorToHttpStatus();
1472 return sendFooterError(
"Could not run close request on the bridge");
1487 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);
1490 return ret_keepalive ? 1 : -1;
1512 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1532 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1546 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1562 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1577 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1582 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1587 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1606 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1638 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1664 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1685 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1690 strcpy(buf2,
host.c_str());
1691 char *pos = strchr(buf2,
':');
1692 if (pos) *pos =
'\0';
1700 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1714 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1724 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1735 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1738 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1743 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1744 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1747 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1748 if (convert_to_base64) {
1749 size_t digest_length = strlen(digest_value);
1750 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1751 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1752 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1753 free(digest_binary_value);
1756 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1758 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1759 free(digest_binary_value);
1760 digest_value = digest_base64_value;
1763 digest_header =
"Digest: ";
1765 digest_header +=
"=";
1766 digest_header += digest_value;
1767 if (convert_to_base64) {free(digest_value);}
1770 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1776 XrdHttpReq::PostProcessListing(
bool final_) {
1779 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1780 httpStatusText.c_str(), httpStatusText.length(),
false);
1786 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1787 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1789 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1790 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1791 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1812 "<th class=\"mode\">Mode</th>"
1813 "<th class=\"flags\">Flags</th>"
1814 "<th class=\"size\">Size</th>"
1815 "<th class=\"datetime\">Modified</th>"
1816 "<th class=\"name\">Name</th>"
1822 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1825 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1827 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1828 strncpy(entry, (
char *) startp, endp - startp);
1829 entry[endp - startp] = 0;
1836 <<
" stat=" << endp);
1839 sscanf(endp,
"%ld %lld %ld %ld",
1845 strcpy(entry, (
char *) startp);
1847 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1849 std::string p =
"<tr>"
1850 "<td class=\"mode\">";
1871 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1872 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1874 "<td class=\"name\">"
1882 if (!p.empty() && p[p.size() - 1] !=
'/')
1887 std::unique_ptr<char, decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1893 p +=
"</a></td></tr>";
1899 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1900 if (pp) startp = pp+1;
1909 stringresp +=
"</table></div><br><br><hr size=1>"
1910 "<p><span id=\"requestby\">Request by ";
1972 XrdHttpReq::ReturnGetHeaders() {
1973 std::string responseHeader;
1978 if (!responseHeader.empty()) {
1979 responseHeader +=
"\r\n";
1981 addAgeHeader(responseHeader);
1993 if (m_transfer_encoding_chunked && m_trailer_headers) {
1995 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
1997 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2005 if (uranges.size() != 1)
2010 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2012 std::string header =
"Content-Range: bytes ";
2013 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2015 if (!responseHeader.empty()) {
2017 header += responseHeader.c_str();
2020 if (m_transfer_encoding_chunked && m_trailer_headers) {
2022 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
2024 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
2031 for (
auto &ur : uranges) {
2032 cnt += ur.end - ur.start + 1;
2037 (
char *)
"123456").size();
2041 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2047 if (!header.empty()) {
2050 addAgeHeader(header);
2053 if (m_transfer_encoding_chunked && m_trailer_headers) {
2055 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2057 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2063 if (m_status_trailer) {
2064 if (header.empty()) {
2065 header +=
"Trailer: X-Transfer-Status";
2067 header +=
"\r\nTrailer: X-Transfer-Status";
2074 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2076 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2077 mapXrdErrorToHttpStatus();
2082 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2091 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2096 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2103 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2107 std::string response_headers;
2111 <<
" stat=" << (
char *)
iovP[0].iov_base);
2114 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2124 addAgeHeader(response_headers);
2125 response_headers +=
"\r\n";
2127 response_headers +=
"Accept-Ranges: bytes";
2128 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2133 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2136 return ret_keepalive ? 1 : -1;
2139 std::string response_headers;
2140 int response = PostProcessChecksum(response_headers);
2141 if (-1 == response) {
2144 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2146 addAgeHeader(response_headers);
2147 response_headers +=
"\r\n";
2149 response_headers +=
"Accept-Ranges: bytes";
2150 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2153 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2175 if (
iovP[1].iov_len > 1) {
2177 <<
" stat=" << (
char *)
iovP[1].iov_base);
2180 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2201 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2202 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2211 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2212 httpStatusText.c_str(), httpStatusText.length(),
false);
2225 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2226 httpStatusText.c_str(), httpStatusText.length(),
false);
2233 return PostProcessListing(final_);
2243 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2250 httpStatusText = rrerror.
errMsg;
2253 if (m_transfer_encoding_chunked && m_trailer_headers) {
2254 if (prot->ChunkRespHeader(0))
2257 const std::string crlf =
"\r\n";
2258 std::stringstream ss;
2259 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2261 const auto header = ss.str();
2262 if (prot->SendData(header.c_str(), header.size()))
2265 if (prot->ChunkRespFooter())
2269 if (rrerror)
return -1;
2276 auto rc = sendFooterError(
"");
2285 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2288 getReadResponse(received);
2292 rc = sendReadResponseSingleRange(received);
2294 rc = sendReadResponsesMultiRanges(received);
2315 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2316 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2324 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2327 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2346 if (m_transfer_encoding_chunked) {
2347 m_current_chunk_offset += l;
2351 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2359 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2362 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2363 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2384 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2385 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2400 <<
" stat=" << (
char *)
iovP[0].iov_base);
2403 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2415 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2418 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2419 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2431 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2432 httpStatusText.c_str(), httpStatusText.length(),
false);
2450 <<
" stat=" << (
char *)
iovP[0].iov_base);
2453 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2459 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2464 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2492 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2493 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2496 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2500 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2501 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2503 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2508 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2518 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";
2521 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2535 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2539 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2541 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2542 strncpy(entry, (
char *) startp, endp - startp);
2543 entry[endp - startp] = 0;
2550 <<
" stat=" << endp);
2553 sscanf(endp,
"%ld %lld %ld %ld",
2561 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2581 if (*p.rbegin() !=
'/') p +=
"/";
2585 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2609 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2610 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2613 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2617 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2618 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2620 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2623 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2631 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2632 if (pp) startp = pp+1;
2643 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";
2646 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2666 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2668 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2669 httpStatusText.c_str(), httpStatusText.length(),
false);
2674 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2682 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2686 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2699 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2700 httpStatusText.c_str(), httpStatusText.length(),
false);
2714 XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2715 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2723 if (prot->ChunkRespHeader(0))
2726 std::stringstream ss;
2727 ss << httpStatusCode;
2728 if (!httpStatusText.empty()) {
2729 std::string_view statusView(httpStatusText);
2732 if (statusView[statusView.size() - 1] ==
'\n') {
2733 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2735 ss <<
": " << httpStatusText;
2738 if (!extra_text.empty())
2739 ss <<
": " << extra_text;
2743 const auto header =
"X-Transfer-Status: " + ss.str();
2744 if (prot->SendData(header.c_str(), header.size()))
2747 if (prot->ChunkRespFooter())
2752 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpStatusText << (extra_text.empty() ?
"" : (
": " + extra_text)));
2757 void XrdHttpReq::addAgeHeader(std::string &headers) {
2759 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2764 TRACE(REQ,
" XrdHttpReq request ended.");
2807 m_transfer_encoding_chunked =
false;
2808 m_current_chunk_size = -1;
2809 m_current_chunk_offset = 0;
2811 m_trailer_headers =
false;
2812 m_status_trailer =
false;
2847 void XrdHttpReq::getfhandle() {
2850 TRACEI(REQ,
"fhandle:" <<
2864 for (
int i = 0; i <
iovN; i++) {
2866 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2868 len = ntohl(l->
rlen);
2881 for (
int i = 0; i <
iovN; i++) {
2882 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2887 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2889 if (received.size() == 0) {
2905 std::string st_header;
2906 std::string fin_header;
2913 std::vector<rinfo> rvec;
2916 rvec.reserve(received.size());
2918 for(
const auto &rcv: received) {
2927 rentry.start = start;
2928 rentry.finish = finish;
2937 rentry.st_header = s;
2938 sum_len += s.size();
2941 sum_len += rcv.size;
2945 rentry.fin_header = s;
2946 sum_len += s.size();
2949 rvec.push_back(rentry);
2954 if (m_transfer_encoding_chunked && m_trailer_headers) {
2955 prot->ChunkRespHeader(sum_len);
2959 for(
const auto &rentry: rvec) {
2962 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2963 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2969 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2973 if (rentry.finish) {
2974 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2981 if (m_transfer_encoding_chunked && m_trailer_headers) {
2982 prot->ChunkRespFooter();
2988 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2991 if (received.size() == 0) {
3001 for(
const auto &rcv: received) {
3010 if (m_transfer_encoding_chunked && m_trailer_headers) {
3011 prot->ChunkRespHeader(sum);
3013 for(
const auto &rcv: received) {
3014 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3016 if (m_transfer_encoding_chunked && m_trailer_headers) {
3017 prot->ChunkRespFooter();
struct ClientCloseRequest close
struct ClientSetRequest set
struct ClientMkdirRequest mkdir
struct ClientDirlistRequest dirlist
struct ClientReadVRequest readv
struct ClientOpenRequest open
struct ClientRequestHdr header
struct ClientRmRequest rm
struct ClientReadRequest read
struct ClientMvRequest mv
struct ClientRmdirRequest rmdir
struct ClientStatRequest stat
struct ClientWriteRequest write
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
std::string ISOdatetime(time_t t)
void trim(std::string &str)
Main request/response class, handling the logical status of the communication.
Static resources, here for performance and ease of setup.
int parseURL(char *url, char *host, int &port, char **path)
void Tobase64(const unsigned char *input, int length, char *out)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
Utility functions for XrdHTTP.
std::string encode_opaque(const std::string &opaque)
std::string encode_str(const std::string &str)
std::vector< XrdOucIOVec2 > XrdHttpIOList
std::string decode_str(const std::string &str)
std::string obfuscateAuth(const std::string &input)
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.
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.
std::vector< readahead_list > ralist
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 stringresp
If we want to give a string as a response, we compose it here.
XResponseType xrdresp
The last response data we got.
ReqType request
The request we got.
long long writtenbytes
In a long write, we track where we have arrived.
XrdOucEnv * opaque
The opaque data, after parsing.
const struct iovec * iovP
The latest data chunks got from the xrd layer. These are valid only inside the callbacks!
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.
int parseFirstLine(char *line, int len)
Parse the first line of the header.
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.
void setTransferStatusHeader(std::string &header)
bool m_appended_hdr2cgistr
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
bool m_appended_asize
Track whether we already appended the oss.asize argument for PUTs.
XrdOucString m_resource_with_digest
virtual bool Redir(XrdXrootd::Bridge::Context &info, int port, const char *hname)
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)
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 bool Error(XrdXrootd::Bridge::Context &info, int ecode, const char *etext)
char * ID
Pointer to the client's link identity.
static const int minTotID
static const int maxTotID
char * Get(const char *varname)
void Put(const char *varname, const char *value)
const char * c_str() const
void assign(const char *s, int j, int k=-1)
int erasefromstart(int sz=0)
int erasefromend(int sz=0)
int erase(int start=0, int size=0)
int find(const char c, int start=0, bool forward=1)
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