39#include "XrdVersion.hh"
66#define MAX_RESOURCE_LEN 16384
69#define TRACELINK prot->Link
73const char *TraceID =
"Req";
86 memset(&t1, 0,
sizeof (t1));
89 strftime(datebuf, 127,
"%a, %d %b %Y %H:%M:%S GMT", &t1);
90 return (std::string) datebuf;
122 if (!line)
return -1;
125 char *p = strchr((
char *) line, (
int)
':');
141 char *val = line + pos + 1;
144 while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
148 std::string ss = val;
149 if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) !=
"\r\n") {
162 if (!strcasecmp(key,
"connection")) {
164 if (!strcasecmp(val,
"Keep-Alive\r\n")) {
166 }
else if (!strcasecmp(val,
"close\r\n")) {
170 }
else if (!strcasecmp(key,
"host")) {
172 }
else if (!strcasecmp(key,
"range")) {
177 }
else if (!strcasecmp(key,
"content-length")) {
180 }
else if (!strcasecmp(key,
"destination")) {
183 }
else if (!strcasecmp(key,
"want-digest")) {
188 }
else if (!strcasecmp(key,
"depth")) {
190 if (strcmp(val,
"infinity"))
193 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
195 }
else if (!strcasecmp(key,
"te") && strstr(val,
"trailers")) {
196 m_trailer_headers =
true;
197 }
else if (!strcasecmp(key,
"transfer-encoding") && strstr(val,
"chunked")) {
198 m_transfer_encoding_chunked =
true;
199 }
else if (!strcasecmp(key,
"x-transfer-status") && strstr(val,
"true")) {
200 m_transfer_encoding_chunked =
true;
201 m_status_trailer =
true;
202 }
else if (!strcasecmp(key,
"scitag")) {
203 if(prot->pmarkHandle !=
nullptr) {
206 }
else if (!strcasecmp(key,
"user-agent")) {
211 auto it = std::find_if(prot->hdr2cgimap.begin(), prot->hdr2cgimap.end(),[key](
const auto & item) {
212 return !strcasecmp(key,item.first.c_str());
214 if (it != prot->hdr2cgimap.end() && (
opaque ? (0 ==
opaque->Get(it->second.c_str())) :
true)) {
216 s.assign(val, line+len-val);
229int XrdHttpReq::parseHost(
char *line) {
235void XrdHttpReq::parseScitag(
const std::string & val) {
239 std::string scitagS = val;
242 if(scitagS[0] !=
'-') {
244 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
268 if (!line)
return -1;
271 char *p = strchr((
char *) line, (
int)
' ');
295 char *val = line + pos + 1;
302 p = strchr((
char *) val, (
int)
' ');
316 if (!strcmp(key,
"GET")) {
318 }
else if (!strcmp(key,
"HEAD")) {
320 }
else if (!strcmp(key,
"PUT")) {
322 }
else if (!strcmp(key,
"POST")) {
324 }
else if (!strcmp(key,
"PATCH")) {
326 }
else if (!strcmp(key,
"OPTIONS")) {
328 }
else if (!strcmp(key,
"DELETE")) {
330 }
else if (!strcmp(key,
"PROPFIND")) {
333 }
else if (!strcmp(key,
"MKCOL")) {
336 }
else if (!strcmp(key,
"MOVE")) {
346 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
360void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
367 for (
int i = 0; i < nitems; i++) {
378void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
385 for (
int i = 0; i < nitems; i++) {
403 for (
const auto &c: cl) {
406 memcpy(&ra.fhandle, this->fhandle, 4);
408 ra.offset = c.offset;
422 clientMarshallReadAheadList(j);
431 std::ostringstream s;
433 s <<
"\r\n--" << token <<
"\r\n";
434 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
435 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
441 std::ostringstream s;
443 s <<
"\r\n--" << token <<
"--\r\n";
456 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
462 this->
final = final_;
464 if (PostProcessHTTPReq(final_))
reset();
478 int rc = info.
Send(0, 0, 0, 0);
479 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
496 TRACE(REQ,
" XrdHttpReq::Done");
502 int r = PostProcessHTTPReq(
true);
505 if (r < 0)
return false;
516 TRACE(REQ,
" XrdHttpReq::Error");
527 auto rc = PostProcessHTTPReq();
552 if (prot->isdesthttps)
560 if (strncmp(hname,
"file://", 7) == 0)
562 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
569 char *pp = strchr((
char *)hname,
'?');
575 int varlen = strlen(vardata);
578 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
587 sprintf(buf,
":%d", port);
606 if (!prot->isdesthttps && prot->ishttps) {
631 return ret_keepalive;
642 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
661 s +=
"&xrdhttptime=";
663 sprintf(buf,
"%lld", (
long long) tnow);
668 s +=
"&xrdhttpname=";
674 s +=
"&xrdhttpvorg=";
679 s +=
"&xrdhttphost=";
689 s +=
"&xrdhttprole=";
694 s +=
"&xrdhttpgrps=";
699 s +=
"&xrdhttpendorsements=";
704 s +=
"&xrdhttpcredslen=";
706 sprintf(buf,
"%d", secent->
credslen);
712 s +=
"&xrdhttpcreds=";
726void XrdHttpReq::sanitizeResourcePfx() {
735 if (
resource.beginswith(
"http://")) {
757void XrdHttpReq::parseResource(
char *res) {
763 char *p = strchr(res,
'?');
771 sanitizeResourcePfx();
796 sanitizeResourcePfx();
812 opaque =
new XrdOucEnv(decoded.c_str());
821void XrdHttpReq::sendWebdavErrorMessage(
823 XRequestTypes xrdOperation, std::string etext,
const char *desc,
824 const char *header_to_add,
bool keepalive) {
826 std::string errCode{
"Unknown"};
827 std::string statusText;
862 httpStatusCode = code;
863 httpErrorCode = errCode;
864 httpErrorBody =
"ERROR: " + errCode +
": " +
etext +
"\n";
866 prot->SendSimpleResp(httpStatusCode, desc, header_to_add,
867 httpErrorBody.c_str(), httpErrorBody.length(),
875void XrdHttpReq::mapXrdErrorToHttpStatus() {
877 httpStatusCode = 500;
878 httpErrorBody =
"Unrecognized error";
884 httpStatusCode = 401; httpErrorBody =
"Unauthorized";
887 httpStatusCode = 403; httpErrorBody =
"Operation not permitted";
890 httpStatusCode = 404; httpErrorBody =
"File not found";
893 httpStatusCode = 405; httpErrorBody =
"Operation not supported";
896 httpStatusCode = 423; httpErrorBody =
"Resource is a locked";
899 httpStatusCode = 409; httpErrorBody =
"Resource is a directory";
903 httpStatusCode = 409; httpErrorBody =
"File already exists";
908 httpStatusCode = 405;
912 httpStatusCode = 405; httpErrorBody =
"Method is not allowed";
915 httpStatusCode = 502; httpErrorBody =
"Bad Gateway";
918 httpStatusCode = 504; httpErrorBody =
"Gateway timeout";
927 <<
"] to status code [" << httpStatusCode <<
"]");
929 httpErrorBody +=
"\n";
931 httpStatusCode = 200;
932 httpErrorBody =
"OK";
944 int query_param_status = 0;
948 if (query_param_status == 0) {
952 query_param_status = 1;
953 auto length_str = std::to_string(
length);
959 opaque->Put(
"oss.asize", length_str.c_str());
965 if (query_param_status == 0) {
977 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
978 << header2cgistrObf.c_str() <<
"'");
998 if (r < 0)
return -1;
1012 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
1018 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
1027 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1039 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1051 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1061 if (
resource.beginswith(
"/static/")) {
1072 if (prot->embeddedstatic) {
1075 if (
resource ==
"/static/css/xrdhttp.css") {
1076 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1080 if (
resource ==
"/static/icons/xrdhttp.ico") {
1081 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1090 if (prot->staticredir) {
1093 s.
append(prot->staticredir);
1101 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1108 if (prot->staticpreload) {
1111 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1138 xrdreq.open.dlen = htonl(l);
1143 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1161 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1173 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1178 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1188 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1189 mapXrdErrorToHttpStatus();
1190 return sendFooterError(
"Could not run close request on the bridge");
1199 if (prot->listdeny) {
1200 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1204 if (prot->listredir) {
1206 s.
append(prot->listredir);
1214 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1225 l = res.length() + 1;
1226 xrdreq.dirlist.dlen = htonl(l);
1228 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) res.c_str(), l)) {
1229 mapXrdErrorToHttpStatus();
1230 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1231 sendFooterError(
"Could not run listing request on the bridge");
1244 auto retval = ReturnGetHeaders();
1264 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1265 TRACEI(REQ,
" Failed to run close request on the bridge.");
1279 if ( readChunkList.size() == 1 ) {
1291 offs = readChunkList[0].offset;
1292 l = readChunkList[0].size;
1294 xrdreq.read.offset = htonll(offs);
1295 xrdreq.read.rlen = htonl(l);
1300 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1303 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1312 TRACE(ALL,
" Data sizes mismatch.");
1316 TRACE(ALL,
" No more bytes to send.");
1323 httpStatusCode = 416;
1324 httpErrorBody =
"Range Not Satisfiable";
1325 std::stringstream ss;
1326 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1327 return sendFooterError(ss.str());
1330 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1331 mapXrdErrorToHttpStatus();
1332 return sendFooterError(
"Could not run read request on the bridge");
1340 mapXrdErrorToHttpStatus();
1341 return sendFooterError(
"Could not run ReadV request on the bridge");
1368 xrdreq.open.dlen = htonl(l);
1370 if (! XrdHttpProtocol::usingEC)
1376 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1391 if (m_transfer_encoding_chunked) {
1392 if (m_current_chunk_size == m_current_chunk_offset) {
1395 if (prot->BuffUsed() < 2)
return 1;
1396 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1397 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1400 prot->BuffConsume(2);
1401 if (m_current_chunk_size == 0) {
1405 m_transfer_encoding_chunked =
false;
1409 m_current_chunk_size = -1;
1410 m_current_chunk_offset = 0;
1412 if (!prot->BuffUsed())
return 1;
1414 if (-1 == m_current_chunk_size) {
1418 bool found_newline =
false;
1425 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1426 for (; idx < max_chunk_size_chars; idx++) {
1427 if (prot->myBuffStart[idx] ==
'\n') {
1428 found_newline =
true;
1434 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1435 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1436 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1439 if (found_newline) {
1440 char *endptr = NULL;
1441 std::string line_contents(prot->myBuffStart, idx);
1442 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1444 if (*endptr !=
';' && *endptr !=
'\r') {
1445 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1446 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1449 m_current_chunk_size = chunk_contents;
1450 m_current_chunk_offset = 0;
1451 prot->BuffConsume(idx + 1);
1452 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1459 if (m_current_chunk_size == 0) {
1468 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1469 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1470 chunk_bytes_remaining);
1473 xrdreq.write.dlen = htonl(bytes_to_write);
1475 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1476 if (!prot->Bridge->Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1477 mapXrdErrorToHttpStatus();
1478 return sendFooterError(
"Could not run write request on the bridge");
1482 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1492 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1496 xrdreq.write.dlen = htonl(bytes_to_read);
1498 TRACEI(REQ,
"Writing " << bytes_to_read);
1499 if (!prot->Bridge->Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1500 mapXrdErrorToHttpStatus();
1501 return sendFooterError(
"Could not run write request on the bridge");
1522 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1523 mapXrdErrorToHttpStatus();
1524 return sendFooterError(
"Could not run close request on the bridge");
1539 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);
1542 return ret_keepalive ? 1 : -1;
1561 xrdreq.stat.dlen = htonl(l);
1564 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1581 xrdreq.rmdir.dlen = htonl(l);
1583 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1584 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1595 xrdreq.rm.dlen = htonl(l);
1597 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1598 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1614 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1629 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1634 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1639 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1655 xrdreq.stat.dlen = htonl(l);
1658 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1687 xrdreq.dirlist.dlen = htonl(l);
1689 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1690 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1713 xrdreq.mkdir.dlen = htonl(l);
1715 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1716 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1737 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1742 strcpy(buf2,
host.c_str());
1743 char *pos = strchr(buf2,
':');
1744 if (pos) *pos =
'\0';
1752 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1762 xrdreq.mv.dlen = htonl(l);
1765 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1766 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1776 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1787XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1790 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1795 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1796 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1798 bool convert_to_base64 =
m_req_cksum->needsBase64Padding();
1799 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1800 if (convert_to_base64) {
1801 size_t digest_length = strlen(digest_value);
1802 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1803 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1804 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1805 free(digest_binary_value);
1808 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1810 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1811 free(digest_binary_value);
1812 digest_value = digest_base64_value;
1815 digest_header =
"Digest: ";
1817 digest_header +=
"=";
1818 digest_header += digest_value;
1819 if (convert_to_base64) {free(digest_value);}
1822 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1828XrdHttpReq::PostProcessListing(
bool final_) {
1831 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1832 httpErrorBody.c_str(), httpErrorBody.length(),
false);
1838 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1839 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1841 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1842 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1843 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1864 "<th class=\"mode\">Mode</th>"
1865 "<th class=\"flags\">Flags</th>"
1866 "<th class=\"size\">Size</th>"
1867 "<th class=\"datetime\">Modified</th>"
1868 "<th class=\"name\">Name</th>"
1874 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1877 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
1879 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1880 strncpy(entry, (
char *) startp, endp - startp);
1881 entry[endp - startp] = 0;
1888 <<
" stat=" << endp);
1891 sscanf(endp,
"%ld %lld %ld %ld",
1897 strcpy(entry, (
char *) startp);
1899 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1901 std::string p =
"<tr>"
1902 "<td class=\"mode\">";
1923 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1924 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1926 "<td class=\"name\">"
1934 if (!p.empty() && p[p.size() - 1] !=
'/')
1939 std::unique_ptr<char,
decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1945 p +=
"</a></td></tr>";
1951 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1952 if (pp) startp = pp+1;
1961 stringresp +=
"</table></div><br><br><hr size=1>"
1962 "<p><span id=\"requestby\">Request by ";
1964 if (prot->SecEntity.name)
1969 if (prot->SecEntity.vorg ||
1970 prot->SecEntity.name ||
1971 prot->SecEntity.moninfo ||
1972 prot->SecEntity.role)
1975 if (prot->SecEntity.vorg) {
1980 if (prot->SecEntity.moninfo) {
1984 if (prot->SecEntity.name) {
1989 if (prot->SecEntity.role) {
1992 if (prot->SecEntity.endorsements) {
1999 if (prot->SecEntity.vorg ||
2000 prot->SecEntity.moninfo ||
2001 prot->SecEntity.role)
2004 if (prot->SecEntity.host) {
2024XrdHttpReq::ReturnGetHeaders() {
2025 std::string responseHeader;
2030 if (!responseHeader.empty()) {
2031 responseHeader +=
"\r\n";
2033 addAgeHeader(responseHeader);
2045 if (m_transfer_encoding_chunked && m_trailer_headers) {
2047 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2049 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2057 if (uranges.size() != 1)
2062 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2064 std::string header =
"Content-Range: bytes ";
2065 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2067 if (!responseHeader.empty()) {
2069 header += responseHeader.c_str();
2072 if (m_transfer_encoding_chunked && m_trailer_headers) {
2074 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
2076 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
2083 for (
auto &ur : uranges) {
2084 cnt += ur.end - ur.start + 1;
2089 (
char *)
"123456").size();
2093 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2099 if (!header.empty()) {
2102 addAgeHeader(header);
2105 if (m_transfer_encoding_chunked && m_trailer_headers) {
2107 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2109 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2115 if (m_status_trailer) {
2116 if (header.empty()) {
2117 header +=
"Trailer: X-Transfer-Status";
2119 header +=
"\r\nTrailer: X-Transfer-Status";
2126int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2128 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2129 mapXrdErrorToHttpStatus();
2134 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2143 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2148 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2155 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2159 std::string response_headers;
2163 <<
" stat=" << (
char *)
iovP[0].iov_base);
2166 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2176 addAgeHeader(response_headers);
2177 response_headers +=
"\r\n";
2179 response_headers +=
"Accept-Ranges: bytes";
2180 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2185 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2188 return ret_keepalive ? 1 : -1;
2191 std::string response_headers;
2192 int response = PostProcessChecksum(response_headers);
2193 if (-1 == response) {
2196 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2198 addAgeHeader(response_headers);
2199 response_headers +=
"\r\n";
2201 response_headers +=
"Accept-Ranges: bytes";
2202 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2205 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2227 if (
iovP[1].iov_len > 1) {
2229 <<
" stat=" << (
char *)
iovP[1].iov_base);
2232 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2253 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2254 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2263 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2264 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2277 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2278 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2285 return PostProcessListing(final_);
2295 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2299 const XrdHttpReadRangeHandler::Error &rrerror =
readRangeHandler.getError();
2302 httpErrorBody = rrerror.
errMsg;
2305 if (m_transfer_encoding_chunked && m_trailer_headers) {
2306 if (prot->ChunkRespHeader(0))
2309 const std::string crlf =
"\r\n";
2310 std::stringstream ss;
2311 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpErrorBody << crlf;
2313 const auto header = ss.str();
2314 if (prot->SendData(header.c_str(), header.size()))
2317 if (prot->ChunkRespFooter())
2321 if (rrerror)
return -1;
2328 auto rc = sendFooterError(
"");
2337 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2340 getReadResponse(received);
2344 rc = sendReadResponseSingleRange(received);
2346 rc = sendReadResponsesMultiRanges(received);
2374 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2377 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2389 int l = ntohl(
xrdreq.write.dlen);
2392 prot->BuffConsume(ntohl(
xrdreq.write.dlen));
2396 if (m_transfer_encoding_chunked) {
2397 m_current_chunk_offset += l;
2401 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2408 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2431 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2432 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2447 <<
" stat=" << (
char *)
iovP[0].iov_base);
2450 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2462 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2465 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2466 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2478 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2479 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2497 <<
" stat=" << (
char *)
iovP[0].iov_base);
2500 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2506 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2511 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2539 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2540 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2542 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2546 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2547 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2549 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2554 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2564 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";
2567 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2581 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2585 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
2587 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2588 strncpy(entry, (
char *) startp, endp - startp);
2589 entry[endp - startp] = 0;
2596 <<
" stat=" << endp);
2599 sscanf(endp,
"%ld %lld %ld %ld",
2607 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2627 if (*p.rbegin() !=
'/') p +=
"/";
2631 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2655 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2656 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2658 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2662 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2663 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2665 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2668 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2676 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2677 if (pp) startp = pp+1;
2688 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";
2691 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2711 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2713 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2714 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2719 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2727 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2731 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2744 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2745 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2759XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2760 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2768 if (prot->ChunkRespHeader(0))
2771 std::stringstream ss;
2773 ss << httpStatusCode;
2774 if (!httpErrorBody.empty()) {
2775 std::string_view statusView(httpErrorBody);
2778 if (statusView[statusView.size() - 1] ==
'\n') {
2779 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2781 ss <<
": " << httpErrorBody;
2785 if (!extra_text.empty())
2786 ss <<
": " << extra_text;
2790 const auto header =
"X-Transfer-Status: " + ss.str();
2791 if (prot->SendData(header.c_str(), header.size()))
2794 if (prot->ChunkRespFooter())
2799 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2805void XrdHttpReq::addAgeHeader(std::string &headers) {
2807 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2812 TRACE(REQ,
" XrdHttpReq request ended.");
2855 m_transfer_encoding_chunked =
false;
2856 m_current_chunk_size = -1;
2857 m_current_chunk_offset = 0;
2859 m_trailer_headers =
false;
2860 m_status_trailer =
false;
2895void XrdHttpReq::getfhandle() {
2898 TRACEI(REQ,
"fhandle:" <<
2912 for (
int i = 0; i <
iovN; i++) {
2914 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2915 l = (readahead_list *) p;
2916 len = ntohl(l->
rlen);
2918 received.emplace_back(p+
sizeof(readahead_list), -1, len);
2920 p +=
sizeof (readahead_list);
2929 for (
int i = 0; i <
iovN; i++) {
2930 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2935int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2937 if (received.size() == 0) {
2951 const XrdOucIOVec2 *ci;
2952 const XrdHttpReadRangeHandler::UserRange *ur;
2953 std::string st_header;
2954 std::string fin_header;
2961 std::vector<rinfo> rvec;
2964 rvec.reserve(received.size());
2966 for(
const auto &rcv: received) {
2969 const XrdHttpReadRangeHandler::UserRange *ur;
2975 rentry.start = start;
2976 rentry.finish = finish;
2985 rentry.st_header = s;
2986 sum_len += s.size();
2989 sum_len += rcv.size;
2993 rentry.fin_header = s;
2994 sum_len += s.size();
2997 rvec.push_back(rentry);
3002 if (m_transfer_encoding_chunked && m_trailer_headers) {
3003 prot->ChunkRespHeader(sum_len);
3007 for(
const auto &rentry: rvec) {
3010 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
3011 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
3017 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
3021 if (rentry.finish) {
3022 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
3029 if (m_transfer_encoding_chunked && m_trailer_headers) {
3030 prot->ChunkRespFooter();
3036int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
3039 if (received.size() == 0) {
3049 for(
const auto &rcv: received) {
3051 if (
readRangeHandler.NotifyReadResult(rcv.size,
nullptr, start, finish) < 0) {
3058 if (m_transfer_encoding_chunked && m_trailer_headers) {
3059 prot->ChunkRespHeader(sum);
3061 for(
const auto &rcv: received) {
3062 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3064 if (m_transfer_encoding_chunked && m_trailer_headers) {
3065 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.
int parseURL(char *url, char *host, int &port, char **path)
void Tobase64(const unsigned char *input, int length, char *out)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
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)
std::string obfuscateAuth(const std::string &input)
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::string m_digest_header
The computed digest for the HTTP response header.
std::string stringresp
If we want to give a string as a response, we compose it here.
XResponseType xrdresp
The last response data we got.
ReqType request
The request we got.
long long writtenbytes
In a long write, we track where we have arrived.
XrdOucEnv * opaque
The opaque data, after parsing.
const struct iovec * iovP
The latest data chunks got from the xrd layer. These are valid only inside the callbacks!
std::string m_req_digest
The requested digest type.
XrdOucString resourceplusopaque
The resource specified by the request, including all the opaque data.
virtual bool Data(XrdXrootd::Bridge::Context &info, const struct iovec *iovP, int iovN, int iovL, bool final)
std::string hdr2cgistr
Additional opaque info that may come from the hdr2cgi directive.
virtual bool Done(XrdXrootd::Bridge::Context &info)
the result context
std::string host
The host field specified in the req.
int parseFirstLine(char *line, int len)
Parse the first line of the header.
ReqType
These are the HTTP/DAV requests that we support.
int parseLine(char *line, int len)
Parse the header.
std::string buildPartialHdrEnd(char *token)
Build the closing part for a multipart response.
XrdHttpChecksumHandler::XrdHttpChecksumRawPtr m_req_cksum
The checksum that was ran for this request.
void setTransferStatusHeader(std::string &header)
bool m_appended_hdr2cgistr
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
bool m_appended_asize
Track whether we already appended the oss.asize argument for PUTs.
XrdOucString m_resource_with_digest
virtual bool Redir(XrdXrootd::Bridge::Context &info, int port, const char *hname)
virtual int File(XrdXrootd::Bridge::Context &info, int dlen)
std::map< std::string, std::string > allheaders
void addCgi(const std::string &key, const std::string &value)
ClientRequest xrdreq
The last issued xrd request, often pending.
std::string buildPartialHdr(long long bytestart, long long byteend, long long filesize, char *token)
Build a partial header for a multipart response.
XrdHttpReadRangeHandler readRangeHandler
Tracking the next ranges of data to read during GET.
virtual bool Error(XrdXrootd::Bridge::Context &info, int ecode, const char *etext)
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)