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")) {
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());
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();
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);
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() {
757void XrdHttpReq::parseResource(
char *res) {
763 char *p = strchr(res,
'?');
771 sanitizeResourcePfx();
796 sanitizeResourcePfx();
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);
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);
1101 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1111 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
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");
1189 mapXrdErrorToHttpStatus();
1190 return sendFooterError(
"Could not run close request on the bridge");
1200 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1214 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1225 l = res.length() + 1;
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();
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;
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());
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");
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);
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()),
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");
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;
1564 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1584 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
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);
1658 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1690 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
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);
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));
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 ";
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);
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);
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;) {
2916 len = ntohl(l->
rlen);
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) {
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) {
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) {
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 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.
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)
XrdHttpChecksumRawPtr getChecksumToRun(const std::string &userDigest) const
bool needsBase64Padding() const
std::string getXRootDConfigDigestName() const
std::string getHttpName() const
virtual int ProcessReq(XrdHttpExtReq &)=0
static char * secretkey
The key used to calculate the url hashes.
static kXR_int32 myRole
Our role.
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static XrdHttpChecksumHandler cksumHandler
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
XrdLink * Link
The link we are bound to.
int doStat(char *fname)
Perform a Stat request.
static bool isdesthttps
True if the redirections must be towards https targets.
static char * listredir
Url to redirect to in the case a listing is requested.
static bool listdeny
If true, any form of listing is denied.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
void reset()
resets this handler
const XrdHttpIOList & NextReadList()
return XrdHttpIOList for sending to read or readv
void ParseContentRange(const char *const line)
parse the line after a "Range: " http request header
int SetFilesize(const off_t sz)
sets the filesize, used during resolving and issuing range requests
void NotifyError()
Force handler to enter error state.
bool isFullFile()
indicates when there were no valid Range head ranges supplied
int NotifyReadResult(const ssize_t ret, const UserRange **const urp, bool &start, bool &allend)
Advance internal counters concerning received bytes.
std::vector< UserRange > UserRangeList
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::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)
char * ID
Pointer to the client's link identity.
static const int minTotID
static const int maxTotID
char * Get(const char *varname)
void Put(const char *varname, const char *value)
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)
virtual int setSF(kXR_char *fhandle, bool seton=false)=0
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0