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")) {
190 if (m_transfer_encoding_chunked) {
208 }
else if (!strcasecmp(key,
"destination")) {
211 }
else if (!strcasecmp(key,
"want-digest")) {
218 }
else if (!strcasecmp(key,
"depth")) {
220 if (strcmp(val,
"infinity"))
223 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
225 }
else if (!strcasecmp(key,
"te") && strstr(val,
"trailers")) {
226 m_trailer_headers =
true;
227 }
else if (!strcasecmp(key,
"transfer-encoding")) {
246 m_transfer_encoding_chunked =
true;
247 }
else if (!strcasecmp(key,
"x-transfer-status") && strstr(val,
"true")) {
248 m_transfer_encoding_chunked =
true;
249 m_status_trailer =
true;
250 }
else if (!strcasecmp(key,
"scitag")) {
254 }
else if (!strcasecmp(key,
"user-agent")) {
257 }
else if (!strcasecmp(key,
"origin")) {
260 }
else if (!strcasecmp(key,
"repr-digest")) {
262 }
else if (!strcasecmp(key,
"want-repr-digest")) {
269 auto it = std::find_if(prot->
hdr2cgimap.begin(), prot->
hdr2cgimap.end(),[key](
const auto & item) {
270 return !strcasecmp(key,item.first.c_str());
274 s.assign(val, line+len-val);
287 int XrdHttpReq::parseHost(
char *line) {
293 void XrdHttpReq::parseScitag(
const std::string & val) {
297 std::string scitagS = val;
300 if(scitagS[0] !=
'-') {
302 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
315 addCgi(
"pmark.appname",this->
request == ReqType::rtGET ?
"http-get" :
"http-put");
326 if (!line)
return -1;
329 char *p = strchr((
char *) line, (
int)
' ');
353 char *val = line + pos + 1;
360 p = strchr((
char *) val, (
int)
' ');
374 if (!strcmp(key,
"GET")) {
376 }
else if (!strcmp(key,
"HEAD")) {
378 }
else if (!strcmp(key,
"PUT")) {
380 }
else if (!strcmp(key,
"POST")) {
382 }
else if (!strcmp(key,
"PATCH")) {
384 }
else if (!strcmp(key,
"OPTIONS")) {
386 }
else if (!strcmp(key,
"DELETE")) {
388 }
else if (!strcmp(key,
"PROPFIND")) {
390 }
else if (!strcmp(key,
"MKCOL")) {
392 }
else if (!strcmp(key,
"MOVE")) {
394 }
else if (!strcmp(key,
"COPY")) {
404 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
418 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
425 for (
int i = 0; i < nitems; i++) {
436 void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
443 for (
int i = 0; i < nitems; i++) {
461 for (
const auto &c: cl) {
464 memcpy(&ra.fhandle, this->fhandle, 4);
466 ra.offset = c.offset;
480 clientMarshallReadAheadList(j);
489 std::ostringstream s;
491 s <<
"\r\n--" << token <<
"\r\n";
492 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
493 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
499 std::ostringstream s;
501 s <<
"\r\n--" << token <<
"--\r\n";
514 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
520 this->
final = final_;
522 if (PostProcessHTTPReq(final_))
reset();
536 int rc = info.
Send(0, 0, 0, 0);
537 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
554 TRACE(REQ,
" XrdHttpReq::Done");
560 int r = PostProcessHTTPReq(
true);
563 if (r < 0)
return false;
574 TRACE(REQ,
" XrdHttpReq::Error");
585 auto rc = PostProcessHTTPReq();
610 bool invalid_host =
false;
615 for (
const char *c = hname; *c; ++c)
616 if (*c ==
'\r' || *c ==
'\n')
621 prot->SendSimpleResp(502,
nullptr,
nullptr,
"Invalid redirect host", 0,
false);
633 if (strncmp(hname,
"file://", 7) == 0)
635 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
642 char *pp = strchr((
char *)hname,
'?');
648 int varlen = strlen(vardata);
651 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
660 sprintf(buf,
":%d", port);
707 return ret_keepalive;
718 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
737 s +=
"&xrdhttptime=";
739 sprintf(buf,
"%lld", (
long long) tnow);
744 s +=
"&xrdhttpname=";
750 s +=
"&xrdhttpvorg=";
755 s +=
"&xrdhttphost=";
765 s +=
"&xrdhttprole=";
770 s +=
"&xrdhttpgrps=";
775 s +=
"&xrdhttpendorsements=";
780 s +=
"&xrdhttpcredslen=";
782 sprintf(buf,
"%d", secent->
credslen);
788 s +=
"&xrdhttpcreds=";
802 void XrdHttpReq::sanitizeResourcePfx() {
833 void XrdHttpReq::parseResource(
char *res) {
839 char *p = strchr(res,
'?');
847 sanitizeResourcePfx();
872 sanitizeResourcePfx();
894 void XrdHttpReq::generateWebdavErrMsg() {
900 httpStatusCode = 200;
901 httpErrorBody =
"OK";
907 httpErrorBody =
etext +
"\n";
930 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
933 outResourceDigestOpaque += !
opaque ?
"?" :
"&";
934 outResourceDigestOpaque +=
"cks.type=";
943 if (
startTime == std::chrono::steady_clock::time_point::min())
startTime = std::chrono::steady_clock::now();
949 int query_param_status = 0;
953 if (query_param_status == 0) {
957 query_param_status = 1;
958 auto length_str = std::to_string(
length);
964 opaque->
Put(
"oss.asize", length_str.c_str());
970 if (query_param_status == 0) {
982 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
983 << header2cgistrObf.c_str() <<
"'");
998 if (r < 0)
return -1;
1013 generateWebdavErrMsg();
1014 prot->SendSimpleResp(400, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1023 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1030 if(prepareCksum < 0) {
1035 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1059 if (
resource ==
"/static/css/xrdhttp.css") {
1060 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1064 if (
resource ==
"/static/icons/xrdhttp.ico") {
1065 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1085 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1095 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1127 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1141 if(prepareCksum < 0) {
1145 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest or Want-Repr-Digest header.", 0,
false);
1150 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1161 generateWebdavErrMsg();
1162 return sendFooterError(
"Could not run close request on the bridge");
1173 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1187 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1198 l = res.length() + 1;
1202 generateWebdavErrMsg();
1203 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1204 sendFooterError(
"Could not run listing request on the bridge");
1217 auto retval = ReturnGetHeaders();
1238 TRACEI(REQ,
" Failed to run close request on the bridge.");
1252 if ( readChunkList.size() == 1 ) {
1264 offs = readChunkList[0].offset;
1265 l = readChunkList[0].size;
1273 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1276 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1285 TRACE(ALL,
" Data sizes mismatch.");
1289 TRACE(ALL,
" No more bytes to send.");
1296 httpStatusCode = 416;
1297 httpErrorBody =
"Range Not Satisfiable";
1298 std::stringstream ss;
1299 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1300 return sendFooterError(ss.str());
1304 generateWebdavErrMsg();
1305 return sendFooterError(
"Could not run read request on the bridge");
1313 generateWebdavErrMsg();
1314 return sendFooterError(
"Could not run ReadV request on the bridge");
1343 if (! XrdHttpProtocol::usingEC)
1349 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1364 if (m_transfer_encoding_chunked) {
1365 if (m_current_chunk_size == m_current_chunk_offset) {
1368 if (prot->BuffUsed() < 2)
return 1;
1369 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1370 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1373 prot->BuffConsume(2);
1374 if (m_current_chunk_size == 0) {
1378 m_transfer_encoding_chunked =
false;
1382 m_current_chunk_size = -1;
1383 m_current_chunk_offset = 0;
1385 if (!prot->BuffUsed())
return 1;
1387 if (-1 == m_current_chunk_size) {
1391 bool found_newline =
false;
1398 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1399 for (; idx < max_chunk_size_chars; idx++) {
1400 if (prot->myBuffStart[idx] ==
'\n') {
1401 found_newline =
true;
1407 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1408 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1409 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1412 if (found_newline) {
1413 char *endptr = NULL;
1414 std::string line_contents(prot->myBuffStart, idx);
1415 long long chunk_contents = strtoll(line_contents.c_str(), &endptr, 16);
1417 if (*endptr !=
';' && *endptr !=
'\r') {
1418 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1419 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1422 m_current_chunk_size = chunk_contents;
1423 m_current_chunk_offset = 0;
1424 prot->BuffConsume(idx + 1);
1425 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1432 if (m_current_chunk_size == 0) {
1441 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1442 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1443 chunk_bytes_remaining);
1448 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1449 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1450 generateWebdavErrMsg();
1451 return sendFooterError(
"Could not run write request on the bridge");
1455 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1465 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1471 TRACEI(REQ,
"Writing " << bytes_to_read);
1472 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1473 generateWebdavErrMsg();
1474 return sendFooterError(
"Could not run write request on the bridge");
1496 generateWebdavErrMsg();
1497 return sendFooterError(
"Could not run close request on the bridge");
1512 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);
1515 return ret_keepalive ? 1 : -1;
1537 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1557 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1571 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1587 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1602 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1607 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1612 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1631 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1663 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1689 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1700 skip = (skip == std::string::npos) ? 0 : skip + 3;
1704 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1715 size_t path_pos =
destination.find(
'/', skip + 1);
1717 if (path_pos == std::string::npos) {
1718 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Cannot determine destination path", 0,
false);
1725 l = mv_args.length() + 1;
1733 if (!prot->
Bridge->
Run((
char *) &
xrdreq, (
char *) mv_args.c_str(), l)) {
1734 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1743 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1754 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1757 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1762 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1763 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1765 std::string cksumType {
reinterpret_cast<char *
>(
iovP[0].iov_base),
iovP[0].iov_len};
1767 size_t cksumValueLen =
iovP[
iovN-1].iov_len - 1;
1768 std::string cksumValue {
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base), cksumValueLen};
1769 std::string digest_value = cksumValue;
1774 if (convert_to_base64) {
1775 std::vector<uint8_t> digest_binary_value;
1777 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1780 Tobase64(digest_binary_value,digest_value);
1784 digest_header =
"Digest: ";
1786 digest_header +=
"=";
1787 digest_header += digest_value;
1789 digest_header =
"Repr-Digest: ";
1791 digest_header +=
"=:";
1792 digest_header += digest_value;
1793 digest_header +=
":";
1798 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1804 XrdHttpReq::PostProcessListing(
bool final_) {
1807 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1808 httpErrorBody.c_str(), httpErrorBody.length(),
false);
1814 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1815 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1817 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1818 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1819 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1840 "<th class=\"mode\">Mode</th>"
1841 "<th class=\"flags\">Flags</th>"
1842 "<th class=\"size\">Size</th>"
1843 "<th class=\"datetime\">Modified</th>"
1844 "<th class=\"name\">Name</th>"
1850 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1853 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1855 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1856 strncpy(entry, (
char *) startp, endp - startp);
1857 entry[endp - startp] = 0;
1864 <<
" stat=" << endp);
1867 sscanf(endp,
"%ld %lld %ld %ld",
1873 strcpy(entry, (
char *) startp);
1875 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1877 std::string p =
"<tr>"
1878 "<td class=\"mode\">";
1899 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1900 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1902 "<td class=\"name\">"
1910 if (!p.empty() && p[p.size() - 1] !=
'/')
1915 std::unique_ptr<char, decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1921 p +=
"</a></td></tr>";
1927 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1928 if (pp) startp = pp+1;
1937 stringresp +=
"</table></div><br><br><hr size=1>"
1938 "<p><span id=\"requestby\">Request by ";
2000 XrdHttpReq::ReturnGetHeaders() {
2001 std::string responseHeader;
2006 if (!responseHeader.empty()) {
2007 responseHeader +=
"\r\n";
2009 addAgeHeader(responseHeader);
2021 if (m_transfer_encoding_chunked && m_trailer_headers) {
2023 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2025 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2033 if (uranges.size() != 1)
2038 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2040 std::string header =
"Content-Range: bytes ";
2041 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2043 if (!responseHeader.empty()) {
2045 header += responseHeader.c_str();
2048 if (m_transfer_encoding_chunked && m_trailer_headers) {
2050 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
2052 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
2059 for (
auto &ur : uranges) {
2060 cnt += ur.end - ur.start + 1;
2065 (
char *)
"123456").size();
2069 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2075 if (!header.empty()) {
2078 addAgeHeader(header);
2081 if (m_transfer_encoding_chunked && m_trailer_headers) {
2083 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2085 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2091 if (m_status_trailer) {
2092 if (header.empty()) {
2093 header +=
"Trailer: X-Transfer-Status";
2095 header +=
"\r\nTrailer: X-Transfer-Status";
2102 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2104 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2105 generateWebdavErrMsg();
2110 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2119 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2124 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2131 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2135 std::string response_headers;
2139 <<
" stat=" << (
char *)
iovP[0].iov_base);
2141 sscanf((
const char *)
iovP[0].iov_base,
"%lld %lld %ld %ld",
2151 addAgeHeader(response_headers);
2152 response_headers +=
"\r\n";
2155 addETagHeader(response_headers);
2156 response_headers +=
"\r\n";
2158 response_headers +=
"Accept-Ranges: bytes";
2159 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2164 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2167 return ret_keepalive ? 1 : -1;
2170 std::string response_headers;
2171 int response = PostProcessChecksum(response_headers);
2172 if (-1 == response) {
2175 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2177 addAgeHeader(response_headers);
2178 response_headers +=
"\r\n";
2180 response_headers +=
"Accept-Ranges: bytes";
2181 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2184 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2206 if (
iovP[1].iov_len > 1) {
2208 <<
" stat=" << (
char *)
iovP[1].iov_base);
2211 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2232 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2233 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2242 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2243 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2256 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2257 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2264 return PostProcessListing(final_);
2274 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2281 httpErrorBody = rrerror.
errMsg;
2284 if (m_transfer_encoding_chunked && m_trailer_headers) {
2285 std::string trailer =
"X-Transfer-Status: " + std::to_string(httpStatusCode) +
": " + httpErrorBody +
"\r\n";
2287 if (prot->ChunkResp(trailer.c_str(), -1))
return -1;
2290 if (rrerror)
return -1;
2297 auto rc = sendFooterError(
"");
2306 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2309 getReadResponse(received);
2313 rc = sendReadResponseSingleRange(received);
2315 rc = sendReadResponsesMultiRanges(received);
2334 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2342 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2345 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2356 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2368 if (m_transfer_encoding_chunked) {
2369 m_current_chunk_offset += l;
2373 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2380 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2383 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2402 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2403 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2418 <<
" stat=" << (
char *)
iovP[0].iov_base);
2421 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2433 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2436 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2437 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2449 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2450 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2468 <<
" stat=" << (
char *)
iovP[0].iov_base);
2471 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2477 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2482 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2510 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2511 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2514 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2518 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2520 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2525 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2535 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";
2538 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2552 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2556 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2558 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2559 strncpy(entry, (
char *) startp, endp - startp);
2560 entry[endp - startp] = 0;
2567 <<
" stat=" << endp);
2570 sscanf(endp,
"%ld %lld %ld %ld",
2578 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2598 if (*p.rbegin() !=
'/') p +=
"/";
2602 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2626 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2627 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2630 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2634 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2635 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2637 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2640 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2648 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2649 if (pp) startp = pp+1;
2660 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";
2663 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2683 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2685 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2686 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2691 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2699 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2703 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2716 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2717 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2730 int XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2731 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2732 std::stringstream ss;
2734 ss << httpStatusCode;
2735 if (!httpErrorBody.empty()) {
2736 std::string_view statusView(httpErrorBody);
2739 if (!statusView.empty() && statusView.back() ==
'\n') {
2740 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2742 ss <<
": " << httpErrorBody;
2746 if (!extra_text.empty()) ss <<
": " << extra_text;
2750 const std::string trailer =
"X-Transfer-Status: " + ss.str();
2753 if (prot->ChunkResp(trailer.c_str(), -1))
return -1;
2757 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2762 void XrdHttpReq::addAgeHeader(std::string &headers) {
2764 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2767 void XrdHttpReq::addETagHeader(std::string &headers) {
2768 headers += std::string(
"Etag: \"") + std::to_string(
etagval) +
"\"";
2773 TRACE(REQ,
" XrdHttpReq request ended.");
2809 httpStatusCode = -1;
2810 initialStatusCode= -1;
2822 m_transfer_encoding_chunked =
false;
2823 m_current_chunk_size = -1;
2824 m_current_chunk_offset = 0;
2826 m_trailer_headers =
false;
2827 m_status_trailer =
false;
2862 startTime = std::chrono::steady_clock::time_point::min();
2865 void XrdHttpReq::getfhandle() {
2868 TRACEI(REQ,
"fhandle:" <<
2882 for (
int i = 0; i <
iovN; i++) {
2884 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2900 for (
int i = 0; i <
iovN; i++) {
2901 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2906 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2908 if (received.size() == 0) {
2924 std::string st_header;
2925 std::string fin_header;
2932 std::vector<rinfo> rvec;
2935 rvec.reserve(received.size());
2937 for(
const auto &rcv: received) {
2946 rentry.start = start;
2947 rentry.finish = finish;
2956 rentry.st_header = s;
2957 sum_len += s.size();
2960 sum_len += rcv.size;
2964 rentry.fin_header = s;
2965 sum_len += s.size();
2968 rvec.push_back(rentry);
2973 if (m_transfer_encoding_chunked && m_trailer_headers) {
2974 prot->ChunkRespHeader(sum_len);
2978 for(
const auto &rentry: rvec) {
2981 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2982 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2988 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2992 if (rentry.finish) {
2993 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
3000 if (m_transfer_encoding_chunked && m_trailer_headers) {
3001 prot->ChunkRespFooter();
3007 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
3010 if (received.size() == 0) {
3020 for(
const auto &rcv: received) {
3029 if (m_transfer_encoding_chunked && m_trailer_headers) {
3030 prot->ChunkRespHeader(sum);
3032 for(
const auto &rcv: received) {
3033 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3035 if (m_transfer_encoding_chunked && m_trailer_headers) {
3036 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