39 #include "XrdVersion.hh"
44 #include <arpa/inet.h>
65 #define MAX_TK_LEN 256
66 #define MAX_RESOURCE_LEN 16384
69 #define TRACELINK prot->Link
73 const char *TraceID =
"Req";
76 void trim(std::string &str)
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);
229 int XrdHttpReq::parseHost(
char *line) {
235 void XrdHttpReq::parseScitag(
const std::string & val) {
239 std::string scitagS = val;
242 if(scitagS[0] !=
'-') {
244 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
257 addCgi(
"pmark.appname",this->
request == ReqType::rtGET ?
"http-get" :
"http-put");
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")) {
360 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
367 for (
int i = 0; i < nitems; i++) {
378 void 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();
538 return rc == 0 ? true :
false;
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);
595 char *newvardata =
quote(vardata);
633 return ret_keepalive;
644 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
656 char *s1 =
quote(p+1);
673 s +=
"&xrdhttptime=";
675 sprintf(buf,
"%lld", (
long long) tnow);
680 s +=
"&xrdhttpname=";
689 s +=
"&xrdhttpvorg=";
698 s +=
"&xrdhttphost=";
716 s +=
"&xrdhttprole=";
725 s +=
"&xrdhttpgrps=";
734 s +=
"&xrdhttpendorsements=";
743 s +=
"&xrdhttpcredslen=";
745 sprintf(buf,
"%d", secent->
credslen);
746 char *s1 =
quote(buf);
755 s +=
"&xrdhttpcreds=";
759 char *s1 =
quote(zerocreds);
777 void XrdHttpReq::sanitizeResourcePfx() {
808 void XrdHttpReq::parseResource(
char *res) {
814 char *p = strchr(res,
'?');
822 sanitizeResourcePfx();
847 sanitizeResourcePfx();
878 void XrdHttpReq::mapXrdErrorToHttpStatus() {
880 httpStatusCode = 500;
881 httpStatusText =
"Unrecognized error";
887 httpStatusCode = 401; httpStatusText =
"Unauthorized";
890 httpStatusCode = 403; httpStatusText =
"Operation not permitted";
893 httpStatusCode = 404; httpStatusText =
"File not found";
896 httpStatusCode = 405; httpStatusText =
"Operation not supported";
899 httpStatusCode = 423; httpStatusText =
"Resource is a locked";
902 httpStatusCode = 409; httpStatusText =
"Resource is a directory";
905 if(
request != ReqType::rtDELETE) {
906 httpStatusCode = 409; httpStatusText =
"File already exists";
911 httpStatusCode = 405;
915 httpStatusCode = 405; httpStatusText =
"Method is not allowed";
918 httpStatusCode = 502; httpStatusText =
"Bad Gateway";
921 httpStatusCode = 504; httpStatusText =
"Gateway timeout";
930 <<
"] to status code [" << httpStatusCode <<
"]");
932 httpStatusText +=
"\n";
934 httpStatusCode = 200;
935 httpStatusText =
"OK";
947 int query_param_status = 0;
951 if (query_param_status == 0) {
955 query_param_status = 1;
956 auto length_str = std::to_string(
length);
962 opaque->
Put(
"oss.asize", length_str.c_str());
968 if (query_param_status == 0) {
980 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
981 << header2cgistrObf.c_str() <<
"'");
1002 if (r < 0)
return -1;
1016 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
1022 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
1031 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1043 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1055 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1079 if (
resource ==
"/static/css/xrdhttp.css") {
1080 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1084 if (
resource ==
"/static/icons/xrdhttp.ico") {
1085 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1105 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1115 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1147 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1165 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1177 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1182 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1193 mapXrdErrorToHttpStatus();
1194 return sendFooterError(
"Could not run close request on the bridge");
1204 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1218 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1229 l = res.length() + 1;
1233 mapXrdErrorToHttpStatus();
1234 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1235 return sendFooterError(
"Could not run listing request on the bridge");
1247 auto retval = ReturnGetHeaders();
1268 TRACEI(REQ,
" Failed to run close request on the bridge.");
1282 if ( readChunkList.size() == 1 ) {
1294 offs = readChunkList[0].offset;
1295 l = readChunkList[0].size;
1303 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1306 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1315 TRACE(ALL,
" Data sizes mismatch.");
1319 TRACE(ALL,
" No more bytes to send.");
1326 httpStatusCode = 416;
1327 httpStatusText =
"Range Not Satisfiable";
1328 std::stringstream ss;
1329 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1330 return sendFooterError(ss.str());
1334 mapXrdErrorToHttpStatus();
1335 return sendFooterError(
"Could not run read request on the bridge");
1343 mapXrdErrorToHttpStatus();
1344 return sendFooterError(
"Could not run ReadV request on the bridge");
1373 if (! XrdHttpProtocol::usingEC)
1379 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1394 if (m_transfer_encoding_chunked) {
1395 if (m_current_chunk_size == m_current_chunk_offset) {
1398 if (prot->BuffUsed() < 2)
return 1;
1399 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1400 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1403 prot->BuffConsume(2);
1404 if (m_current_chunk_size == 0) {
1408 m_transfer_encoding_chunked =
false;
1412 m_current_chunk_size = -1;
1413 m_current_chunk_offset = 0;
1415 if (!prot->BuffUsed())
return 1;
1417 if (-1 == m_current_chunk_size) {
1421 bool found_newline =
false;
1428 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1429 for (; idx < max_chunk_size_chars; idx++) {
1430 if (prot->myBuffStart[idx] ==
'\n') {
1431 found_newline =
true;
1437 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1438 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1439 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1442 if (found_newline) {
1443 char *endptr = NULL;
1444 std::string line_contents(prot->myBuffStart, idx);
1445 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1447 if (*endptr !=
';' && *endptr !=
'\r') {
1448 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1449 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1452 m_current_chunk_size = chunk_contents;
1453 m_current_chunk_offset = 0;
1454 prot->BuffConsume(idx + 1);
1455 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1462 if (m_current_chunk_size == 0) {
1471 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1472 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1473 chunk_bytes_remaining);
1478 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1479 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1480 mapXrdErrorToHttpStatus();
1481 return sendFooterError(
"Could not run write request on the bridge");
1485 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1495 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1501 TRACEI(REQ,
"Writing " << bytes_to_read);
1502 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1503 mapXrdErrorToHttpStatus();
1504 return sendFooterError(
"Could not run write request on the bridge");
1526 mapXrdErrorToHttpStatus();
1527 return sendFooterError(
"Could not run close request on the bridge");
1542 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);
1545 return ret_keepalive ? 1 : -1;
1567 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1587 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1601 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1617 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1632 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1637 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1642 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1661 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1693 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1719 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1740 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1745 strcpy(buf2,
host.c_str());
1746 char *pos = strchr(buf2,
':');
1747 if (pos) *pos =
'\0';
1755 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1769 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1779 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1790 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1793 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1798 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1799 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1802 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1803 if (convert_to_base64) {
1804 size_t digest_length = strlen(digest_value);
1805 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1806 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1807 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1808 free(digest_binary_value);
1811 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1813 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1814 free(digest_binary_value);
1815 digest_value = digest_base64_value;
1818 digest_header =
"Digest: ";
1820 digest_header +=
"=";
1821 digest_header += digest_value;
1822 if (convert_to_base64) {free(digest_value);}
1825 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1831 XrdHttpReq::PostProcessListing(
bool final_) {
1834 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1835 httpStatusText.c_str(), httpStatusText.length(),
false);
1841 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1842 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1844 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1845 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1846 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1867 "<th class=\"mode\">Mode</th>"
1868 "<th class=\"flags\">Flags</th>"
1869 "<th class=\"size\">Size</th>"
1870 "<th class=\"datetime\">Modified</th>"
1871 "<th class=\"name\">Name</th>"
1877 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1880 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1882 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1883 strncpy(entry, (
char *) startp, endp - startp);
1884 entry[endp - startp] = 0;
1891 <<
" stat=" << endp);
1894 sscanf(endp,
"%ld %lld %ld %ld",
1900 strcpy(entry, (
char *) startp);
1902 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1904 std::string p =
"<tr>"
1905 "<td class=\"mode\">";
1926 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1927 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1929 "<td class=\"name\">"
1937 if (!p.empty() && p[p.size() - 1] !=
'/')
1942 std::unique_ptr<char, decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1948 p +=
"</a></td></tr>";
1954 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1955 if (pp) startp = pp+1;
1964 stringresp +=
"</table></div><br><br><hr size=1>"
1965 "<p><span id=\"requestby\">Request by ";
2027 XrdHttpReq::ReturnGetHeaders() {
2028 std::string responseHeader;
2033 if (!responseHeader.empty()) {
2034 responseHeader +=
"\r\n";
2036 addAgeHeader(responseHeader);
2048 if (m_transfer_encoding_chunked && m_trailer_headers) {
2050 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2052 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2060 if (uranges.size() != 1)
2065 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2067 std::string header =
"Content-Range: bytes ";
2068 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2070 if (!responseHeader.empty()) {
2072 header += responseHeader.c_str();
2075 if (m_transfer_encoding_chunked && m_trailer_headers) {
2077 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
2079 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
2086 for (
auto &ur : uranges) {
2087 cnt += ur.end - ur.start + 1;
2092 (
char *)
"123456").size();
2096 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2102 if (!header.empty()) {
2105 addAgeHeader(header);
2108 if (m_transfer_encoding_chunked && m_trailer_headers) {
2110 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2112 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2118 if (m_status_trailer) {
2119 if (header.empty()) {
2120 header +=
"Trailer: X-Transfer-Status";
2122 header +=
"\r\nTrailer: X-Transfer-Status";
2129 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2131 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2132 mapXrdErrorToHttpStatus();
2137 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2146 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2151 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2158 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2162 std::string response_headers;
2166 <<
" stat=" << (
char *)
iovP[0].iov_base);
2169 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2179 addAgeHeader(response_headers);
2180 response_headers +=
"\r\n";
2182 response_headers +=
"Accept-Ranges: bytes";
2183 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2188 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2191 return ret_keepalive ? 1 : -1;
2194 std::string response_headers;
2195 int response = PostProcessChecksum(response_headers);
2196 if (-1 == response) {
2199 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2201 addAgeHeader(response_headers);
2202 response_headers +=
"\r\n";
2204 response_headers +=
"Accept-Ranges: bytes";
2205 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2208 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2230 if (
iovP[1].iov_len > 1) {
2232 <<
" stat=" << (
char *)
iovP[1].iov_base);
2235 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2256 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2257 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2266 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2267 httpStatusText.c_str(), httpStatusText.length(),
false);
2280 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2281 httpStatusText.c_str(), httpStatusText.length(),
false);
2288 return PostProcessListing(final_);
2298 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2305 httpStatusText = rrerror.
errMsg;
2308 if (m_transfer_encoding_chunked && m_trailer_headers) {
2309 if (prot->ChunkRespHeader(0))
2312 const std::string crlf =
"\r\n";
2313 std::stringstream ss;
2314 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2316 const auto header = ss.str();
2317 if (prot->SendData(header.c_str(), header.size()))
2320 if (prot->ChunkRespFooter())
2324 if (rrerror)
return -1;
2331 auto rc = sendFooterError(
"");
2340 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2343 getReadResponse(received);
2347 rc = sendReadResponseSingleRange(received);
2349 rc = sendReadResponsesMultiRanges(received);
2370 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2371 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2379 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2382 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2401 if (m_transfer_encoding_chunked) {
2402 m_current_chunk_offset += l;
2406 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2414 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2417 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2418 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2439 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2440 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2455 <<
" stat=" << (
char *)
iovP[0].iov_base);
2458 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2470 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2473 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2474 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2486 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2487 httpStatusText.c_str(), httpStatusText.length(),
false);
2505 <<
" stat=" << (
char *)
iovP[0].iov_base);
2508 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2514 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2519 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2547 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2548 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2550 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2554 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2555 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2557 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2562 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2572 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";
2575 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2589 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2593 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2595 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2596 strncpy(entry, (
char *) startp, endp - startp);
2597 entry[endp - startp] = 0;
2604 <<
" stat=" << endp);
2607 sscanf(endp,
"%ld %lld %ld %ld",
2615 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2635 if (*p.rbegin() !=
'/') p +=
"/";
2639 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2663 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2664 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2666 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2670 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2671 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2673 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2676 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2684 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2685 if (pp) startp = pp+1;
2696 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";
2699 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2719 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2721 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2722 httpStatusText.c_str(), httpStatusText.length(),
false);
2727 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2735 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2739 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2752 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2753 httpStatusText.c_str(), httpStatusText.length(),
false);
2767 XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2768 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2776 if (prot->ChunkRespHeader(0))
2779 std::stringstream ss;
2780 ss << httpStatusCode;
2781 if (!httpStatusText.empty()) {
2782 std::string_view statusView(httpStatusText);
2785 if (statusView[statusView.size() - 1] ==
'\n') {
2786 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2788 ss <<
": " << httpStatusText;
2791 if (!extra_text.empty())
2792 ss <<
": " << extra_text;
2796 const auto header =
"X-Transfer-Status: " + ss.str();
2797 if (prot->SendData(header.c_str(), header.size()))
2800 if (prot->ChunkRespFooter())
2805 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpStatusText << (extra_text.empty() ?
"" : (
": " + extra_text)));
2810 void XrdHttpReq::addAgeHeader(std::string &headers) {
2812 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2817 TRACE(REQ,
" XrdHttpReq request ended.");
2860 m_transfer_encoding_chunked =
false;
2861 m_current_chunk_size = -1;
2862 m_current_chunk_offset = 0;
2864 m_trailer_headers =
false;
2865 m_status_trailer =
false;
2900 void XrdHttpReq::getfhandle() {
2903 TRACEI(REQ,
"fhandle:" <<
2917 for (
int i = 0; i <
iovN; i++) {
2919 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2921 len = ntohl(l->
rlen);
2934 for (
int i = 0; i <
iovN; i++) {
2935 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2940 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2942 if (received.size() == 0) {
2958 std::string st_header;
2959 std::string fin_header;
2966 std::vector<rinfo> rvec;
2969 rvec.reserve(received.size());
2971 for(
const auto &rcv: received) {
2980 rentry.start = start;
2981 rentry.finish = finish;
2990 rentry.st_header = s;
2991 sum_len += s.size();
2994 sum_len += rcv.size;
2998 rentry.fin_header = s;
2999 sum_len += s.size();
3002 rvec.push_back(rentry);
3007 if (m_transfer_encoding_chunked && m_trailer_headers) {
3008 prot->ChunkRespHeader(sum_len);
3012 for(
const auto &rentry: rvec) {
3015 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
3016 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
3022 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
3026 if (rentry.finish) {
3027 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
3034 if (m_transfer_encoding_chunked && m_trailer_headers) {
3035 prot->ChunkRespFooter();
3041 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
3044 if (received.size() == 0) {
3054 for(
const auto &rcv: received) {
3063 if (m_transfer_encoding_chunked && m_trailer_headers) {
3064 prot->ChunkRespHeader(sum);
3066 for(
const auto &rcv: received) {
3067 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3069 if (m_transfer_encoding_chunked && m_trailer_headers) {
3070 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.
int parseURL(char *url, char *host, int &port, char **path)
void Tobase64(const unsigned char *input, int length, char *out)
char * unquote(char *str)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
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)
char * quote(const char *str)
Utility functions for XrdHTTP.
std::vector< XrdOucIOVec2 > XrdHttpIOList
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.
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