39#include "XrdVersion.hh"
68#define MAX_RESOURCE_LEN 16384
71#define TRACELINK prot->Link
75const char *TraceID =
"Req";
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")) {
251 if(prot->pmarkHandle !=
nullptr) {
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());
272 if (it != prot->hdr2cgimap.end() && (
opaque ? (0 ==
opaque->
Get(it->second.c_str())) :
true)) {
274 s.assign(val, line+len-val);
287int XrdHttpReq::parseHost(
char *line) {
293void XrdHttpReq::parseScitag(
const std::string & val) {
297 std::string scitagS = val;
300 if(scitagS[0] !=
'-') {
302 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
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")) {
418void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
425 for (
int i = 0; i < nitems; i++) {
436void 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();
609 bool invalid_host =
false;
614 for (
const char *c = hname; *c; ++c)
615 if (*c ==
'\r' || *c ==
'\n')
620 prot->SendSimpleResp(502,
nullptr,
nullptr,
"Invalid redirect host", 0,
false);
628 const bool fullUrl = (port < 0);
630 TRACE(REQ,
" XrdHttpReq::Redir using absolute URL: " << hname);
636 char *pp = strchr((
char *)hname,
'?');
641 int varlen = strlen(vardata);
644 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
649 const bool destHttpCleartext =
650 fullUrl ? (strncmp(hname,
"http://", 7) == 0) : (!prot->isdesthttps);
652 std::string urlHashPath;
654 const std::string encodedResource =
657 encodedResource.c_str()).c_str();
661 if (fullUrl && destHttpCleartext)
682 if (destHttpCleartext && prot->ishttps) {
683 const char *hashPath = urlHashPath.empty() ?
resource.
c_str() : urlHashPath.c_str();
698 if (!prot->strp_cgi_params.empty()) {
711 return ret_keepalive;
722 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
741 s +=
"&xrdhttptime=";
743 sprintf(buf,
"%lld", (
long long) tnow);
748 s +=
"&xrdhttpname=";
754 s +=
"&xrdhttpvorg=";
759 s +=
"&xrdhttphost=";
769 s +=
"&xrdhttprole=";
774 s +=
"&xrdhttpgrps=";
779 s +=
"&xrdhttpendorsements=";
784 s +=
"&xrdhttpcredslen=";
786 sprintf(buf,
"%d", secent->
credslen);
792 s +=
"&xrdhttpcreds=";
806void XrdHttpReq::sanitizeResourcePfx() {
837void XrdHttpReq::parseResource(
char *res) {
843 char *p = strchr(res,
'?');
851 sanitizeResourcePfx();
867 sanitizeResourcePfx();
881void XrdHttpReq::generateWebdavErrMsg() {
887 httpStatusCode = 200;
888 httpErrorBody =
"OK";
894 httpErrorBody =
etext +
"\n";
910 outCksum = prot->cksumHandler.getChecksumToRunWantDigest(
m_want_digest);
917 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
920 outResourceDigestOpaque += !
opaque ?
"?" :
"&";
921 outResourceDigestOpaque +=
"cks.type=";
930 if (
startTime == std::chrono::steady_clock::time_point::min())
startTime = std::chrono::steady_clock::now();
936 int query_param_status = 0;
940 if (query_param_status == 0) {
944 query_param_status = 1;
945 auto length_str = std::to_string(
length);
951 opaque->Put(
"oss.asize", length_str.c_str());
957 if (query_param_status == 0) {
969 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
970 << header2cgistrObf.c_str() <<
"'");
985 if (r < 0)
return -1;
1000 generateWebdavErrMsg();
1001 prot->SendSimpleResp(400, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1010 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1017 if(prepareCksum < 0) {
1022 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1043 if (prot->embeddedstatic) {
1046 if (
resource ==
"/static/css/xrdhttp.css") {
1047 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1051 if (
resource ==
"/static/icons/xrdhttp.ico") {
1052 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1061 if (prot->staticredir) {
1064 s.
append(prot->staticredir);
1072 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1079 if (prot->staticpreload) {
1082 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1114 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1128 if(prepareCksum < 0) {
1132 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest or Want-Repr-Digest header.", 0,
false);
1137 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1147 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1148 generateWebdavErrMsg();
1149 return sendFooterError(
"Could not run close request on the bridge");
1158 if (prot->listdeny) {
1160 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1164 if (prot->listredir) {
1166 s.
append(prot->listredir);
1174 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1185 l = res.length() + 1;
1188 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) res.c_str(), l)) {
1189 generateWebdavErrMsg();
1190 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1191 sendFooterError(
"Could not run listing request on the bridge");
1204 auto retval = ReturnGetHeaders();
1224 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1225 TRACEI(REQ,
" Failed to run close request on the bridge.");
1239 if ( readChunkList.size() == 1 ) {
1251 offs = readChunkList[0].offset;
1252 l = readChunkList[0].size;
1260 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1263 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1272 TRACE(ALL,
" Data sizes mismatch.");
1276 TRACE(ALL,
" No more bytes to send.");
1283 httpStatusCode = 416;
1284 httpErrorBody =
"Range Not Satisfiable";
1285 std::stringstream ss;
1286 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1287 return sendFooterError(ss.str());
1290 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1291 generateWebdavErrMsg();
1292 return sendFooterError(
"Could not run read request on the bridge");
1300 generateWebdavErrMsg();
1301 return sendFooterError(
"Could not run ReadV request on the bridge");
1333 bool noOverwrite = overwrite !=
allheaders.end() && overwrite->second ==
"F";
1334 if (! XrdHttpProtocol::usingEC && ! noOverwrite)
1340 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1355 if (m_transfer_encoding_chunked) {
1356 if (m_current_chunk_size == m_current_chunk_offset) {
1359 if (prot->BuffUsed() < 2)
return 1;
1360 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1361 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1364 prot->BuffConsume(2);
1365 if (m_current_chunk_size == 0) {
1369 m_transfer_encoding_chunked =
false;
1373 m_current_chunk_size = -1;
1374 m_current_chunk_offset = 0;
1376 if (!prot->BuffUsed())
return 1;
1378 if (-1 == m_current_chunk_size) {
1382 bool found_newline =
false;
1389 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1390 for (; idx < max_chunk_size_chars; idx++) {
1391 if (prot->myBuffStart[idx] ==
'\n') {
1392 found_newline =
true;
1398 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1399 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1400 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1403 if (found_newline) {
1404 char *endptr = NULL;
1405 std::string line_contents(prot->myBuffStart, idx);
1406 long long chunk_contents = strtoll(line_contents.c_str(), &endptr, 16);
1408 if (*endptr !=
';' && *endptr !=
'\r') {
1409 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1410 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1413 m_current_chunk_size = chunk_contents;
1414 m_current_chunk_offset = 0;
1415 prot->BuffConsume(idx + 1);
1416 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1423 if (m_current_chunk_size == 0) {
1432 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1433 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1434 chunk_bytes_remaining);
1439 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1440 if (!prot->Bridge->Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1441 generateWebdavErrMsg();
1442 return sendFooterError(
"Could not run write request on the bridge");
1446 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1456 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1462 TRACEI(REQ,
"Writing " << bytes_to_read);
1463 if (!prot->Bridge->Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1464 generateWebdavErrMsg();
1465 return sendFooterError(
"Could not run write request on the bridge");
1486 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1487 generateWebdavErrMsg();
1488 return sendFooterError(
"Could not run close request on the bridge");
1503 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);
1506 return ret_keepalive ? 1 : -1;
1528 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1547 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1548 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1561 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1562 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1578 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1593 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1598 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1603 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1622 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1653 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1654 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1679 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1680 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1691 skip = (skip == std::string::npos) ? 0 : skip + 3;
1695 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1706 size_t path_pos =
destination.find(
'/', skip + 1);
1708 if (path_pos == std::string::npos) {
1709 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Cannot determine destination path", 0,
false);
1716 l = mv_args.length() + 1;
1724 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) mv_args.c_str(), l)) {
1725 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1734 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1745XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1748 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1753 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1754 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1756 std::string cksumType {
reinterpret_cast<char *
>(
iovP[0].iov_base),
iovP[0].iov_len};
1758 size_t cksumValueLen =
iovP[
iovN-1].iov_len - 1;
1759 std::string cksumValue {
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base), cksumValueLen};
1760 std::string digest_value = cksumValue;
1765 if (convert_to_base64) {
1766 std::vector<uint8_t> digest_binary_value;
1768 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1771 Tobase64(digest_binary_value,digest_value);
1775 digest_header =
"Digest: ";
1777 digest_header +=
"=";
1778 digest_header += digest_value;
1780 digest_header =
"Repr-Digest: ";
1782 digest_header +=
"=:";
1783 digest_header += digest_value;
1784 digest_header +=
":";
1789 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1795XrdHttpReq::PostProcessListing(
bool final_) {
1798 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1799 httpErrorBody.c_str(), httpErrorBody.length(),
false);
1805 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1806 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1808 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1809 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1810 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1831 "<th class=\"mode\">Mode</th>"
1832 "<th class=\"flags\">Flags</th>"
1833 "<th class=\"size\">Size</th>"
1834 "<th class=\"datetime\">Modified</th>"
1835 "<th class=\"name\">Name</th>"
1841 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1844 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
1846 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1847 strncpy(entry, (
char *) startp, endp - startp);
1848 entry[endp - startp] = 0;
1855 <<
" stat=" << endp);
1858 sscanf(endp,
"%ld %lld %ld %ld",
1864 strcpy(entry, (
char *) startp);
1866 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1868 std::string p =
"<tr>"
1869 "<td class=\"mode\">";
1890 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1891 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1893 "<td class=\"name\">"
1901 if (!p.empty() && p[p.size() - 1] !=
'/')
1906 std::unique_ptr<char,
decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1912 p +=
"</a></td></tr>";
1918 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1919 if (pp) startp = pp+1;
1928 stringresp +=
"</table></div><br><br><hr size=1>"
1929 "<p><span id=\"requestby\">Request by ";
1931 if (prot->SecEntity.name)
1936 if (prot->SecEntity.vorg ||
1937 prot->SecEntity.name ||
1938 prot->SecEntity.moninfo ||
1939 prot->SecEntity.role)
1942 if (prot->SecEntity.vorg) {
1947 if (prot->SecEntity.moninfo) {
1951 if (prot->SecEntity.name) {
1956 if (prot->SecEntity.role) {
1959 if (prot->SecEntity.endorsements) {
1966 if (prot->SecEntity.vorg ||
1967 prot->SecEntity.moninfo ||
1968 prot->SecEntity.role)
1971 if (prot->SecEntity.host) {
1991XrdHttpReq::ReturnGetHeaders() {
1992 std::string responseHeader;
1997 if (!responseHeader.empty()) {
1998 responseHeader +=
"\r\n";
2000 addAgeHeader(responseHeader);
2012 if (m_transfer_encoding_chunked && m_trailer_headers) {
2014 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2016 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2024 if (uranges.size() != 1)
2029 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2031 std::string header =
"Content-Range: bytes ";
2032 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2034 if (!responseHeader.empty()) {
2036 header += responseHeader.c_str();
2039 if (m_transfer_encoding_chunked && m_trailer_headers) {
2041 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
2043 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
2050 for (
auto &ur : uranges) {
2051 cnt += ur.end - ur.start + 1;
2056 (
char *)
"123456").size();
2060 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2066 if (!header.empty()) {
2069 addAgeHeader(header);
2072 if (m_transfer_encoding_chunked && m_trailer_headers) {
2074 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2076 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2082 if (m_status_trailer) {
2083 if (header.empty()) {
2084 header +=
"Trailer: X-Transfer-Status";
2086 header +=
"\r\nTrailer: X-Transfer-Status";
2093int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2095 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2096 generateWebdavErrMsg();
2101 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2110 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2115 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2122 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2126 std::string response_headers;
2130 <<
" stat=" << (
char *)
iovP[0].iov_base);
2132 sscanf((
const char *)
iovP[0].iov_base,
"%lld %lld %ld %ld",
2142 addAgeHeader(response_headers);
2143 response_headers +=
"\r\n";
2146 addETagHeader(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);
2155 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2158 return ret_keepalive ? 1 : -1;
2161 std::string response_headers;
2162 int response = PostProcessChecksum(response_headers);
2163 if (-1 == response) {
2166 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2168 addAgeHeader(response_headers);
2169 response_headers +=
"\r\n";
2171 response_headers +=
"Accept-Ranges: bytes";
2172 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2175 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2197 if (
iovP[1].iov_len > 1) {
2199 <<
" stat=" << (
char *)
iovP[1].iov_base);
2202 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2223 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2224 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2233 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2234 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2247 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2248 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2255 return PostProcessListing(final_);
2265 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2272 httpErrorBody = rrerror.
errMsg;
2275 if (m_transfer_encoding_chunked && m_trailer_headers) {
2276 std::string trailer =
"X-Transfer-Status: " + std::to_string(httpStatusCode) +
": " + httpErrorBody +
"\r\n";
2278 if (prot->ChunkResp(trailer.c_str(), -1))
return -1;
2281 if (rrerror)
return -1;
2288 auto rc = sendFooterError(
"");
2297 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2300 getReadResponse(received);
2304 rc = sendReadResponseSingleRange(received);
2306 rc = sendReadResponsesMultiRanges(received);
2325 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2333 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2336 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2347 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2359 if (m_transfer_encoding_chunked) {
2360 m_current_chunk_offset += l;
2364 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2371 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2374 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2393 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2394 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2409 <<
" stat=" << (
char *)
iovP[0].iov_base);
2412 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2424 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2427 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2428 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2440 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2441 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2459 <<
" stat=" << (
char *)
iovP[0].iov_base);
2462 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2468 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2473 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2501 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2502 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2505 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2509 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2511 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2516 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2526 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";
2529 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2543 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2547 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
2549 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2550 strncpy(entry, (
char *) startp, endp - startp);
2551 entry[endp - startp] = 0;
2558 <<
" stat=" << endp);
2561 sscanf(endp,
"%ld %lld %ld %ld",
2569 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2589 if (*p.rbegin() !=
'/') p +=
"/";
2593 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2617 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2618 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2621 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2625 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2626 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2628 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2631 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2639 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2640 if (pp) startp = pp+1;
2651 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";
2654 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2674 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2676 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2677 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2682 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2690 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2694 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2707 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2708 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2721int XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2722 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2723 std::stringstream ss;
2725 ss << httpStatusCode;
2726 if (!httpErrorBody.empty()) {
2727 std::string_view statusView(httpErrorBody);
2730 if (!statusView.empty() && statusView.back() ==
'\n') {
2731 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2733 ss <<
": " << httpErrorBody;
2737 if (!extra_text.empty()) ss <<
": " << extra_text;
2741 const std::string trailer =
"X-Transfer-Status: " + ss.str();
2744 if (prot->ChunkResp(trailer.c_str(), -1))
return -1;
2748 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2753void XrdHttpReq::addAgeHeader(std::string &headers) {
2755 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2758void XrdHttpReq::addETagHeader(std::string &headers) {
2759 headers += std::string(
"Etag: \"") + std::to_string(
etagval) +
"\"";
2764 TRACE(REQ,
" XrdHttpReq request ended.");
2800 httpStatusCode = -1;
2801 initialStatusCode= -1;
2813 m_transfer_encoding_chunked =
false;
2814 m_current_chunk_size = -1;
2815 m_current_chunk_offset = 0;
2817 m_trailer_headers =
false;
2818 m_status_trailer =
false;
2853 startTime = std::chrono::steady_clock::time_point::min();
2856void XrdHttpReq::getfhandle() {
2859 TRACEI(REQ,
"fhandle:" <<
2873 for (
int i = 0; i <
iovN; i++) {
2875 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2876 l = (readahead_list *) p;
2880 received.emplace_back(p+
sizeof(readahead_list), -1, len);
2882 p +=
sizeof (readahead_list);
2891 for (
int i = 0; i <
iovN; i++) {
2892 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2897int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2899 if (received.size() == 0) {
2913 const XrdOucIOVec2 *ci;
2914 const XrdHttpReadRangeHandler::UserRange *ur;
2915 std::string st_header;
2916 std::string fin_header;
2923 std::vector<rinfo> rvec;
2926 rvec.reserve(received.size());
2928 for(
const auto &rcv: received) {
2931 const XrdHttpReadRangeHandler::UserRange *ur;
2937 rentry.start = start;
2938 rentry.finish = finish;
2947 rentry.st_header = s;
2948 sum_len += s.size();
2951 sum_len += rcv.size;
2955 rentry.fin_header = s;
2956 sum_len += s.size();
2959 rvec.push_back(rentry);
2964 if (m_transfer_encoding_chunked && m_trailer_headers) {
2965 prot->ChunkRespHeader(sum_len);
2969 for(
const auto &rentry: rvec) {
2972 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2973 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2979 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2983 if (rentry.finish) {
2984 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2991 if (m_transfer_encoding_chunked && m_trailer_headers) {
2992 prot->ChunkRespFooter();
2998int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
3001 if (received.size() == 0) {
3011 for(
const auto &rcv: received) {
3020 if (m_transfer_encoding_chunked && m_trailer_headers) {
3021 prot->ChunkRespHeader(sum);
3023 for(
const auto &rcv: received) {
3024 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3026 if (m_transfer_encoding_chunked && m_trailer_headers) {
3027 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.
void trim(std::string &str)
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)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
std::string httpCollapseSlashes(std::string path)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
std::string httpPathFromAbsoluteUrl(const char *hname)
Path starting at the first / after the authority. An absent path is /.
std::string httpBuildRedirectLocation(bool destHttps, int port, const char *hname, const char *encodedResource)
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)
XrdHttpChecksumHandlerImpl::XrdHttpChecksumRawPtr XrdHttpChecksumRawPtr
bool needsBase64Padding() const
std::string getXRootDConfigDigestName() const
std::string getHttpName() const
virtual int ProcessReq(XrdHttpExtReq &)=0
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)
static const int minTotID
static const int maxTotID
char * Get(const char *varname)
XrdOucEnv(const char *vardata=0, int vardlen=0, const XrdSecEntity *secent=0)
void assign(const char *s, int j, int k=-1)
int erasefromstart(int sz=0)
int erasefromend(int sz=0)
int find(const char c, int start=0, bool forward=1)
const char * c_str() const
static std::map< std::string, T >::const_iterator caseInsensitiveFind(const std::map< std::string, T > &m, const std::string &lowerCaseSearchKey)
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)