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 if (PostProcessHTTPReq())
reset();
557 if (strncmp(hname,
"file://", 7) == 0)
559 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
566 char *pp = strchr((
char *)hname,
'?');
572 int varlen = strlen(vardata);
575 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
584 sprintf(buf,
":%d", port);
628 return ret_keepalive;
639 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
658 s +=
"&xrdhttptime=";
660 sprintf(buf,
"%lld", (
long long) tnow);
665 s +=
"&xrdhttpname=";
671 s +=
"&xrdhttpvorg=";
676 s +=
"&xrdhttphost=";
686 s +=
"&xrdhttprole=";
691 s +=
"&xrdhttpgrps=";
696 s +=
"&xrdhttpendorsements=";
701 s +=
"&xrdhttpcredslen=";
703 sprintf(buf,
"%d", secent->
credslen);
709 s +=
"&xrdhttpcreds=";
723void XrdHttpReq::sanitizeResourcePfx() {
754void XrdHttpReq::parseResource(
char *res) {
760 char *p = strchr(res,
'?');
768 sanitizeResourcePfx();
793 sanitizeResourcePfx();
821void XrdHttpReq::mapXrdErrorToHttpStatus() {
823 httpStatusCode = 500;
824 httpStatusText =
"Unrecognized error";
830 httpStatusCode = 401; httpStatusText =
"Unauthorized";
833 httpStatusCode = 403; httpStatusText =
"Operation not permitted";
836 httpStatusCode = 404; httpStatusText =
"File not found";
839 httpStatusCode = 405; httpStatusText =
"Operation not supported";
842 httpStatusCode = 423; httpStatusText =
"Resource is a locked";
845 httpStatusCode = 409; httpStatusText =
"Resource is a directory";
849 httpStatusCode = 409; httpStatusText =
"File already exists";
854 httpStatusCode = 405;
858 httpStatusCode = 405; httpStatusText =
"Method is not allowed";
861 httpStatusCode = 502; httpStatusText =
"Bad Gateway";
864 httpStatusCode = 504; httpStatusText =
"Gateway timeout";
873 <<
"] to status code [" << httpStatusCode <<
"]");
875 httpStatusText +=
"\n";
877 httpStatusCode = 200;
878 httpStatusText =
"OK";
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() <<
"'");
944 if (r < 0)
return -1;
958 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
964 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
973 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
985 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
997 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1021 if (
resource ==
"/static/css/xrdhttp.css") {
1022 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1026 if (
resource ==
"/static/icons/xrdhttp.ico") {
1027 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1047 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1057 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1089 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1107 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1119 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1124 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1135 mapXrdErrorToHttpStatus();
1136 sendFooterError(
"Could not run close request on the bridge");
1147 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1161 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1172 l = res.length() + 1;
1176 mapXrdErrorToHttpStatus();
1177 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1178 sendFooterError(
"Could not run listing request on the bridge");
1191 auto retval = ReturnGetHeaders();
1204 if ( readChunkList.empty() )
1212 TRACEI(REQ,
" Failed to run close request on the bridge.");
1226 if ( readChunkList.size() == 1 ) {
1238 offs = readChunkList[0].offset;
1239 l = readChunkList[0].size;
1247 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1250 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1259 TRACE(ALL,
" Data sizes mismatch.");
1263 TRACE(ALL,
" No more bytes to send.");
1270 httpStatusCode = 416;
1271 httpStatusText =
"Range Not Satisfiable";
1272 std::stringstream ss;
1273 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1274 sendFooterError(ss.str());
1279 mapXrdErrorToHttpStatus();
1280 sendFooterError(
"Could not run read request on the bridge");
1289 mapXrdErrorToHttpStatus();
1290 sendFooterError(
"Could not run ReadV request on the bridge");
1320 if (! XrdHttpProtocol::usingEC)
1326 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1341 if (m_transfer_encoding_chunked) {
1342 if (m_current_chunk_size == m_current_chunk_offset) {
1345 if (prot->BuffUsed() < 2)
return 1;
1346 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1347 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1350 prot->BuffConsume(2);
1351 if (m_current_chunk_size == 0) {
1355 m_transfer_encoding_chunked =
false;
1359 m_current_chunk_size = -1;
1360 m_current_chunk_offset = 0;
1362 if (!prot->BuffUsed())
return 1;
1364 if (-1 == m_current_chunk_size) {
1368 bool found_newline =
false;
1375 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1376 for (; idx < max_chunk_size_chars; idx++) {
1377 if (prot->myBuffStart[idx] ==
'\n') {
1378 found_newline =
true;
1384 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1385 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1386 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1389 if (found_newline) {
1390 char *endptr = NULL;
1391 std::string line_contents(prot->myBuffStart, idx);
1392 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1394 if (*endptr !=
';' && *endptr !=
'\r') {
1395 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1396 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1399 m_current_chunk_size = chunk_contents;
1400 m_current_chunk_offset = 0;
1401 prot->BuffConsume(idx + 1);
1402 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1409 if (m_current_chunk_size == 0) {
1418 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1419 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1420 chunk_bytes_remaining);
1425 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1426 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1427 mapXrdErrorToHttpStatus();
1428 sendFooterError(
"Could not run write request on the bridge");
1433 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1443 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1449 TRACEI(REQ,
"Writing " << bytes_to_read);
1450 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1451 mapXrdErrorToHttpStatus();
1452 sendFooterError(
"Could not run write request on the bridge");
1475 mapXrdErrorToHttpStatus();
1476 sendFooterError(
"Could not run close request on the bridge");
1492 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);
1495 return ret_keepalive ? 1 : -1;
1517 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1537 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1551 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1567 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1582 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1587 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1592 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1611 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1643 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1669 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1690 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1695 strcpy(buf2,
host.c_str());
1696 char *pos = strchr(buf2,
':');
1697 if (pos) *pos =
'\0';
1705 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1719 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1729 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1740XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1743 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1748 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1749 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1752 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1753 if (convert_to_base64) {
1754 size_t digest_length = strlen(digest_value);
1755 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1756 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1757 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1758 free(digest_binary_value);
1761 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1763 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1764 free(digest_binary_value);
1765 digest_value = digest_base64_value;
1768 digest_header =
"Digest: ";
1770 digest_header +=
"=";
1771 digest_header += digest_value;
1772 if (convert_to_base64) {free(digest_value);}
1775 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1781XrdHttpReq::PostProcessListing(
bool final_) {
1784 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1785 httpStatusText.c_str(), httpStatusText.length(),
false);
1791 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1792 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1794 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1795 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1796 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1817 "<th class=\"mode\">Mode</th>"
1818 "<th class=\"flags\">Flags</th>"
1819 "<th class=\"size\">Size</th>"
1820 "<th class=\"datetime\">Modified</th>"
1821 "<th class=\"name\">Name</th>"
1827 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1830 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1832 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1833 strncpy(entry, (
char *) startp, endp - startp);
1834 entry[endp - startp] = 0;
1841 <<
" stat=" << endp);
1844 sscanf(endp,
"%ld %lld %ld %ld",
1850 strcpy(entry, (
char *) startp);
1852 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1854 std::string p =
"<tr>"
1855 "<td class=\"mode\">";
1876 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1877 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1879 "<td class=\"name\">"
1887 if (!p.empty() && p[p.size() - 1] !=
'/')
1892 std::unique_ptr<char,
decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1898 p +=
"</a></td></tr>";
1904 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1905 if (pp) startp = pp+1;
1914 stringresp +=
"</table></div><br><br><hr size=1>"
1915 "<p><span id=\"requestby\">Request by ";
1977XrdHttpReq::ReturnGetHeaders() {
1978 std::string responseHeader;
1983 if (!responseHeader.empty()) {
1984 responseHeader +=
"\r\n";
1986 addAgeHeader(responseHeader);
1998 if (m_transfer_encoding_chunked && m_trailer_headers) {
1999 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2001 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2009 if (uranges.size() != 1)
2014 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2017 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2019 if (!responseHeader.empty()) {
2021 s += responseHeader.
c_str();
2024 if (m_transfer_encoding_chunked && m_trailer_headers) {
2025 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2027 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2034 for (
auto &ur : uranges) {
2035 cnt += ur.end - ur.start + 1;
2040 (
char *)
"123456").size();
2044 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2050 if (!header.empty()) {
2053 addAgeHeader(header);
2056 if (m_transfer_encoding_chunked && m_trailer_headers) {
2057 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2059 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2066int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2068 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2069 mapXrdErrorToHttpStatus();
2074 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2083 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2088 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2095 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2099 std::string response_headers;
2103 <<
" stat=" << (
char *)
iovP[0].iov_base);
2106 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2116 addAgeHeader(response_headers);
2117 response_headers +=
"\r\n";
2119 response_headers +=
"Accept-Ranges: bytes";
2120 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2125 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2128 return ret_keepalive ? 1 : -1;
2131 std::string response_headers;
2132 int response = PostProcessChecksum(response_headers);
2133 if (-1 == response) {
2136 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2138 addAgeHeader(response_headers);
2139 response_headers +=
"\r\n";
2141 response_headers +=
"Accept-Ranges: bytes";
2142 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2145 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2167 if (
iovP[1].iov_len > 1) {
2169 <<
" stat=" << (
char *)
iovP[1].iov_base);
2172 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2193 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2194 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2203 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2204 httpStatusText.c_str(), httpStatusText.length(),
false);
2217 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2218 httpStatusText.c_str(), httpStatusText.length(),
false);
2225 return PostProcessListing(final_);
2235 httpStatusText = rrerror.
errMsg;
2238 if (m_transfer_encoding_chunked && m_trailer_headers) {
2239 if (prot->ChunkRespHeader(0))
2242 const std::string crlf =
"\r\n";
2243 std::stringstream ss;
2244 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2246 const auto header = ss.str();
2247 if (prot->SendData(header.c_str(), header.size()))
2250 if (prot->ChunkRespFooter())
2254 if (rrerror)
return -1;
2261 sendFooterError(
"");
2266 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2269 getReadResponse(received);
2273 rc = sendReadResponseSingleRange(received);
2275 rc = sendReadResponsesMultiRanges(received);
2296 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2297 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2305 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2308 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2327 if (m_transfer_encoding_chunked) {
2328 m_current_chunk_offset += l;
2332 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2339 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2342 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2343 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2364 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2365 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2380 <<
" stat=" << (
char *)
iovP[0].iov_base);
2383 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2395 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2398 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2399 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2411 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2412 httpStatusText.c_str(), httpStatusText.length(),
false);
2430 <<
" stat=" << (
char *)
iovP[0].iov_base);
2433 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2439 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2444 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2472 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2473 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2475 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2479 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2480 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2482 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2487 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2497 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";
2500 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2514 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2518 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2520 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2521 strncpy(entry, (
char *) startp, endp - startp);
2522 entry[endp - startp] = 0;
2529 <<
" stat=" << endp);
2532 sscanf(endp,
"%ld %lld %ld %ld",
2540 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2560 if (*p.rbegin() !=
'/') p +=
"/";
2564 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2588 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2589 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2591 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2595 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2596 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2598 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2601 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2609 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2610 if (pp) startp = pp+1;
2621 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";
2624 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2644 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2646 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2647 httpStatusText.c_str(), httpStatusText.length(),
false);
2652 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2660 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2664 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2677 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2678 httpStatusText.c_str(), httpStatusText.length(),
false);
2692XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2693 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2701 if (prot->ChunkRespHeader(0))
2704 std::stringstream ss;
2705 ss << httpStatusCode <<
": " << httpStatusText;
2706 if (!extra_text.empty())
2707 ss <<
": " << extra_text;
2711 const auto header =
"X-Transfer-Status: " + ss.str();
2712 if (prot->SendData(header.c_str(), header.size()))
2715 prot->ChunkRespFooter();
2717 TRACEI(REQ, httpStatusCode <<
": " << httpStatusText << (extra_text.empty() ?
"" : (
": " + extra_text)));
2721void XrdHttpReq::addAgeHeader(std::string &headers) {
2723 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2728 TRACE(REQ,
" XrdHttpReq request ended.");
2770 m_transfer_encoding_chunked =
false;
2771 m_current_chunk_size = -1;
2772 m_current_chunk_offset = 0;
2774 m_trailer_headers =
false;
2775 m_status_trailer =
false;
2810void XrdHttpReq::getfhandle() {
2813 TRACEI(REQ,
"fhandle:" <<
2827 for (
int i = 0; i <
iovN; i++) {
2829 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2831 len = ntohl(l->
rlen);
2844 for (
int i = 0; i <
iovN; i++) {
2845 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2850int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2852 if (received.size() == 0) {
2868 std::string st_header;
2869 std::string fin_header;
2876 std::vector<rinfo> rvec;
2879 rvec.reserve(received.size());
2881 for(
const auto &rcv: received) {
2890 rentry.start = start;
2891 rentry.finish = finish;
2900 rentry.st_header = s;
2901 sum_len += s.size();
2904 sum_len += rcv.size;
2908 rentry.fin_header = s;
2909 sum_len += s.size();
2912 rvec.push_back(rentry);
2917 if (m_transfer_encoding_chunked && m_trailer_headers) {
2918 prot->ChunkRespHeader(sum_len);
2922 for(
const auto &rentry: rvec) {
2925 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2926 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2932 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2936 if (rentry.finish) {
2937 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2944 if (m_transfer_encoding_chunked && m_trailer_headers) {
2945 prot->ChunkRespFooter();
2951int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2954 if (received.size() == 0) {
2964 for(
const auto &rcv: received) {
2973 if (m_transfer_encoding_chunked && m_trailer_headers) {
2974 prot->ChunkRespHeader(sum);
2976 for(
const auto &rcv: received) {
2977 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2979 if (m_transfer_encoding_chunked && m_trailer_headers) {
2980 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
std::vector< UserRange > UserRangeList
int NotifyReadResult(const ssize_t ret, const UserRange **const urp, bool &start, bool &allend)
Advance internal counters concerning received bytes.
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.
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.
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 insert(const int i, int start=-1)
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