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";
932 int has_query_params = 0;
936 if (has_query_params == 0) {
940 has_query_params = 1;
941 auto length_str = std::to_string(
length);
947 opaque->
Put(
"oss.asize", length_str.c_str());
953 if (has_query_params == 0) {
965 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
966 << header2cgistrObf.c_str() <<
"'");
987 if (r < 0)
return -1;
1001 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
1007 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
1016 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1028 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1040 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1064 if (
resource ==
"/static/css/xrdhttp.css") {
1065 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1069 if (
resource ==
"/static/icons/xrdhttp.ico") {
1070 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1090 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1100 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1132 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1150 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1162 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1167 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1178 mapXrdErrorToHttpStatus();
1179 sendFooterError(
"Could not run close request on the bridge");
1190 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1204 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1215 l = res.length() + 1;
1219 mapXrdErrorToHttpStatus();
1220 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1221 sendFooterError(
"Could not run listing request on the bridge");
1234 auto retval = ReturnGetHeaders();
1247 if ( readChunkList.empty() )
1255 TRACEI(REQ,
" Failed to run close request on the bridge.");
1269 if ( readChunkList.size() == 1 ) {
1281 offs = readChunkList[0].offset;
1282 l = readChunkList[0].size;
1290 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1293 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1302 TRACE(ALL,
" Data sizes mismatch.");
1306 TRACE(ALL,
" No more bytes to send.");
1313 httpStatusCode = 416;
1314 httpStatusText =
"Range Not Satisfiable";
1315 std::stringstream ss;
1316 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1317 sendFooterError(ss.str());
1322 mapXrdErrorToHttpStatus();
1323 sendFooterError(
"Could not run read request on the bridge");
1332 mapXrdErrorToHttpStatus();
1333 sendFooterError(
"Could not run ReadV request on the bridge");
1363 if (! XrdHttpProtocol::usingEC)
1369 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1384 if (m_transfer_encoding_chunked) {
1385 if (m_current_chunk_size == m_current_chunk_offset) {
1388 if (prot->BuffUsed() < 2)
return 1;
1389 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1390 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1393 prot->BuffConsume(2);
1394 if (m_current_chunk_size == 0) {
1398 m_transfer_encoding_chunked =
false;
1402 m_current_chunk_size = -1;
1403 m_current_chunk_offset = 0;
1405 if (!prot->BuffUsed())
return 1;
1407 if (-1 == m_current_chunk_size) {
1411 bool found_newline =
false;
1418 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1419 for (; idx < max_chunk_size_chars; idx++) {
1420 if (prot->myBuffStart[idx] ==
'\n') {
1421 found_newline =
true;
1427 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1428 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1429 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1432 if (found_newline) {
1433 char *endptr = NULL;
1434 std::string line_contents(prot->myBuffStart, idx);
1435 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1437 if (*endptr !=
';' && *endptr !=
'\r') {
1438 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1439 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1442 m_current_chunk_size = chunk_contents;
1443 m_current_chunk_offset = 0;
1444 prot->BuffConsume(idx + 1);
1445 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1452 if (m_current_chunk_size == 0) {
1461 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1462 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1463 chunk_bytes_remaining);
1468 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1469 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1470 mapXrdErrorToHttpStatus();
1471 sendFooterError(
"Could not run write request on the bridge");
1476 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1486 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1492 TRACEI(REQ,
"Writing " << bytes_to_read);
1493 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1494 mapXrdErrorToHttpStatus();
1495 sendFooterError(
"Could not run write request on the bridge");
1518 mapXrdErrorToHttpStatus();
1519 sendFooterError(
"Could not run close request on the bridge");
1535 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);
1538 return ret_keepalive ? 1 : -1;
1560 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1580 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1594 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1610 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1625 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1630 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1635 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1654 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1686 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1712 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1733 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1738 strcpy(buf2,
host.c_str());
1739 char *pos = strchr(buf2,
':');
1740 if (pos) *pos =
'\0';
1748 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1762 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1772 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1783 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1786 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1791 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1792 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1795 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1796 if (convert_to_base64) {
1797 size_t digest_length = strlen(digest_value);
1798 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1799 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1800 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1801 free(digest_binary_value);
1804 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1806 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1807 free(digest_binary_value);
1808 digest_value = digest_base64_value;
1811 digest_header =
"Digest: ";
1813 digest_header +=
"=";
1814 digest_header += digest_value;
1815 if (convert_to_base64) {free(digest_value);}
1818 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1824 XrdHttpReq::PostProcessListing(
bool final_) {
1827 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1828 httpStatusText.c_str(), httpStatusText.length(),
false);
1834 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1835 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1837 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1838 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1839 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1860 "<th class=\"mode\">Mode</th>"
1861 "<th class=\"flags\">Flags</th>"
1862 "<th class=\"size\">Size</th>"
1863 "<th class=\"datetime\">Modified</th>"
1864 "<th class=\"name\">Name</th>"
1870 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1873 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1875 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1876 strncpy(entry, (
char *) startp, endp - startp);
1877 entry[endp - startp] = 0;
1884 <<
" stat=" << endp);
1887 sscanf(endp,
"%ld %lld %ld %ld",
1893 strcpy(entry, (
char *) startp);
1895 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1897 std::string p =
"<tr>"
1898 "<td class=\"mode\">";
1919 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1920 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1922 "<td class=\"name\">"
1930 if (!p.empty() && p[p.size() - 1] !=
'/')
1938 p += e.
path +
"\">";
1943 p +=
"</a></td></tr>";
1949 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1950 if (pp) startp = pp+1;
1959 stringresp +=
"</table></div><br><br><hr size=1>"
1960 "<p><span id=\"requestby\">Request by ";
2022 XrdHttpReq::ReturnGetHeaders() {
2023 std::string responseHeader;
2029 if (!responseHeader.empty()) {
2030 responseHeader +=
"\r\n";
2033 responseHeader += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2045 if (m_transfer_encoding_chunked && m_trailer_headers) {
2046 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2048 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2056 if (uranges.size() != 1)
2061 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2064 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2066 if (!responseHeader.empty()) {
2068 s += responseHeader.
c_str();
2071 if (m_transfer_encoding_chunked && m_trailer_headers) {
2072 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2074 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2081 for (
auto &ur : uranges) {
2082 cnt += ur.end - ur.start + 1;
2087 (
char *)
"123456").size();
2091 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2097 if (m_transfer_encoding_chunked && m_trailer_headers) {
2098 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2100 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2107 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2109 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2110 mapXrdErrorToHttpStatus();
2115 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2124 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2129 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2136 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2143 <<
" stat=" << (
char *)
iovP[0].iov_base);
2146 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2155 prot->SendSimpleResp(200, NULL,
"Accept-Ranges: bytes", NULL,
filesize,
keepalive);
2160 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2163 return ret_keepalive ? 1 : -1;
2166 std::string response_headers;
2167 int response = PostProcessChecksum(response_headers);
2168 if (-1 == response) {
2171 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2172 response_headers +=
"Accept-Ranges: bytes";
2173 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2176 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2198 if (
iovP[1].iov_len > 1) {
2200 <<
" stat=" << (
char *)
iovP[1].iov_base);
2203 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2224 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2225 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2234 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2235 httpStatusText.c_str(), httpStatusText.length(),
false);
2248 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2249 httpStatusText.c_str(), httpStatusText.length(),
false);
2256 return PostProcessListing(final_);
2266 httpStatusText = rrerror.
errMsg;
2269 if (m_transfer_encoding_chunked && m_trailer_headers) {
2270 if (prot->ChunkRespHeader(0))
2273 const std::string crlf =
"\r\n";
2274 std::stringstream ss;
2275 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2277 const auto header = ss.str();
2278 if (prot->SendData(header.c_str(), header.size()))
2281 if (prot->ChunkRespFooter())
2285 if (rrerror)
return -1;
2292 sendFooterError(
"");
2297 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2300 getReadResponse(received);
2304 rc = sendReadResponseSingleRange(received);
2306 rc = sendReadResponsesMultiRanges(received);
2327 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2328 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2336 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2339 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2358 if (m_transfer_encoding_chunked) {
2359 m_current_chunk_offset += l;
2363 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2370 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2373 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2374 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2395 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2396 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2411 <<
" stat=" << (
char *)
iovP[0].iov_base);
2414 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2426 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2429 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2430 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2442 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2443 httpStatusText.c_str(), httpStatusText.length(),
false);
2461 <<
" stat=" << (
char *)
iovP[0].iov_base);
2464 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2470 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2475 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2503 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2504 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2506 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2510 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2511 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2513 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2518 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2528 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";
2531 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2545 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2549 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2551 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2552 strncpy(entry, (
char *) startp, endp - startp);
2553 entry[endp - startp] = 0;
2560 <<
" stat=" << endp);
2563 sscanf(endp,
"%ld %lld %ld %ld",
2571 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2591 if (*p.rbegin() !=
'/') p +=
"/";
2595 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2619 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2620 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2622 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2626 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2627 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2629 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2632 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2640 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2641 if (pp) startp = pp+1;
2652 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";
2655 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2675 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2677 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2678 httpStatusText.c_str(), httpStatusText.length(),
false);
2683 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2691 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2695 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2708 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2709 httpStatusText.c_str(), httpStatusText.length(),
false);
2723 XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2724 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2732 if (prot->ChunkRespHeader(0))
2735 std::stringstream ss;
2736 ss << httpStatusCode <<
": " << httpStatusText;
2737 if (!extra_text.empty())
2738 ss <<
": " << extra_text;
2742 const auto header =
"X-Transfer-Status: " + ss.str();
2743 if (prot->SendData(header.c_str(), header.size()))
2746 prot->ChunkRespFooter();
2748 TRACEI(REQ, httpStatusCode <<
": " << httpStatusText << (extra_text.empty() ?
"" : (
": " + extra_text)));
2754 TRACE(REQ,
" XrdHttpReq request ended.");
2796 m_transfer_encoding_chunked =
false;
2797 m_current_chunk_size = -1;
2798 m_current_chunk_offset = 0;
2800 m_trailer_headers =
false;
2801 m_status_trailer =
false;
2836 void XrdHttpReq::getfhandle() {
2839 TRACEI(REQ,
"fhandle:" <<
2853 for (
int i = 0; i <
iovN; i++) {
2855 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2857 len = ntohl(l->
rlen);
2870 for (
int i = 0; i <
iovN; i++) {
2871 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2876 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2878 if (received.size() == 0) {
2894 std::string st_header;
2895 std::string fin_header;
2902 std::vector<rinfo> rvec;
2905 rvec.reserve(received.size());
2907 for(
const auto &rcv: received) {
2916 rentry.start = start;
2917 rentry.finish = finish;
2926 rentry.st_header = s;
2927 sum_len += s.size();
2930 sum_len += rcv.size;
2934 rentry.fin_header = s;
2935 sum_len += s.size();
2938 rvec.push_back(rentry);
2943 if (m_transfer_encoding_chunked && m_trailer_headers) {
2944 prot->ChunkRespHeader(sum_len);
2948 for(
const auto &rentry: rvec) {
2951 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2952 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2958 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2962 if (rentry.finish) {
2963 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2970 if (m_transfer_encoding_chunked && m_trailer_headers) {
2971 prot->ChunkRespFooter();
2977 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2980 if (received.size() == 0) {
2990 for(
const auto &rcv: received) {
2999 if (m_transfer_encoding_chunked && m_trailer_headers) {
3000 prot->ChunkRespHeader(sum);
3002 for(
const auto &rcv: received) {
3003 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3005 if (m_transfer_encoding_chunked && m_trailer_headers) {
3006 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