39 #include "XrdVersion.hh"
44 #include <arpa/inet.h>
66 #define MAX_TK_LEN 256
67 #define MAX_RESOURCE_LEN 16384
70 #define TRACELINK prot->Link
74 const char *TraceID =
"Req";
77 void trim(std::string &str)
87 memset(&t1, 0,
sizeof (t1));
90 strftime(datebuf, 127,
"%a, %d %b %Y %H:%M:%S GMT", &t1);
91 return (std::string) datebuf;
123 if (!line)
return -1;
126 char *p = strchr((
char *) line, (
int)
':');
142 char *val = line + pos + 1;
145 while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
149 std::string ss = val;
150 if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) !=
"\r\n") {
163 if (!strcasecmp(key,
"connection")) {
165 if (!strcasecmp(val,
"Keep-Alive\r\n")) {
167 }
else if (!strcasecmp(val,
"close\r\n")) {
171 }
else if (!strcasecmp(key,
"host")) {
173 }
else if (!strcasecmp(key,
"range")) {
178 }
else if (!strcasecmp(key,
"content-length")) {
181 }
else if (!strcasecmp(key,
"destination")) {
184 }
else if (!strcasecmp(key,
"want-digest")) {
189 }
else if (!strcasecmp(key,
"depth")) {
191 if (strcmp(val,
"infinity"))
194 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
196 }
else if (!strcasecmp(key,
"te") && strstr(val,
"trailers")) {
197 m_trailer_headers =
true;
198 }
else if (!strcasecmp(key,
"transfer-encoding") && strstr(val,
"chunked")) {
199 m_transfer_encoding_chunked =
true;
200 }
else if (!strcasecmp(key,
"x-transfer-status") && strstr(val,
"true")) {
201 m_transfer_encoding_chunked =
true;
202 m_status_trailer =
true;
203 }
else if (!strcasecmp(key,
"scitag")) {
207 }
else if (!strcasecmp(key,
"user-agent")) {
210 }
else if (!strcasecmp(key,
"origin")) {
215 auto it = std::find_if(prot->
hdr2cgimap.begin(), prot->
hdr2cgimap.end(),[key](
const auto & item) {
216 return !strcasecmp(key,item.first.c_str());
220 s.assign(val, line+len-val);
233 int XrdHttpReq::parseHost(
char *line) {
239 void XrdHttpReq::parseScitag(
const std::string & val) {
243 std::string scitagS = val;
246 if(scitagS[0] !=
'-') {
248 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
261 addCgi(
"pmark.appname",this->
request == ReqType::rtGET ?
"http-get" :
"http-put");
272 if (!line)
return -1;
275 char *p = strchr((
char *) line, (
int)
' ');
299 char *val = line + pos + 1;
306 p = strchr((
char *) val, (
int)
' ');
320 if (!strcmp(key,
"GET")) {
322 }
else if (!strcmp(key,
"HEAD")) {
324 }
else if (!strcmp(key,
"PUT")) {
326 }
else if (!strcmp(key,
"POST")) {
328 }
else if (!strcmp(key,
"PATCH")) {
330 }
else if (!strcmp(key,
"OPTIONS")) {
332 }
else if (!strcmp(key,
"DELETE")) {
334 }
else if (!strcmp(key,
"PROPFIND")) {
337 }
else if (!strcmp(key,
"MKCOL")) {
340 }
else if (!strcmp(key,
"MOVE")) {
350 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
364 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
371 for (
int i = 0; i < nitems; i++) {
382 void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
389 for (
int i = 0; i < nitems; i++) {
407 for (
const auto &c: cl) {
410 memcpy(&ra.fhandle, this->fhandle, 4);
412 ra.offset = c.offset;
426 clientMarshallReadAheadList(j);
435 std::ostringstream s;
437 s <<
"\r\n--" << token <<
"\r\n";
438 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
439 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
445 std::ostringstream s;
447 s <<
"\r\n--" << token <<
"--\r\n";
460 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
466 this->
final = final_;
468 if (PostProcessHTTPReq(final_))
reset();
482 int rc = info.
Send(0, 0, 0, 0);
483 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
500 TRACE(REQ,
" XrdHttpReq::Done");
506 int r = PostProcessHTTPReq(
true);
509 if (r < 0)
return false;
520 TRACE(REQ,
" XrdHttpReq::Error");
531 auto rc = PostProcessHTTPReq();
556 bool invalid_host =
false;
561 for (
const char *c = hname; *c; ++c)
562 if (*c ==
'\r' || *c ==
'\n')
567 prot->SendSimpleResp(502,
nullptr,
nullptr,
"Invalid redirect host", 0,
false);
579 if (strncmp(hname,
"file://", 7) == 0)
581 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
588 char *pp = strchr((
char *)hname,
'?');
594 int varlen = strlen(vardata);
597 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
606 sprintf(buf,
":%d", port);
650 return ret_keepalive;
661 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
680 s +=
"&xrdhttptime=";
682 sprintf(buf,
"%lld", (
long long) tnow);
687 s +=
"&xrdhttpname=";
693 s +=
"&xrdhttpvorg=";
698 s +=
"&xrdhttphost=";
708 s +=
"&xrdhttprole=";
713 s +=
"&xrdhttpgrps=";
718 s +=
"&xrdhttpendorsements=";
723 s +=
"&xrdhttpcredslen=";
725 sprintf(buf,
"%d", secent->
credslen);
731 s +=
"&xrdhttpcreds=";
745 void XrdHttpReq::sanitizeResourcePfx() {
776 void XrdHttpReq::parseResource(
char *res) {
782 char *p = strchr(res,
'?');
790 sanitizeResourcePfx();
815 sanitizeResourcePfx();
837 void XrdHttpReq::generateWebdavErrMsg() {
843 httpStatusCode = 200;
844 httpErrorBody =
"OK";
850 httpErrorBody =
etext +
"\n";
862 int query_param_status = 0;
866 if (query_param_status == 0) {
870 query_param_status = 1;
871 auto length_str = std::to_string(
length);
877 opaque->
Put(
"oss.asize", length_str.c_str());
883 if (query_param_status == 0) {
895 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
896 << header2cgistrObf.c_str() <<
"'");
911 if (r < 0)
return -1;
925 generateWebdavErrMsg();
926 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
935 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
960 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
984 if (
resource ==
"/static/css/xrdhttp.css") {
985 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
989 if (
resource ==
"/static/icons/xrdhttp.ico") {
990 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1010 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1020 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1052 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1070 prot->SendSimpleResp(
HTTP_METHOD_NOT_ALLOWED, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1082 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1087 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1098 generateWebdavErrMsg();
1099 return sendFooterError(
"Could not run close request on the bridge");
1109 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1123 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1134 l = res.length() + 1;
1138 generateWebdavErrMsg();
1139 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1140 sendFooterError(
"Could not run listing request on the bridge");
1153 auto retval = ReturnGetHeaders();
1174 TRACEI(REQ,
" Failed to run close request on the bridge.");
1188 if ( readChunkList.size() == 1 ) {
1200 offs = readChunkList[0].offset;
1201 l = readChunkList[0].size;
1209 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1212 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1221 TRACE(ALL,
" Data sizes mismatch.");
1225 TRACE(ALL,
" No more bytes to send.");
1232 httpStatusCode = 416;
1233 httpErrorBody =
"Range Not Satisfiable";
1234 std::stringstream ss;
1235 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1236 return sendFooterError(ss.str());
1240 generateWebdavErrMsg();
1241 return sendFooterError(
"Could not run read request on the bridge");
1249 generateWebdavErrMsg();
1250 return sendFooterError(
"Could not run ReadV request on the bridge");
1279 if (! XrdHttpProtocol::usingEC)
1285 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1300 if (m_transfer_encoding_chunked) {
1301 if (m_current_chunk_size == m_current_chunk_offset) {
1304 if (prot->BuffUsed() < 2)
return 1;
1305 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1306 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1309 prot->BuffConsume(2);
1310 if (m_current_chunk_size == 0) {
1314 m_transfer_encoding_chunked =
false;
1318 m_current_chunk_size = -1;
1319 m_current_chunk_offset = 0;
1321 if (!prot->BuffUsed())
return 1;
1323 if (-1 == m_current_chunk_size) {
1327 bool found_newline =
false;
1334 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1335 for (; idx < max_chunk_size_chars; idx++) {
1336 if (prot->myBuffStart[idx] ==
'\n') {
1337 found_newline =
true;
1343 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1344 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1345 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1348 if (found_newline) {
1349 char *endptr = NULL;
1350 std::string line_contents(prot->myBuffStart, idx);
1351 long long chunk_contents = strtoll(line_contents.c_str(), &endptr, 16);
1353 if (*endptr !=
';' && *endptr !=
'\r') {
1354 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1355 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1358 m_current_chunk_size = chunk_contents;
1359 m_current_chunk_offset = 0;
1360 prot->BuffConsume(idx + 1);
1361 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1368 if (m_current_chunk_size == 0) {
1377 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1378 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1379 chunk_bytes_remaining);
1384 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1385 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1386 generateWebdavErrMsg();
1387 return sendFooterError(
"Could not run write request on the bridge");
1391 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1401 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1407 TRACEI(REQ,
"Writing " << bytes_to_read);
1408 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1409 generateWebdavErrMsg();
1410 return sendFooterError(
"Could not run write request on the bridge");
1432 generateWebdavErrMsg();
1433 return sendFooterError(
"Could not run close request on the bridge");
1448 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);
1451 return ret_keepalive ? 1 : -1;
1473 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1493 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1507 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1523 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1538 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1543 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1548 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1567 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1599 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1625 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1636 skip = (skip == std::string::npos) ? 0 : skip + 3;
1640 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1651 size_t path_pos =
destination.find(
'/', skip + 1);
1653 if (path_pos == std::string::npos) {
1654 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Cannot determine destination path", 0,
false);
1661 l = mv_args.length() + 1;
1669 if (!prot->
Bridge->
Run((
char *) &
xrdreq, (
char *) mv_args.c_str(), l)) {
1670 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1679 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1690 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1693 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1698 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1699 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1702 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1703 if (convert_to_base64) {
1704 size_t digest_length = strlen(digest_value);
1705 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1706 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1707 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1708 free(digest_binary_value);
1711 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1713 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1714 free(digest_binary_value);
1715 digest_value = digest_base64_value;
1718 digest_header =
"Digest: ";
1720 digest_header +=
"=";
1721 digest_header += digest_value;
1722 if (convert_to_base64) {free(digest_value);}
1725 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1731 XrdHttpReq::PostProcessListing(
bool final_) {
1734 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1735 httpErrorBody.c_str(), httpErrorBody.length(),
false);
1741 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1742 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1744 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1745 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1746 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1767 "<th class=\"mode\">Mode</th>"
1768 "<th class=\"flags\">Flags</th>"
1769 "<th class=\"size\">Size</th>"
1770 "<th class=\"datetime\">Modified</th>"
1771 "<th class=\"name\">Name</th>"
1777 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1780 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1782 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1783 strncpy(entry, (
char *) startp, endp - startp);
1784 entry[endp - startp] = 0;
1791 <<
" stat=" << endp);
1794 sscanf(endp,
"%ld %lld %ld %ld",
1800 strcpy(entry, (
char *) startp);
1802 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1804 std::string p =
"<tr>"
1805 "<td class=\"mode\">";
1826 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1827 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1829 "<td class=\"name\">"
1837 if (!p.empty() && p[p.size() - 1] !=
'/')
1842 std::unique_ptr<char, decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1848 p +=
"</a></td></tr>";
1854 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1855 if (pp) startp = pp+1;
1864 stringresp +=
"</table></div><br><br><hr size=1>"
1865 "<p><span id=\"requestby\">Request by ";
1927 XrdHttpReq::ReturnGetHeaders() {
1928 std::string responseHeader;
1933 if (!responseHeader.empty()) {
1934 responseHeader +=
"\r\n";
1936 addAgeHeader(responseHeader);
1948 if (m_transfer_encoding_chunked && m_trailer_headers) {
1950 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
1952 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
1960 if (uranges.size() != 1)
1965 const off_t cnt = uranges[0].end - uranges[0].start + 1;
1967 std::string header =
"Content-Range: bytes ";
1968 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
1970 if (!responseHeader.empty()) {
1972 header += responseHeader.c_str();
1975 if (m_transfer_encoding_chunked && m_trailer_headers) {
1977 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
1979 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
1986 for (
auto &ur : uranges) {
1987 cnt += ur.end - ur.start + 1;
1992 (
char *)
"123456").size();
1996 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2002 if (!header.empty()) {
2005 addAgeHeader(header);
2008 if (m_transfer_encoding_chunked && m_trailer_headers) {
2010 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2012 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2018 if (m_status_trailer) {
2019 if (header.empty()) {
2020 header +=
"Trailer: X-Transfer-Status";
2022 header +=
"\r\nTrailer: X-Transfer-Status";
2029 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2031 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2032 generateWebdavErrMsg();
2037 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2046 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2051 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2058 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2062 std::string response_headers;
2066 <<
" stat=" << (
char *)
iovP[0].iov_base);
2069 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2079 addAgeHeader(response_headers);
2080 response_headers +=
"\r\n";
2082 response_headers +=
"Accept-Ranges: bytes";
2083 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2088 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2091 return ret_keepalive ? 1 : -1;
2094 std::string response_headers;
2095 int response = PostProcessChecksum(response_headers);
2096 if (-1 == response) {
2099 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2101 addAgeHeader(response_headers);
2102 response_headers +=
"\r\n";
2104 response_headers +=
"Accept-Ranges: bytes";
2105 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2108 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2130 if (
iovP[1].iov_len > 1) {
2132 <<
" stat=" << (
char *)
iovP[1].iov_base);
2135 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2156 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2157 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2166 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2167 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2180 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2181 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2188 return PostProcessListing(final_);
2198 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2205 httpErrorBody = rrerror.
errMsg;
2208 if (m_transfer_encoding_chunked && m_trailer_headers) {
2209 if (prot->ChunkRespHeader(0))
2212 const std::string crlf =
"\r\n";
2213 std::stringstream ss;
2214 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpErrorBody << crlf;
2216 const auto header = ss.str();
2217 if (prot->SendData(header.c_str(), header.size()))
2220 if (prot->ChunkRespFooter())
2224 if (rrerror)
return -1;
2231 auto rc = sendFooterError(
"");
2240 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2243 getReadResponse(received);
2247 rc = sendReadResponseSingleRange(received);
2249 rc = sendReadResponsesMultiRanges(received);
2268 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2276 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2279 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2290 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2302 if (m_transfer_encoding_chunked) {
2303 m_current_chunk_offset += l;
2307 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2314 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2317 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2336 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2337 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2352 <<
" stat=" << (
char *)
iovP[0].iov_base);
2355 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2367 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2370 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2371 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2383 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2384 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2402 <<
" stat=" << (
char *)
iovP[0].iov_base);
2405 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2411 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2416 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2444 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2445 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2448 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2452 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2454 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2459 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2469 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";
2472 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2486 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2490 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2492 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2493 strncpy(entry, (
char *) startp, endp - startp);
2494 entry[endp - startp] = 0;
2501 <<
" stat=" << endp);
2504 sscanf(endp,
"%ld %lld %ld %ld",
2512 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2532 if (*p.rbegin() !=
'/') p +=
"/";
2536 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2560 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2561 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2564 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2568 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2569 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2571 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2574 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2582 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2583 if (pp) startp = pp+1;
2594 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";
2597 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2617 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2619 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2620 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2625 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2633 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2637 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2650 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2651 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2665 XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2666 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2674 if (prot->ChunkRespHeader(0))
2677 std::stringstream ss;
2679 ss << httpStatusCode;
2680 if (!httpErrorBody.empty()) {
2681 std::string_view statusView(httpErrorBody);
2684 if (statusView[statusView.size() - 1] ==
'\n') {
2685 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2687 ss <<
": " << httpErrorBody;
2691 if (!extra_text.empty())
2692 ss <<
": " << extra_text;
2696 const auto header =
"X-Transfer-Status: " + ss.str();
2697 if (prot->SendData(header.c_str(), header.size()))
2700 if (prot->ChunkRespFooter())
2705 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2711 void XrdHttpReq::addAgeHeader(std::string &headers) {
2713 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2718 TRACE(REQ,
" XrdHttpReq request ended.");
2762 m_transfer_encoding_chunked =
false;
2763 m_current_chunk_size = -1;
2764 m_current_chunk_offset = 0;
2766 m_trailer_headers =
false;
2767 m_status_trailer =
false;
2801 httpStatusCode = -1;
2807 void XrdHttpReq::getfhandle() {
2810 TRACEI(REQ,
"fhandle:" <<
2824 for (
int i = 0; i <
iovN; i++) {
2826 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2828 len = ntohl(l->
rlen);
2841 for (
int i = 0; i <
iovN; i++) {
2842 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2847 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2849 if (received.size() == 0) {
2865 std::string st_header;
2866 std::string fin_header;
2873 std::vector<rinfo> rvec;
2876 rvec.reserve(received.size());
2878 for(
const auto &rcv: received) {
2887 rentry.start = start;
2888 rentry.finish = finish;
2897 rentry.st_header = s;
2898 sum_len += s.size();
2901 sum_len += rcv.size;
2905 rentry.fin_header = s;
2906 sum_len += s.size();
2909 rvec.push_back(rentry);
2914 if (m_transfer_encoding_chunked && m_trailer_headers) {
2915 prot->ChunkRespHeader(sum_len);
2919 for(
const auto &rentry: rvec) {
2922 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2923 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2929 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2933 if (rentry.finish) {
2934 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2941 if (m_transfer_encoding_chunked && m_trailer_headers) {
2942 prot->ChunkRespFooter();
2948 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2951 if (received.size() == 0) {
2961 for(
const auto &rcv: received) {
2970 if (m_transfer_encoding_chunked && m_trailer_headers) {
2971 prot->ChunkRespHeader(sum);
2973 for(
const auto &rcv: received) {
2974 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2976 if (m_transfer_encoding_chunked && m_trailer_headers) {
2977 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.
Static resources, here for performance and ease of setup.
void Tobase64(const unsigned char *input, int length, char *out)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
int mapXrdErrToHttp(XErrorCode xrdError)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
Utility functions for XrdHTTP.
@ HTTP_METHOD_NOT_ALLOWED
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.
size_t getMaxRanges() const
return the maximum number of ranges that may be requested
const Error & getError() const
return the Error object
bool isSingleRange()
indicates a single range (implied whole file, or single range) or empty file
const UserRangeList & ListResolvedRanges()
return resolved (i.e. obsolute start and end) byte ranges desired
int reqstate
State machine to talk to the bridge.
int ReqReadV(const XrdHttpIOList &cl)
Prepare the buffers for sending a readv request.
int parseBody(char *body, long long len)
Parse the body of a request, assuming that it's XML and that it's entirely in memory.
std::vector< readahead_list > ralist
std::string destination
The destination field specified in the req.
XrdOucString resource
The resource specified by the request, stripped of opaque data.
bool headerok
Tells if we have finished reading the header.
std::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.
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)
const char * c_str() const
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)
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