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")) {
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")) {
207 if(prot->pmarkHandle !=
nullptr) {
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());
228 if (it != prot->hdr2cgimap.end() && (
opaque ? (0 ==
opaque->Get(it->second.c_str())) :
true)) {
230 s.assign(val, line+len-val);
243int XrdHttpReq::parseHost(
char *line) {
249void XrdHttpReq::parseScitag(
const std::string & val) {
253 std::string scitagS = val;
256 if(scitagS[0] !=
'-') {
258 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
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")) {
374void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
381 for (
int i = 0; i < nitems; i++) {
392void 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();
566 if (prot->isdesthttps)
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);
620 if (!prot->isdesthttps && prot->ishttps) {
635 if (!prot->strp_cgi_params.empty()) {
639 TRACE(REQ,
" XrdHttpReq::Redir Redirecting to " <<
redirdest.c_str());
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=";
743void XrdHttpReq::sanitizeResourcePfx() {
752 if (
resource.beginswith(
"http://")) {
774void XrdHttpReq::parseResource(
char *res) {
780 char *p = strchr(res,
'?');
788 sanitizeResourcePfx();
813 sanitizeResourcePfx();
829 opaque =
new XrdOucEnv(decoded.c_str());
835void XrdHttpReq::generateWebdavErrMsg() {
841 httpStatusCode = 200;
842 httpErrorBody =
"OK";
848 httpErrorBody =
etext +
"\n";
864 outCksum = prot->cksumHandler.getChecksumToRunWantDigest(
m_want_digest);
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);
986 if (
resource.beginswith(
"/static/")) {
997 if (prot->embeddedstatic) {
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);
1015 if (prot->staticredir) {
1018 s.
append(prot->staticredir);
1026 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1033 if (prot->staticpreload) {
1036 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1063 xrdreq.open.dlen = htonl(l);
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");
1101 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1102 generateWebdavErrMsg();
1103 return sendFooterError(
"Could not run close request on the bridge");
1112 if (prot->listdeny) {
1114 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1118 if (prot->listredir) {
1120 s.
append(prot->listredir);
1128 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1139 l = res.length() + 1;
1140 xrdreq.dirlist.dlen = htonl(l);
1142 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) res.c_str(), l)) {
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();
1178 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
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;
1208 xrdreq.read.offset = htonll(offs);
1209 xrdreq.read.rlen = htonl(l);
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());
1244 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
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");
1282 xrdreq.open.dlen = htonl(l);
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);
1387 xrdreq.write.dlen = htonl(bytes_to_write);
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()),
1410 xrdreq.write.dlen = htonl(bytes_to_read);
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");
1436 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
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;
1475 xrdreq.stat.dlen = htonl(l);
1478 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1495 xrdreq.rmdir.dlen = htonl(l);
1497 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1498 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1509 xrdreq.rm.dlen = htonl(l);
1511 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
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);
1569 xrdreq.stat.dlen = htonl(l);
1572 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1601 xrdreq.dirlist.dlen = htonl(l);
1603 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1604 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1627 xrdreq.mkdir.dlen = htonl(l);
1629 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
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;
1672 xrdreq.mv.dlen = htonl(l);
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);
1695XrdHttpReq::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);
1745XrdHttpReq::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 ";
1881 if (prot->SecEntity.name)
1886 if (prot->SecEntity.vorg ||
1887 prot->SecEntity.name ||
1888 prot->SecEntity.moninfo ||
1889 prot->SecEntity.role)
1892 if (prot->SecEntity.vorg) {
1897 if (prot->SecEntity.moninfo) {
1901 if (prot->SecEntity.name) {
1906 if (prot->SecEntity.role) {
1909 if (prot->SecEntity.endorsements) {
1916 if (prot->SecEntity.vorg ||
1917 prot->SecEntity.moninfo ||
1918 prot->SecEntity.role)
1921 if (prot->SecEntity.host) {
1941XrdHttpReq::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";
2043int 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);
2219 const XrdHttpReadRangeHandler::Error &rrerror =
readRangeHandler.getError();
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);
2302 int l = ntohl(
xrdreq.write.dlen);
2305 prot->BuffConsume(ntohl(
xrdreq.write.dlen));
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";
2454 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2458 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2459 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2461 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2466 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2476 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";
2479 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2493 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2497 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
2499 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2500 strncpy(entry, (
char *) startp, endp - startp);
2501 entry[endp - startp] = 0;
2508 <<
" stat=" << endp);
2511 sscanf(endp,
"%ld %lld %ld %ld",
2519 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2539 if (*p.rbegin() !=
'/') p +=
"/";
2543 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2567 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2568 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2570 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2574 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2575 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2577 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2580 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2588 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2589 if (pp) startp = pp+1;
2600 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";
2603 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2623 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2625 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2626 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2631 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2639 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2643 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2656 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2657 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2670int XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2671 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2672 std::stringstream ss;
2674 ss << httpStatusCode;
2675 if (!httpErrorBody.empty()) {
2676 std::string_view statusView(httpErrorBody);
2679 if (!statusView.empty() && statusView.back() ==
'\n') {
2680 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2682 ss <<
": " << httpErrorBody;
2686 if (!extra_text.empty()) ss <<
": " << extra_text;
2690 const std::string trailer =
"X-Transfer-Status: " + ss.str();
2693 if (prot->ChunkResp(trailer.c_str(), -1))
return -1;
2697 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2702void XrdHttpReq::addAgeHeader(std::string &headers) {
2704 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2707void XrdHttpReq::addETagHeader(std::string &headers) {
2708 headers += std::string(
"Etag: \"") + std::to_string(
etagval) +
"\"";
2713 TRACE(REQ,
" XrdHttpReq request ended.");
2749 httpStatusCode = -1;
2750 initialStatusCode= -1;
2761 m_transfer_encoding_chunked =
false;
2762 m_current_chunk_size = -1;
2763 m_current_chunk_offset = 0;
2765 m_trailer_headers =
false;
2766 m_status_trailer =
false;
2801 startTime = std::chrono::steady_clock::time_point::min();
2804void XrdHttpReq::getfhandle() {
2807 TRACEI(REQ,
"fhandle:" <<
2821 for (
int i = 0; i <
iovN; i++) {
2823 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2824 l = (readahead_list *) p;
2825 len = ntohl(l->
rlen);
2827 received.emplace_back(p+
sizeof(readahead_list), -1, len);
2829 p +=
sizeof (readahead_list);
2838 for (
int i = 0; i <
iovN; i++) {
2839 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2844int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2846 if (received.size() == 0) {
2860 const XrdOucIOVec2 *ci;
2861 const XrdHttpReadRangeHandler::UserRange *ur;
2862 std::string st_header;
2863 std::string fin_header;
2870 std::vector<rinfo> rvec;
2873 rvec.reserve(received.size());
2875 for(
const auto &rcv: received) {
2878 const XrdHttpReadRangeHandler::UserRange *ur;
2884 rentry.start = start;
2885 rentry.finish = finish;
2894 rentry.st_header = s;
2895 sum_len += s.size();
2898 sum_len += rcv.size;
2902 rentry.fin_header = s;
2903 sum_len += s.size();
2906 rvec.push_back(rentry);
2911 if (m_transfer_encoding_chunked && m_trailer_headers) {
2912 prot->ChunkRespHeader(sum_len);
2916 for(
const auto &rentry: rvec) {
2919 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2920 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2926 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2930 if (rentry.finish) {
2931 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2938 if (m_transfer_encoding_chunked && m_trailer_headers) {
2939 prot->ChunkRespFooter();
2945int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2948 if (received.size() == 0) {
2958 for(
const auto &rcv: received) {
2960 if (
readRangeHandler.NotifyReadResult(rcv.size,
nullptr, start, finish) < 0) {
2967 if (m_transfer_encoding_chunked && m_trailer_headers) {
2968 prot->ChunkRespHeader(sum);
2970 for(
const auto &rcv: received) {
2971 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2973 if (m_transfer_encoding_chunked && m_trailer_headers) {
2974 prot->ChunkRespFooter();
struct ClientSetRequest set
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)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
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
std::string getXRootDConfigDigestName() const
virtual int ProcessReq(XrdHttpExtReq &)=0
std::vector< UserRange > UserRangeList
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
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)
const char * c_str() const
static void trim(std::string &str)
char * vorg
Entity's virtual organization(s).
int credslen
Length of the 'creds' data.
char * creds
Raw entity credentials or cert.
char * grps
Entity's group name(s).
char * name
Entity's name.
char * role
Entity's role(s).
char * endorsements
Protocol specific endorsements.
char * moninfo
Information for monitoring.
char * host
Entity's host name dnr dependent.
virtual int Send(const struct iovec *headP, int headN, const struct iovec *tailP, int tailN)