39 #include "XrdVersion.hh"
45 #include <arpa/inet.h>
67 #define MAX_TK_LEN 256
68 #define MAX_RESOURCE_LEN 16384
71 #define TRACELINK prot->Link
75 const char *TraceID =
"Req";
78 void trim(std::string &str)
88 memset(&t1, 0,
sizeof (t1));
91 strftime(datebuf, 127,
"%a, %d %b %Y %H:%M:%S GMT", &t1);
92 return (std::string) datebuf;
124 if (!line)
return -1;
127 char *p = strchr((
char *) line, (
int)
':');
143 char *val = line + pos + 1;
146 while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
150 std::string ss = val;
151 if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) !=
"\r\n") {
164 if (!strcasecmp(key,
"connection")) {
166 if (!strcasecmp(val,
"Keep-Alive\r\n")) {
168 }
else if (!strcasecmp(val,
"close\r\n")) {
172 }
else if (!strcasecmp(key,
"host")) {
174 }
else if (!strcasecmp(key,
"range")) {
179 }
else if (!strcasecmp(key,
"content-length")) {
182 }
else if (!strcasecmp(key,
"destination")) {
185 }
else if (!strcasecmp(key,
"want-digest")) {
192 }
else if (!strcasecmp(key,
"depth")) {
194 if (strcmp(val,
"infinity"))
197 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
199 }
else if (!strcasecmp(key,
"te") && strstr(val,
"trailers")) {
200 m_trailer_headers =
true;
201 }
else if (!strcasecmp(key,
"transfer-encoding") && strstr(val,
"chunked")) {
202 m_transfer_encoding_chunked =
true;
203 }
else if (!strcasecmp(key,
"x-transfer-status") && strstr(val,
"true")) {
204 m_transfer_encoding_chunked =
true;
205 m_status_trailer =
true;
206 }
else if (!strcasecmp(key,
"scitag")) {
210 }
else if (!strcasecmp(key,
"user-agent")) {
213 }
else if (!strcasecmp(key,
"origin")) {
216 }
else if (!strcasecmp(key,
"repr-digest")) {
218 }
else if (!strcasecmp(key,
"want-repr-digest")) {
225 auto it = std::find_if(prot->
hdr2cgimap.begin(), prot->
hdr2cgimap.end(),[key](
const auto & item) {
226 return !strcasecmp(key,item.first.c_str());
230 s.assign(val, line+len-val);
243 int XrdHttpReq::parseHost(
char *line) {
249 void XrdHttpReq::parseScitag(
const std::string & val) {
253 std::string scitagS = val;
256 if(scitagS[0] !=
'-') {
258 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
271 addCgi(
"pmark.appname",this->
request == ReqType::rtGET ?
"http-get" :
"http-put");
282 if (!line)
return -1;
285 char *p = strchr((
char *) line, (
int)
' ');
309 char *val = line + pos + 1;
316 p = strchr((
char *) val, (
int)
' ');
330 if (!strcmp(key,
"GET")) {
332 }
else if (!strcmp(key,
"HEAD")) {
334 }
else if (!strcmp(key,
"PUT")) {
336 }
else if (!strcmp(key,
"POST")) {
338 }
else if (!strcmp(key,
"PATCH")) {
340 }
else if (!strcmp(key,
"OPTIONS")) {
342 }
else if (!strcmp(key,
"DELETE")) {
344 }
else if (!strcmp(key,
"PROPFIND")) {
346 }
else if (!strcmp(key,
"MKCOL")) {
348 }
else if (!strcmp(key,
"MOVE")) {
350 }
else if (!strcmp(key,
"COPY")) {
360 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
374 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
381 for (
int i = 0; i < nitems; i++) {
392 void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
399 for (
int i = 0; i < nitems; i++) {
417 for (
const auto &c: cl) {
420 memcpy(&ra.fhandle, this->fhandle, 4);
422 ra.offset = c.offset;
436 clientMarshallReadAheadList(j);
445 std::ostringstream s;
447 s <<
"\r\n--" << token <<
"\r\n";
448 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
449 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
455 std::ostringstream s;
457 s <<
"\r\n--" << token <<
"--\r\n";
470 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
476 this->
final = final_;
478 if (PostProcessHTTPReq(final_))
reset();
492 int rc = info.
Send(0, 0, 0, 0);
493 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
510 TRACE(REQ,
" XrdHttpReq::Done");
516 int r = PostProcessHTTPReq(
true);
519 if (r < 0)
return false;
530 TRACE(REQ,
" XrdHttpReq::Error");
541 auto rc = PostProcessHTTPReq();
574 if (strncmp(hname,
"file://", 7) == 0)
576 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
583 char *pp = strchr((
char *)hname,
'?');
589 int varlen = strlen(vardata);
592 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
601 sprintf(buf,
":%d", port);
648 return ret_keepalive;
659 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
678 s +=
"&xrdhttptime=";
680 sprintf(buf,
"%lld", (
long long) tnow);
685 s +=
"&xrdhttpname=";
691 s +=
"&xrdhttpvorg=";
696 s +=
"&xrdhttphost=";
706 s +=
"&xrdhttprole=";
711 s +=
"&xrdhttpgrps=";
716 s +=
"&xrdhttpendorsements=";
721 s +=
"&xrdhttpcredslen=";
723 sprintf(buf,
"%d", secent->
credslen);
729 s +=
"&xrdhttpcreds=";
743 void XrdHttpReq::sanitizeResourcePfx() {
774 void XrdHttpReq::parseResource(
char *res) {
780 char *p = strchr(res,
'?');
788 sanitizeResourcePfx();
813 sanitizeResourcePfx();
835 void XrdHttpReq::generateWebdavErrMsg() {
841 httpStatusCode = 200;
842 httpErrorBody =
"OK";
848 httpErrorBody =
etext +
"\n";
871 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
874 outResourceDigestOpaque += !
opaque ?
"?" :
"&";
875 outResourceDigestOpaque +=
"cks.type=";
884 if (
startTime == std::chrono::steady_clock::time_point::min())
startTime = std::chrono::steady_clock::now();
890 int query_param_status = 0;
894 if (query_param_status == 0) {
898 query_param_status = 1;
899 auto length_str = std::to_string(
length);
905 opaque->
Put(
"oss.asize", length_str.c_str());
911 if (query_param_status == 0) {
923 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
924 << header2cgistrObf.c_str() <<
"'");
939 if (r < 0)
return -1;
954 generateWebdavErrMsg();
955 prot->SendSimpleResp(400, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
964 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
971 if(prepareCksum < 0) {
976 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1000 if (
resource ==
"/static/css/xrdhttp.css") {
1001 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1005 if (
resource ==
"/static/icons/xrdhttp.ico") {
1006 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1026 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1036 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1068 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1082 if(prepareCksum < 0) {
1086 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest or Want-Repr-Digest header.", 0,
false);
1091 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1102 generateWebdavErrMsg();
1103 return sendFooterError(
"Could not run close request on the bridge");
1114 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1128 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1139 l = res.length() + 1;
1143 generateWebdavErrMsg();
1144 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1145 sendFooterError(
"Could not run listing request on the bridge");
1158 auto retval = ReturnGetHeaders();
1179 TRACEI(REQ,
" Failed to run close request on the bridge.");
1193 if ( readChunkList.size() == 1 ) {
1205 offs = readChunkList[0].offset;
1206 l = readChunkList[0].size;
1214 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1217 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1226 TRACE(ALL,
" Data sizes mismatch.");
1230 TRACE(ALL,
" No more bytes to send.");
1237 httpStatusCode = 416;
1238 httpErrorBody =
"Range Not Satisfiable";
1239 std::stringstream ss;
1240 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1241 return sendFooterError(ss.str());
1245 generateWebdavErrMsg();
1246 return sendFooterError(
"Could not run read request on the bridge");
1254 generateWebdavErrMsg();
1255 return sendFooterError(
"Could not run ReadV request on the bridge");
1284 if (! XrdHttpProtocol::usingEC)
1290 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1305 if (m_transfer_encoding_chunked) {
1306 if (m_current_chunk_size == m_current_chunk_offset) {
1309 if (prot->BuffUsed() < 2)
return 1;
1310 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1311 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1314 prot->BuffConsume(2);
1315 if (m_current_chunk_size == 0) {
1319 m_transfer_encoding_chunked =
false;
1323 m_current_chunk_size = -1;
1324 m_current_chunk_offset = 0;
1326 if (!prot->BuffUsed())
return 1;
1328 if (-1 == m_current_chunk_size) {
1332 bool found_newline =
false;
1339 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1340 for (; idx < max_chunk_size_chars; idx++) {
1341 if (prot->myBuffStart[idx] ==
'\n') {
1342 found_newline =
true;
1348 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1349 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1350 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1353 if (found_newline) {
1354 char *endptr = NULL;
1355 std::string line_contents(prot->myBuffStart, idx);
1356 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1358 if (*endptr !=
';' && *endptr !=
'\r') {
1359 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1360 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1363 m_current_chunk_size = chunk_contents;
1364 m_current_chunk_offset = 0;
1365 prot->BuffConsume(idx + 1);
1366 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1373 if (m_current_chunk_size == 0) {
1382 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1383 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1384 chunk_bytes_remaining);
1389 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1390 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1391 generateWebdavErrMsg();
1392 return sendFooterError(
"Could not run write request on the bridge");
1396 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1406 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1412 TRACEI(REQ,
"Writing " << bytes_to_read);
1413 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1414 generateWebdavErrMsg();
1415 return sendFooterError(
"Could not run write request on the bridge");
1437 generateWebdavErrMsg();
1438 return sendFooterError(
"Could not run close request on the bridge");
1453 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);
1456 return ret_keepalive ? 1 : -1;
1478 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1498 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1512 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1528 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1543 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1548 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1553 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1572 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1604 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1630 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1641 skip = (skip == std::string::npos) ? 0 : skip + 3;
1645 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1656 size_t path_pos =
destination.find(
'/', skip + 1);
1658 if (path_pos == std::string::npos) {
1659 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Cannot determine destination path", 0,
false);
1666 l = mv_args.length() + 1;
1674 if (!prot->
Bridge->
Run((
char *) &
xrdreq, (
char *) mv_args.c_str(), l)) {
1675 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1684 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1695 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1698 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1703 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1704 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1706 std::string cksumType {
reinterpret_cast<char *
>(
iovP[0].iov_base),
iovP[0].iov_len};
1708 size_t cksumValueLen =
iovP[
iovN-1].iov_len - 1;
1709 std::string cksumValue {
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base), cksumValueLen};
1710 std::string digest_value = cksumValue;
1715 if (convert_to_base64) {
1716 std::vector<uint8_t> digest_binary_value;
1718 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1721 Tobase64(digest_binary_value,digest_value);
1725 digest_header =
"Digest: ";
1727 digest_header +=
"=";
1728 digest_header += digest_value;
1730 digest_header =
"Repr-Digest: ";
1732 digest_header +=
"=:";
1733 digest_header += digest_value;
1734 digest_header +=
":";
1739 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1745 XrdHttpReq::PostProcessListing(
bool final_) {
1748 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1749 httpErrorBody.c_str(), httpErrorBody.length(),
false);
1755 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1756 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1758 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1759 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1760 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1781 "<th class=\"mode\">Mode</th>"
1782 "<th class=\"flags\">Flags</th>"
1783 "<th class=\"size\">Size</th>"
1784 "<th class=\"datetime\">Modified</th>"
1785 "<th class=\"name\">Name</th>"
1791 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1794 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1796 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1797 strncpy(entry, (
char *) startp, endp - startp);
1798 entry[endp - startp] = 0;
1805 <<
" stat=" << endp);
1808 sscanf(endp,
"%ld %lld %ld %ld",
1814 strcpy(entry, (
char *) startp);
1816 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1818 std::string p =
"<tr>"
1819 "<td class=\"mode\">";
1840 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1841 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1843 "<td class=\"name\">"
1851 if (!p.empty() && p[p.size() - 1] !=
'/')
1856 std::unique_ptr<char, decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1862 p +=
"</a></td></tr>";
1868 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1869 if (pp) startp = pp+1;
1878 stringresp +=
"</table></div><br><br><hr size=1>"
1879 "<p><span id=\"requestby\">Request by ";
1941 XrdHttpReq::ReturnGetHeaders() {
1942 std::string responseHeader;
1947 if (!responseHeader.empty()) {
1948 responseHeader +=
"\r\n";
1950 addAgeHeader(responseHeader);
1962 if (m_transfer_encoding_chunked && m_trailer_headers) {
1964 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
1966 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
1974 if (uranges.size() != 1)
1979 const off_t cnt = uranges[0].end - uranges[0].start + 1;
1981 std::string header =
"Content-Range: bytes ";
1982 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
1984 if (!responseHeader.empty()) {
1986 header += responseHeader.c_str();
1989 if (m_transfer_encoding_chunked && m_trailer_headers) {
1991 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
1993 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
2000 for (
auto &ur : uranges) {
2001 cnt += ur.end - ur.start + 1;
2006 (
char *)
"123456").size();
2010 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2016 if (!header.empty()) {
2019 addAgeHeader(header);
2022 if (m_transfer_encoding_chunked && m_trailer_headers) {
2024 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2026 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2032 if (m_status_trailer) {
2033 if (header.empty()) {
2034 header +=
"Trailer: X-Transfer-Status";
2036 header +=
"\r\nTrailer: X-Transfer-Status";
2043 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2045 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2046 generateWebdavErrMsg();
2051 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2060 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2065 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2072 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2076 std::string response_headers;
2080 <<
" stat=" << (
char *)
iovP[0].iov_base);
2082 sscanf((
const char *)
iovP[0].iov_base,
"%lld %lld %ld %ld",
2092 addAgeHeader(response_headers);
2093 response_headers +=
"\r\n";
2096 addETagHeader(response_headers);
2097 response_headers +=
"\r\n";
2099 response_headers +=
"Accept-Ranges: bytes";
2100 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2105 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2108 return ret_keepalive ? 1 : -1;
2111 std::string response_headers;
2112 int response = PostProcessChecksum(response_headers);
2113 if (-1 == response) {
2116 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2118 addAgeHeader(response_headers);
2119 response_headers +=
"\r\n";
2121 response_headers +=
"Accept-Ranges: bytes";
2122 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2125 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2147 if (
iovP[1].iov_len > 1) {
2149 <<
" stat=" << (
char *)
iovP[1].iov_base);
2152 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2173 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2174 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2183 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2184 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2197 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2198 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2205 return PostProcessListing(final_);
2215 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2222 httpErrorBody = rrerror.
errMsg;
2225 if (m_transfer_encoding_chunked && m_trailer_headers) {
2226 std::string trailer =
"X-Transfer-Status: " + std::to_string(httpStatusCode) +
": " + httpErrorBody +
"\r\n";
2228 if (prot->ChunkResp(trailer.c_str(), -1))
return -1;
2231 if (rrerror)
return -1;
2238 auto rc = sendFooterError(
"");
2247 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2250 getReadResponse(received);
2254 rc = sendReadResponseSingleRange(received);
2256 rc = sendReadResponsesMultiRanges(received);
2275 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2283 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2286 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2297 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2309 if (m_transfer_encoding_chunked) {
2310 m_current_chunk_offset += l;
2314 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2321 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2324 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2343 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2344 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2359 <<
" stat=" << (
char *)
iovP[0].iov_base);
2362 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2374 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2377 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2378 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2390 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2391 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2409 <<
" stat=" << (
char *)
iovP[0].iov_base);
2412 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2418 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2423 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2451 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2452 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2455 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2459 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2460 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2462 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2467 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2477 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";
2480 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2494 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2498 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2500 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2501 strncpy(entry, (
char *) startp, endp - startp);
2502 entry[endp - startp] = 0;
2509 <<
" stat=" << endp);
2512 sscanf(endp,
"%ld %lld %ld %ld",
2520 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2540 if (*p.rbegin() !=
'/') p +=
"/";
2544 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2568 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2569 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2572 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2576 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2577 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2579 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2582 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2590 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2591 if (pp) startp = pp+1;
2602 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";
2605 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2625 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2627 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2628 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2633 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2641 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2645 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2658 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2659 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2672 int XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2673 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2674 std::stringstream ss;
2676 ss << httpStatusCode;
2677 if (!httpErrorBody.empty()) {
2678 std::string_view statusView(httpErrorBody);
2681 if (!statusView.empty() && statusView.back() ==
'\n') {
2682 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2684 ss <<
": " << httpErrorBody;
2688 if (!extra_text.empty()) ss <<
": " << extra_text;
2692 const std::string trailer =
"X-Transfer-Status: " + ss.str();
2695 if (prot->ChunkResp(trailer.c_str(), -1))
return -1;
2699 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2704 void XrdHttpReq::addAgeHeader(std::string &headers) {
2706 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2709 void XrdHttpReq::addETagHeader(std::string &headers) {
2710 headers += std::string(
"Etag: \"") + std::to_string(
etagval) +
"\"";
2715 TRACE(REQ,
" XrdHttpReq request ended.");
2751 httpStatusCode = -1;
2752 initialStatusCode= -1;
2763 m_transfer_encoding_chunked =
false;
2764 m_current_chunk_size = -1;
2765 m_current_chunk_offset = 0;
2767 m_trailer_headers =
false;
2768 m_status_trailer =
false;
2803 startTime = std::chrono::steady_clock::time_point::min();
2806 void XrdHttpReq::getfhandle() {
2809 TRACEI(REQ,
"fhandle:" <<
2823 for (
int i = 0; i <
iovN; i++) {
2825 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2841 for (
int i = 0; i <
iovN; i++) {
2842 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2847 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2849 if (received.size() == 0) {
2865 std::string st_header;
2866 std::string fin_header;
2873 std::vector<rinfo> rvec;
2876 rvec.reserve(received.size());
2878 for(
const auto &rcv: received) {
2887 rentry.start = start;
2888 rentry.finish = finish;
2897 rentry.st_header = s;
2898 sum_len += s.size();
2901 sum_len += rcv.size;
2905 rentry.fin_header = s;
2906 sum_len += s.size();
2909 rvec.push_back(rentry);
2914 if (m_transfer_encoding_chunked && m_trailer_headers) {
2915 prot->ChunkRespHeader(sum_len);
2919 for(
const auto &rentry: rvec) {
2922 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2923 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2929 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2933 if (rentry.finish) {
2934 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2941 if (m_transfer_encoding_chunked && m_trailer_headers) {
2942 prot->ChunkRespFooter();
2948 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2951 if (received.size() == 0) {
2961 for(
const auto &rcv: received) {
2970 if (m_transfer_encoding_chunked && m_trailer_headers) {
2971 prot->ChunkRespHeader(sum);
2973 for(
const auto &rcv: received) {
2974 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2976 if (m_transfer_encoding_chunked && m_trailer_headers) {
2977 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.
void Tobase64(const unsigned char *input, int length, char *out)
int mapXrdErrToHttp(XErrorCode xrdError)
bool Fromhexdigest(const std::string &hex, std::vector< uint8_t > &outputBytes)
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)
void stripCgi(std::string &url, const std::unordered_set< std::string > &cgiKeys)
std::string obfuscateAuth(const std::string &input)
XrdHttpChecksumRawPtr getChecksumToRunWantDigest(const std::string &wantDigest) const
XrdHttpChecksumRawPtr getChecksumToRunWantReprDigest(const std::map< std::string, uint8_t > &wantReprDigest) 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 std::unordered_set< std::string > strp_cgi_params
CGI parameters (names) to strip from redirect URLs.
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.
size_t getMaxRanges() const
return the maximum number of ranges that may be requested
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::map< std::string, uint8_t > m_want_repr_digest
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.
std::map< std::string, std::string > m_repr_digest
Repr-Digest map where the key is the digest name and the value is the base64 encoded digest value.
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!
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.
std::string m_want_digest
The requested digest type.
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
std::chrono::steady_clock::time_point startTime
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