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);
592 char *newvardata =
quote(vardata);
630 return ret_keepalive;
641 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
653 char *s1 =
quote(p+1);
670 s +=
"&xrdhttptime=";
672 sprintf(buf,
"%lld", (
long long) tnow);
677 s +=
"&xrdhttpname=";
686 s +=
"&xrdhttpvorg=";
695 s +=
"&xrdhttphost=";
713 s +=
"&xrdhttprole=";
722 s +=
"&xrdhttpgrps=";
731 s +=
"&xrdhttpendorsements=";
740 s +=
"&xrdhttpcredslen=";
742 sprintf(buf,
"%d", secent->
credslen);
743 char *s1 =
quote(buf);
752 s +=
"&xrdhttpcreds=";
756 char *s1 =
quote(zerocreds);
774void XrdHttpReq::sanitizeResourcePfx() {
805void XrdHttpReq::parseResource(
char *res) {
811 char *p = strchr(res,
'?');
819 sanitizeResourcePfx();
844 sanitizeResourcePfx();
875void XrdHttpReq::mapXrdErrorToHttpStatus() {
877 httpStatusCode = 500;
878 httpStatusText =
"Unrecognized error";
884 httpStatusCode = 401; httpStatusText =
"Unauthorized";
887 httpStatusCode = 403; httpStatusText =
"Operation not permitted";
890 httpStatusCode = 404; httpStatusText =
"File not found";
893 httpStatusCode = 405; httpStatusText =
"Operation not supported";
896 httpStatusCode = 423; httpStatusText =
"Resource is a locked";
899 httpStatusCode = 409; httpStatusText =
"Resource is a directory";
903 httpStatusCode = 409; httpStatusText =
"File already exists";
908 httpStatusCode = 405;
912 httpStatusCode = 405; httpStatusText =
"Method is not allowed";
915 httpStatusCode = 502; httpStatusText =
"Bad Gateway";
918 httpStatusCode = 504; httpStatusText =
"Gateway timeout";
927 <<
"] to status code [" << httpStatusCode <<
"]");
929 httpStatusText +=
"\n";
931 httpStatusCode = 200;
932 httpStatusText =
"OK";
944 int query_param_status = 0;
948 if (query_param_status == 0) {
952 query_param_status = 1;
953 auto length_str = std::to_string(
length);
959 opaque->
Put(
"oss.asize", length_str.c_str());
965 if (query_param_status == 0) {
977 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
978 << header2cgistrObf.c_str() <<
"'");
999 if (r < 0)
return -1;
1013 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
1019 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
1028 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1040 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1052 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1076 if (
resource ==
"/static/css/xrdhttp.css") {
1077 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1081 if (
resource ==
"/static/icons/xrdhttp.ico") {
1082 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1102 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1112 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1144 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1162 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1174 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1179 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1190 mapXrdErrorToHttpStatus();
1191 sendFooterError(
"Could not run close request on the bridge");
1202 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1216 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1227 l = res.length() + 1;
1231 mapXrdErrorToHttpStatus();
1232 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1233 sendFooterError(
"Could not run listing request on the bridge");
1246 auto retval = ReturnGetHeaders();
1259 if ( readChunkList.empty() )
1267 TRACEI(REQ,
" Failed to run close request on the bridge.");
1281 if ( readChunkList.size() == 1 ) {
1293 offs = readChunkList[0].offset;
1294 l = readChunkList[0].size;
1302 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1305 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1314 TRACE(ALL,
" Data sizes mismatch.");
1318 TRACE(ALL,
" No more bytes to send.");
1325 httpStatusCode = 416;
1326 httpStatusText =
"Range Not Satisfiable";
1327 std::stringstream ss;
1328 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1329 sendFooterError(ss.str());
1334 mapXrdErrorToHttpStatus();
1335 sendFooterError(
"Could not run read request on the bridge");
1344 mapXrdErrorToHttpStatus();
1345 sendFooterError(
"Could not run ReadV request on the bridge");
1375 if (! XrdHttpProtocol::usingEC)
1381 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1396 if (m_transfer_encoding_chunked) {
1397 if (m_current_chunk_size == m_current_chunk_offset) {
1400 if (prot->BuffUsed() < 2)
return 1;
1401 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1402 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1405 prot->BuffConsume(2);
1406 if (m_current_chunk_size == 0) {
1410 m_transfer_encoding_chunked =
false;
1414 m_current_chunk_size = -1;
1415 m_current_chunk_offset = 0;
1417 if (!prot->BuffUsed())
return 1;
1419 if (-1 == m_current_chunk_size) {
1423 bool found_newline =
false;
1430 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1431 for (; idx < max_chunk_size_chars; idx++) {
1432 if (prot->myBuffStart[idx] ==
'\n') {
1433 found_newline =
true;
1439 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1440 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1441 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1444 if (found_newline) {
1445 char *endptr = NULL;
1446 std::string line_contents(prot->myBuffStart, idx);
1447 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1449 if (*endptr !=
';' && *endptr !=
'\r') {
1450 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1451 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1454 m_current_chunk_size = chunk_contents;
1455 m_current_chunk_offset = 0;
1456 prot->BuffConsume(idx + 1);
1457 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1464 if (m_current_chunk_size == 0) {
1473 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1474 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1475 chunk_bytes_remaining);
1480 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1481 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1482 mapXrdErrorToHttpStatus();
1483 sendFooterError(
"Could not run write request on the bridge");
1488 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1498 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1504 TRACEI(REQ,
"Writing " << bytes_to_read);
1505 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1506 mapXrdErrorToHttpStatus();
1507 sendFooterError(
"Could not run write request on the bridge");
1530 mapXrdErrorToHttpStatus();
1531 sendFooterError(
"Could not run close request on the bridge");
1547 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);
1550 return ret_keepalive ? 1 : -1;
1572 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1592 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1606 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1622 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1637 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1642 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1647 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1666 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1698 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1724 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1745 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1750 strcpy(buf2,
host.c_str());
1751 char *pos = strchr(buf2,
':');
1752 if (pos) *pos =
'\0';
1760 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1774 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1784 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1795XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1798 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1803 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1804 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1807 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1808 if (convert_to_base64) {
1809 size_t digest_length = strlen(digest_value);
1810 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1811 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1812 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1813 free(digest_binary_value);
1816 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1818 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1819 free(digest_binary_value);
1820 digest_value = digest_base64_value;
1823 digest_header =
"Digest: ";
1825 digest_header +=
"=";
1826 digest_header += digest_value;
1827 if (convert_to_base64) {free(digest_value);}
1830 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1836XrdHttpReq::PostProcessListing(
bool final_) {
1839 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1840 httpStatusText.c_str(), httpStatusText.length(),
false);
1846 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1847 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1849 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1850 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1851 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1872 "<th class=\"mode\">Mode</th>"
1873 "<th class=\"flags\">Flags</th>"
1874 "<th class=\"size\">Size</th>"
1875 "<th class=\"datetime\">Modified</th>"
1876 "<th class=\"name\">Name</th>"
1882 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1885 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1887 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1888 strncpy(entry, (
char *) startp, endp - startp);
1889 entry[endp - startp] = 0;
1896 <<
" stat=" << endp);
1899 sscanf(endp,
"%ld %lld %ld %ld",
1905 strcpy(entry, (
char *) startp);
1907 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1909 std::string p =
"<tr>"
1910 "<td class=\"mode\">";
1931 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1932 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1934 "<td class=\"name\">"
1942 if (!p.empty() && p[p.size() - 1] !=
'/')
1947 std::unique_ptr<char,
decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1953 p +=
"</a></td></tr>";
1959 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1960 if (pp) startp = pp+1;
1969 stringresp +=
"</table></div><br><br><hr size=1>"
1970 "<p><span id=\"requestby\">Request by ";
2032XrdHttpReq::ReturnGetHeaders() {
2033 std::string responseHeader;
2038 if (!responseHeader.empty()) {
2039 responseHeader +=
"\r\n";
2041 addAgeHeader(responseHeader);
2053 if (m_transfer_encoding_chunked && m_trailer_headers) {
2054 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2056 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2064 if (uranges.size() != 1)
2069 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2072 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2074 if (!responseHeader.empty()) {
2076 s += responseHeader.
c_str();
2079 if (m_transfer_encoding_chunked && m_trailer_headers) {
2080 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2082 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2089 for (
auto &ur : uranges) {
2090 cnt += ur.end - ur.start + 1;
2095 (
char *)
"123456").size();
2099 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2105 if (!header.empty()) {
2108 addAgeHeader(header);
2111 if (m_transfer_encoding_chunked && m_trailer_headers) {
2112 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2114 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2121int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2123 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2124 mapXrdErrorToHttpStatus();
2129 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2138 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2143 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2150 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2154 std::string response_headers;
2158 <<
" stat=" << (
char *)
iovP[0].iov_base);
2161 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2171 addAgeHeader(response_headers);
2172 response_headers +=
"\r\n";
2174 response_headers +=
"Accept-Ranges: bytes";
2175 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2180 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2183 return ret_keepalive ? 1 : -1;
2186 std::string response_headers;
2187 int response = PostProcessChecksum(response_headers);
2188 if (-1 == response) {
2191 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2193 addAgeHeader(response_headers);
2194 response_headers +=
"\r\n";
2196 response_headers +=
"Accept-Ranges: bytes";
2197 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2200 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2222 if (
iovP[1].iov_len > 1) {
2224 <<
" stat=" << (
char *)
iovP[1].iov_base);
2227 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2248 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2249 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2258 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2259 httpStatusText.c_str(), httpStatusText.length(),
false);
2272 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2273 httpStatusText.c_str(), httpStatusText.length(),
false);
2280 return PostProcessListing(final_);
2290 httpStatusText = rrerror.
errMsg;
2293 if (m_transfer_encoding_chunked && m_trailer_headers) {
2294 if (prot->ChunkRespHeader(0))
2297 const std::string crlf =
"\r\n";
2298 std::stringstream ss;
2299 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2301 const auto header = ss.str();
2302 if (prot->SendData(header.c_str(), header.size()))
2305 if (prot->ChunkRespFooter())
2309 if (rrerror)
return -1;
2316 sendFooterError(
"");
2321 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2324 getReadResponse(received);
2328 rc = sendReadResponseSingleRange(received);
2330 rc = sendReadResponsesMultiRanges(received);
2351 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2352 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2360 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2363 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2382 if (m_transfer_encoding_chunked) {
2383 m_current_chunk_offset += l;
2387 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2394 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2397 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2398 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2419 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2420 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2435 <<
" stat=" << (
char *)
iovP[0].iov_base);
2438 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2450 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2453 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2454 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2466 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2467 httpStatusText.c_str(), httpStatusText.length(),
false);
2485 <<
" stat=" << (
char *)
iovP[0].iov_base);
2488 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2494 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2499 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2527 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2528 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2530 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2534 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2535 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2537 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2542 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2552 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";
2555 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2569 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2573 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2575 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2576 strncpy(entry, (
char *) startp, endp - startp);
2577 entry[endp - startp] = 0;
2584 <<
" stat=" << endp);
2587 sscanf(endp,
"%ld %lld %ld %ld",
2595 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2615 if (*p.rbegin() !=
'/') p +=
"/";
2619 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2643 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2644 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2646 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2650 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2651 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2653 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2656 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2664 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2665 if (pp) startp = pp+1;
2676 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";
2679 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2699 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2701 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2702 httpStatusText.c_str(), httpStatusText.length(),
false);
2707 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2715 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2719 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2732 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2733 httpStatusText.c_str(), httpStatusText.length(),
false);
2747XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2748 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2756 if (prot->ChunkRespHeader(0))
2759 std::stringstream ss;
2760 ss << httpStatusCode <<
": " << httpStatusText;
2761 if (!extra_text.empty())
2762 ss <<
": " << extra_text;
2766 const auto header =
"X-Transfer-Status: " + ss.str();
2767 if (prot->SendData(header.c_str(), header.size()))
2770 prot->ChunkRespFooter();
2772 TRACEI(REQ, httpStatusCode <<
": " << httpStatusText << (extra_text.empty() ?
"" : (
": " + extra_text)));
2776void XrdHttpReq::addAgeHeader(std::string &headers) {
2778 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2783 TRACE(REQ,
" XrdHttpReq request ended.");
2825 m_transfer_encoding_chunked =
false;
2826 m_current_chunk_size = -1;
2827 m_current_chunk_offset = 0;
2829 m_trailer_headers =
false;
2830 m_status_trailer =
false;
2865void XrdHttpReq::getfhandle() {
2868 TRACEI(REQ,
"fhandle:" <<
2882 for (
int i = 0; i <
iovN; i++) {
2884 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2886 len = ntohl(l->
rlen);
2899 for (
int i = 0; i <
iovN; i++) {
2900 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2905int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2907 if (received.size() == 0) {
2923 std::string st_header;
2924 std::string fin_header;
2931 std::vector<rinfo> rvec;
2934 rvec.reserve(received.size());
2936 for(
const auto &rcv: received) {
2945 rentry.start = start;
2946 rentry.finish = finish;
2955 rentry.st_header = s;
2956 sum_len += s.size();
2959 sum_len += rcv.size;
2963 rentry.fin_header = s;
2964 sum_len += s.size();
2967 rvec.push_back(rentry);
2972 if (m_transfer_encoding_chunked && m_trailer_headers) {
2973 prot->ChunkRespHeader(sum_len);
2977 for(
const auto &rentry: rvec) {
2980 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2981 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2987 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2991 if (rentry.finish) {
2992 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2999 if (m_transfer_encoding_chunked && m_trailer_headers) {
3000 prot->ChunkRespFooter();
3006int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
3009 if (received.size() == 0) {
3019 for(
const auto &rcv: received) {
3028 if (m_transfer_encoding_chunked && m_trailer_headers) {
3029 prot->ChunkRespHeader(sum);
3031 for(
const auto &rcv: received) {
3032 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3034 if (m_transfer_encoding_chunked && m_trailer_headers) {
3035 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 * unquote(char *str)
char * quote(const char *str)
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::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.
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