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);
263 if (!line)
return -1;
266 char *p = strchr((
char *) line, (
int)
' ');
290 char *val = line + pos + 1;
297 p = strchr((
char *) val, (
int)
' ');
311 if (!strcmp(key,
"GET")) {
313 }
else if (!strcmp(key,
"HEAD")) {
315 }
else if (!strcmp(key,
"PUT")) {
317 }
else if (!strcmp(key,
"POST")) {
319 }
else if (!strcmp(key,
"PATCH")) {
321 }
else if (!strcmp(key,
"OPTIONS")) {
323 }
else if (!strcmp(key,
"DELETE")) {
325 }
else if (!strcmp(key,
"PROPFIND")) {
328 }
else if (!strcmp(key,
"MKCOL")) {
331 }
else if (!strcmp(key,
"MOVE")) {
341 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
355 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
362 for (
int i = 0; i < nitems; i++) {
373 void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
380 for (
int i = 0; i < nitems; i++) {
398 for (
const auto &c: cl) {
401 memcpy(&ra.fhandle, this->fhandle, 4);
403 ra.offset = c.offset;
417 clientMarshallReadAheadList(j);
426 std::ostringstream s;
428 s <<
"\r\n--" << token <<
"\r\n";
429 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
430 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
436 std::ostringstream s;
438 s <<
"\r\n--" << token <<
"--\r\n";
451 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
457 this->
final = final_;
459 if (PostProcessHTTPReq(final_))
reset();
473 int rc = info.
Send(0, 0, 0, 0);
474 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
491 TRACE(REQ,
" XrdHttpReq::Done");
497 int r = PostProcessHTTPReq(
true);
500 if (r < 0)
return false;
511 TRACE(REQ,
" XrdHttpReq::Error");
522 if (PostProcessHTTPReq())
reset();
552 if (strncmp(hname,
"file://", 7) == 0)
554 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
561 char *pp = strchr((
char *)hname,
'?');
567 int varlen = strlen(vardata);
570 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
579 sprintf(buf,
":%d", port);
587 char *newvardata =
quote(vardata);
625 return ret_keepalive;
636 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
648 char *s1 =
quote(p+1);
665 s +=
"&xrdhttptime=";
667 sprintf(buf,
"%lld", (
long long) tnow);
672 s +=
"&xrdhttpname=";
681 s +=
"&xrdhttpvorg=";
690 s +=
"&xrdhttphost=";
708 s +=
"&xrdhttprole=";
717 s +=
"&xrdhttpgrps=";
726 s +=
"&xrdhttpendorsements=";
735 s +=
"&xrdhttpcredslen=";
737 sprintf(buf,
"%d", secent->
credslen);
738 char *s1 =
quote(buf);
747 s +=
"&xrdhttpcreds=";
751 char *s1 =
quote(zerocreds);
769 void XrdHttpReq::sanitizeResourcePfx() {
800 void XrdHttpReq::parseResource(
char *res) {
806 char *p = strchr(res,
'?');
814 sanitizeResourcePfx();
839 sanitizeResourcePfx();
870 void XrdHttpReq::mapXrdErrorToHttpStatus() {
872 httpStatusCode = 500;
873 httpStatusText =
"Unrecognized error";
879 httpStatusCode = 401; httpStatusText =
"Unauthorized";
882 httpStatusCode = 403; httpStatusText =
"Operation not permitted";
885 httpStatusCode = 404; httpStatusText =
"File not found";
888 httpStatusCode = 405; httpStatusText =
"Operation not supported";
891 httpStatusCode = 423; httpStatusText =
"Resource is a locked";
894 httpStatusCode = 409; httpStatusText =
"Resource is a directory";
897 if(
request != ReqType::rtDELETE) {
898 httpStatusCode = 409; httpStatusText =
"File already exists";
903 httpStatusCode = 405;
907 httpStatusCode = 405; httpStatusText =
"Method is not allowed";
910 httpStatusCode = 504; httpStatusText =
"Gateway timeout";
919 <<
"] to status code [" << httpStatusCode <<
"]");
921 httpStatusText +=
"\n";
923 httpStatusCode = 200;
924 httpStatusText =
"OK";
936 int query_param_status = 0;
940 if (query_param_status == 0) {
944 query_param_status = 1;
945 auto length_str = std::to_string(
length);
951 opaque->
Put(
"oss.asize", length_str.c_str());
957 if (query_param_status == 0) {
969 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
970 << header2cgistrObf.c_str() <<
"'");
991 if (r < 0)
return -1;
1005 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
1011 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
1020 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1032 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1044 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1068 if (
resource ==
"/static/css/xrdhttp.css") {
1069 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1073 if (
resource ==
"/static/icons/xrdhttp.ico") {
1074 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1094 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1104 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1136 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1154 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1166 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1171 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1182 mapXrdErrorToHttpStatus();
1183 sendFooterError(
"Could not run close request on the bridge");
1194 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1208 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1219 l = res.length() + 1;
1223 mapXrdErrorToHttpStatus();
1224 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1225 sendFooterError(
"Could not run listing request on the bridge");
1238 auto retval = ReturnGetHeaders();
1251 if ( readChunkList.empty() )
1259 TRACEI(REQ,
" Failed to run close request on the bridge.");
1273 if ( readChunkList.size() == 1 ) {
1285 offs = readChunkList[0].offset;
1286 l = readChunkList[0].size;
1294 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1297 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1306 TRACE(ALL,
" Data sizes mismatch.");
1310 TRACE(ALL,
" No more bytes to send.");
1317 httpStatusCode = 416;
1318 httpStatusText =
"Range Not Satisfiable";
1319 std::stringstream ss;
1320 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1321 sendFooterError(ss.str());
1326 mapXrdErrorToHttpStatus();
1327 sendFooterError(
"Could not run read request on the bridge");
1336 mapXrdErrorToHttpStatus();
1337 sendFooterError(
"Could not run ReadV request on the bridge");
1367 if (! XrdHttpProtocol::usingEC)
1373 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1388 if (m_transfer_encoding_chunked) {
1389 if (m_current_chunk_size == m_current_chunk_offset) {
1392 if (prot->BuffUsed() < 2)
return 1;
1393 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1394 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1397 prot->BuffConsume(2);
1398 if (m_current_chunk_size == 0) {
1402 m_transfer_encoding_chunked =
false;
1406 m_current_chunk_size = -1;
1407 m_current_chunk_offset = 0;
1409 if (!prot->BuffUsed())
return 1;
1411 if (-1 == m_current_chunk_size) {
1415 bool found_newline =
false;
1422 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1423 for (; idx < max_chunk_size_chars; idx++) {
1424 if (prot->myBuffStart[idx] ==
'\n') {
1425 found_newline =
true;
1431 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1432 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1433 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1436 if (found_newline) {
1437 char *endptr = NULL;
1438 std::string line_contents(prot->myBuffStart, idx);
1439 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1441 if (*endptr !=
';' && *endptr !=
'\r') {
1442 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1443 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1446 m_current_chunk_size = chunk_contents;
1447 m_current_chunk_offset = 0;
1448 prot->BuffConsume(idx + 1);
1449 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1456 if (m_current_chunk_size == 0) {
1465 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1466 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1467 chunk_bytes_remaining);
1472 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1473 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1474 mapXrdErrorToHttpStatus();
1475 sendFooterError(
"Could not run write request on the bridge");
1480 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1490 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1496 TRACEI(REQ,
"Writing " << bytes_to_read);
1497 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1498 mapXrdErrorToHttpStatus();
1499 sendFooterError(
"Could not run write request on the bridge");
1522 mapXrdErrorToHttpStatus();
1523 sendFooterError(
"Could not run close request on the bridge");
1539 prot->SendSimpleResp(200, NULL, (
char *)
"DAV: 1\r\nDAV: <http://apache.org/dav/propset/fs/1>\r\nAllow: HEAD,GET,PUT,PROPFIND,DELETE,OPTIONS", NULL, 0,
keepalive);
1542 return ret_keepalive ? 1 : -1;
1564 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1584 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1598 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1614 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1629 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1634 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1639 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1658 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1690 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1716 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1737 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1742 strcpy(buf2,
host.c_str());
1743 char *pos = strchr(buf2,
':');
1744 if (pos) *pos =
'\0';
1752 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1766 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1776 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1787 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1790 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1795 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1796 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1799 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1800 if (convert_to_base64) {
1801 size_t digest_length = strlen(digest_value);
1802 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1803 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1804 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1805 free(digest_binary_value);
1808 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1810 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1811 free(digest_binary_value);
1812 digest_value = digest_base64_value;
1815 digest_header =
"Digest: ";
1817 digest_header +=
"=";
1818 digest_header += digest_value;
1819 if (convert_to_base64) {free(digest_value);}
1822 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1828 XrdHttpReq::PostProcessListing(
bool final_) {
1831 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1832 httpStatusText.c_str(), httpStatusText.length(),
false);
1838 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1839 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1841 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1842 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1843 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1864 "<th class=\"mode\">Mode</th>"
1865 "<th class=\"flags\">Flags</th>"
1866 "<th class=\"size\">Size</th>"
1867 "<th class=\"datetime\">Modified</th>"
1868 "<th class=\"name\">Name</th>"
1874 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1877 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1879 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1880 strncpy(entry, (
char *) startp, endp - startp);
1881 entry[endp - startp] = 0;
1888 <<
" stat=" << endp);
1891 sscanf(endp,
"%ld %lld %ld %ld",
1897 strcpy(entry, (
char *) startp);
1899 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1901 std::string p =
"<tr>"
1902 "<td class=\"mode\">";
1923 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1924 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1926 "<td class=\"name\">"
1934 if (!p.empty() && p[p.size() - 1] !=
'/')
1942 p += e.
path +
"\">";
1947 p +=
"</a></td></tr>";
1953 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1954 if (pp) startp = pp+1;
1963 stringresp +=
"</table></div><br><br><hr size=1>"
1964 "<p><span id=\"requestby\">Request by ";
2026 XrdHttpReq::ReturnGetHeaders() {
2027 std::string responseHeader;
2033 if (!responseHeader.empty()) {
2034 responseHeader +=
"\r\n";
2037 responseHeader += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2049 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;
2068 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2070 if (!responseHeader.empty()) {
2072 s += responseHeader.
c_str();
2075 if (m_transfer_encoding_chunked && m_trailer_headers) {
2076 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2078 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2085 for (
auto &ur : uranges) {
2086 cnt += ur.end - ur.start + 1;
2091 (
char *)
"123456").size();
2095 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2101 if (m_transfer_encoding_chunked && m_trailer_headers) {
2102 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2104 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2111 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2113 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2114 mapXrdErrorToHttpStatus();
2119 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2128 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2133 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2140 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2147 <<
" stat=" << (
char *)
iovP[0].iov_base);
2150 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2159 prot->SendSimpleResp(200, NULL,
"Accept-Ranges: bytes", NULL,
filesize,
keepalive);
2164 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2167 return ret_keepalive ? 1 : -1;
2170 std::string response_headers;
2171 int response = PostProcessChecksum(response_headers);
2172 if (-1 == response) {
2175 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2176 response_headers +=
"Accept-Ranges: bytes";
2177 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2180 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2202 if (
iovP[1].iov_len > 1) {
2204 <<
" stat=" << (
char *)
iovP[1].iov_base);
2207 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2228 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2229 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2238 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2239 httpStatusText.c_str(), httpStatusText.length(),
false);
2252 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2253 httpStatusText.c_str(), httpStatusText.length(),
false);
2260 return PostProcessListing(final_);
2270 httpStatusText = rrerror.
errMsg;
2273 if (m_transfer_encoding_chunked && m_trailer_headers) {
2274 if (prot->ChunkRespHeader(0))
2277 const std::string crlf =
"\r\n";
2278 std::stringstream ss;
2279 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2281 const auto header = ss.str();
2282 if (prot->SendData(header.c_str(), header.size()))
2285 if (prot->ChunkRespFooter())
2289 if (rrerror)
return -1;
2296 sendFooterError(
"");
2301 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2304 getReadResponse(received);
2308 rc = sendReadResponseSingleRange(received);
2310 rc = sendReadResponsesMultiRanges(received);
2331 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2332 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2340 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2343 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2362 if (m_transfer_encoding_chunked) {
2363 m_current_chunk_offset += l;
2367 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2375 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2378 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2379 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2400 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2401 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2416 <<
" stat=" << (
char *)
iovP[0].iov_base);
2419 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2431 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2434 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2435 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2447 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2448 httpStatusText.c_str(), httpStatusText.length(),
false);
2466 <<
" stat=" << (
char *)
iovP[0].iov_base);
2469 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2475 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2480 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2508 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2509 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2511 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2515 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2516 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2518 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2523 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2533 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";
2536 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2550 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2554 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2556 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2557 strncpy(entry, (
char *) startp, endp - startp);
2558 entry[endp - startp] = 0;
2565 <<
" stat=" << endp);
2568 sscanf(endp,
"%ld %lld %ld %ld",
2576 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2596 if (*p.rbegin() !=
'/') p +=
"/";
2600 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2624 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2625 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2627 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2631 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2632 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2634 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2637 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2645 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2646 if (pp) startp = pp+1;
2657 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";
2660 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2680 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2682 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2683 httpStatusText.c_str(), httpStatusText.length(),
false);
2688 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2696 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2700 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2713 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2714 httpStatusText.c_str(), httpStatusText.length(),
false);
2728 XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2729 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2737 if (prot->ChunkRespHeader(0))
2740 std::stringstream ss;
2741 ss << httpStatusCode <<
": " << httpStatusText;
2742 if (!extra_text.empty())
2743 ss <<
": " << extra_text;
2747 const auto header =
"X-Transfer-Status: " + ss.str();
2748 if (prot->SendData(header.c_str(), header.size()))
2751 prot->ChunkRespFooter();
2753 TRACEI(REQ, httpStatusCode <<
": " << httpStatusText << (extra_text.empty() ?
"" : (
": " + extra_text)));
2759 TRACE(REQ,
" XrdHttpReq request ended.");
2801 m_transfer_encoding_chunked =
false;
2802 m_current_chunk_size = -1;
2803 m_current_chunk_offset = 0;
2805 m_trailer_headers =
false;
2806 m_status_trailer =
false;
2841 void XrdHttpReq::getfhandle() {
2844 TRACEI(REQ,
"fhandle:" <<
2858 for (
int i = 0; i <
iovN; i++) {
2860 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2862 len = ntohl(l->
rlen);
2875 for (
int i = 0; i <
iovN; i++) {
2876 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2881 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2883 if (received.size() == 0) {
2899 std::string st_header;
2900 std::string fin_header;
2907 std::vector<rinfo> rvec;
2910 rvec.reserve(received.size());
2912 for(
const auto &rcv: received) {
2921 rentry.start = start;
2922 rentry.finish = finish;
2931 rentry.st_header = s;
2932 sum_len += s.size();
2935 sum_len += rcv.size;
2939 rentry.fin_header = s;
2940 sum_len += s.size();
2943 rvec.push_back(rentry);
2948 if (m_transfer_encoding_chunked && m_trailer_headers) {
2949 prot->ChunkRespHeader(sum_len);
2953 for(
const auto &rentry: rvec) {
2956 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2957 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2963 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2967 if (rentry.finish) {
2968 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2975 if (m_transfer_encoding_chunked && m_trailer_headers) {
2976 prot->ChunkRespFooter();
2982 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2985 if (received.size() == 0) {
2995 for(
const auto &rcv: received) {
3004 if (m_transfer_encoding_chunked && m_trailer_headers) {
3005 prot->ChunkRespHeader(sum);
3007 for(
const auto &rcv: received) {
3008 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3010 if (m_transfer_encoding_chunked && m_trailer_headers) {
3011 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.
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
static bool Import(const char *var, char *&val)
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