39 #include "XrdVersion.hh"
44 #include <arpa/inet.h>
65 #define MAX_TK_LEN 256
66 #define MAX_RESOURCE_LEN 16384
69 #define TRACELINK prot->Link
73 const char *TraceID =
"Req";
76 void trim(std::string &str)
86 memset(&t1, 0,
sizeof (t1));
89 strftime(datebuf, 127,
"%a, %d %b %Y %H:%M:%S GMT", &t1);
90 return (std::string) datebuf;
122 if (!line)
return -1;
125 char *p = strchr((
char *) line, (
int)
':');
141 char *val = line + pos + 1;
144 while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
148 std::string ss = val;
149 if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) !=
"\r\n") {
162 if (!strcasecmp(key,
"connection")) {
164 if (!strcasecmp(val,
"Keep-Alive\r\n")) {
166 }
else if (!strcasecmp(val,
"close\r\n")) {
170 }
else if (!strcasecmp(key,
"host")) {
172 }
else if (!strcasecmp(key,
"range")) {
177 }
else if (!strcasecmp(key,
"content-length")) {
180 }
else if (!strcasecmp(key,
"destination")) {
183 }
else if (!strcasecmp(key,
"want-digest")) {
188 }
else if (!strcasecmp(key,
"depth")) {
190 if (strcmp(val,
"infinity"))
193 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
195 }
else if (!strcasecmp(key,
"te") && strstr(val,
"trailers")) {
196 m_trailer_headers =
true;
197 }
else if (!strcasecmp(key,
"transfer-encoding") && strstr(val,
"chunked")) {
198 m_transfer_encoding_chunked =
true;
199 }
else if (!strcasecmp(key,
"x-transfer-status") && strstr(val,
"true")) {
200 m_transfer_encoding_chunked =
true;
201 m_status_trailer =
true;
202 }
else if (!strcasecmp(key,
"scitag")) {
206 }
else if (!strcasecmp(key,
"user-agent")) {
211 auto it = std::find_if(prot->
hdr2cgimap.begin(), prot->
hdr2cgimap.end(),[key](
const auto & item) {
212 return !strcasecmp(key,item.first.c_str());
216 s.assign(val, line+len-val);
229 int XrdHttpReq::parseHost(
char *line) {
235 void XrdHttpReq::parseScitag(
const std::string & val) {
239 std::string scitagS = val;
242 if(scitagS[0] !=
'-') {
244 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
257 addCgi(
"pmark.appname",this->
request == ReqType::rtGET ?
"http-get" :
"http-put");
268 if (!line)
return -1;
271 char *p = strchr((
char *) line, (
int)
' ');
295 char *val = line + pos + 1;
302 p = strchr((
char *) val, (
int)
' ');
316 if (!strcmp(key,
"GET")) {
318 }
else if (!strcmp(key,
"HEAD")) {
320 }
else if (!strcmp(key,
"PUT")) {
322 }
else if (!strcmp(key,
"POST")) {
324 }
else if (!strcmp(key,
"PATCH")) {
326 }
else if (!strcmp(key,
"OPTIONS")) {
328 }
else if (!strcmp(key,
"DELETE")) {
330 }
else if (!strcmp(key,
"PROPFIND")) {
333 }
else if (!strcmp(key,
"MKCOL")) {
336 }
else if (!strcmp(key,
"MOVE")) {
346 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
360 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
367 for (
int i = 0; i < nitems; i++) {
378 void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
385 for (
int i = 0; i < nitems; i++) {
403 for (
const auto &c: cl) {
406 memcpy(&ra.fhandle, this->fhandle, 4);
408 ra.offset = c.offset;
422 clientMarshallReadAheadList(j);
431 std::ostringstream s;
433 s <<
"\r\n--" << token <<
"\r\n";
434 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
435 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
441 std::ostringstream s;
443 s <<
"\r\n--" << token <<
"--\r\n";
456 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
462 this->
final = final_;
464 if (PostProcessHTTPReq(final_))
reset();
478 int rc = info.
Send(0, 0, 0, 0);
479 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
496 TRACE(REQ,
" XrdHttpReq::Done");
502 int r = PostProcessHTTPReq(
true);
505 if (r < 0)
return false;
516 TRACE(REQ,
" XrdHttpReq::Error");
527 auto rc = PostProcessHTTPReq();
560 if (strncmp(hname,
"file://", 7) == 0)
562 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
569 char *pp = strchr((
char *)hname,
'?');
575 int varlen = strlen(vardata);
578 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
587 sprintf(buf,
":%d", port);
631 return ret_keepalive;
642 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
661 s +=
"&xrdhttptime=";
663 sprintf(buf,
"%lld", (
long long) tnow);
668 s +=
"&xrdhttpname=";
674 s +=
"&xrdhttpvorg=";
679 s +=
"&xrdhttphost=";
689 s +=
"&xrdhttprole=";
694 s +=
"&xrdhttpgrps=";
699 s +=
"&xrdhttpendorsements=";
704 s +=
"&xrdhttpcredslen=";
706 sprintf(buf,
"%d", secent->
credslen);
712 s +=
"&xrdhttpcreds=";
726 void XrdHttpReq::sanitizeResourcePfx() {
757 void XrdHttpReq::parseResource(
char *res) {
763 char *p = strchr(res,
'?');
771 sanitizeResourcePfx();
796 sanitizeResourcePfx();
821 void XrdHttpReq::sendWebdavErrorMessage(
823 XRequestTypes xrdOperation, std::string etext,
const char *desc,
824 const char *header_to_add,
bool keepalive) {
826 std::string errCode{
"Unknown"};
827 std::string statusText;
862 httpStatusCode = code;
863 httpErrorCode = errCode;
864 httpErrorBody =
"ERROR: " + errCode +
": " +
etext +
"\n";
866 prot->SendSimpleResp(httpStatusCode, desc, header_to_add,
867 httpErrorBody.c_str(), httpErrorBody.length(),
875 void XrdHttpReq::mapXrdErrorToHttpStatus() {
877 httpStatusCode = 500;
878 httpErrorBody =
"Unrecognized error";
884 httpStatusCode = 401; httpErrorBody =
"Unauthorized";
887 httpStatusCode = 403; httpErrorBody =
"Operation not permitted";
890 httpStatusCode = 404; httpErrorBody =
"File not found";
893 httpStatusCode = 405; httpErrorBody =
"Operation not supported";
896 httpStatusCode = 423; httpErrorBody =
"Resource is a locked";
899 httpStatusCode = 409; httpErrorBody =
"Resource is a directory";
902 if(
request != ReqType::rtDELETE) {
903 httpStatusCode = 409; httpErrorBody =
"File already exists";
908 httpStatusCode = 405;
912 httpStatusCode = 405; httpErrorBody =
"Method is not allowed";
915 httpStatusCode = 502; httpErrorBody =
"Bad Gateway";
918 httpStatusCode = 504; httpErrorBody =
"Gateway timeout";
927 <<
"] to status code [" << httpStatusCode <<
"]");
929 httpErrorBody +=
"\n";
931 httpStatusCode = 200;
932 httpErrorBody =
"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() <<
"'");
998 if (r < 0)
return -1;
1012 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
1018 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
1027 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1039 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1051 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1075 if (
resource ==
"/static/css/xrdhttp.css") {
1076 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1080 if (
resource ==
"/static/icons/xrdhttp.ico") {
1081 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1101 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1111 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1143 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1161 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1173 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1178 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1189 mapXrdErrorToHttpStatus();
1190 return sendFooterError(
"Could not run close request on the bridge");
1200 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1214 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1225 l = res.length() + 1;
1229 mapXrdErrorToHttpStatus();
1230 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1231 sendFooterError(
"Could not run listing request on the bridge");
1244 auto retval = ReturnGetHeaders();
1265 TRACEI(REQ,
" Failed to run close request on the bridge.");
1279 if ( readChunkList.size() == 1 ) {
1291 offs = readChunkList[0].offset;
1292 l = readChunkList[0].size;
1300 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1303 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1312 TRACE(ALL,
" Data sizes mismatch.");
1316 TRACE(ALL,
" No more bytes to send.");
1323 httpStatusCode = 416;
1324 httpErrorBody =
"Range Not Satisfiable";
1325 std::stringstream ss;
1326 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1327 return sendFooterError(ss.str());
1331 mapXrdErrorToHttpStatus();
1332 return sendFooterError(
"Could not run read request on the bridge");
1340 mapXrdErrorToHttpStatus();
1341 return sendFooterError(
"Could not run ReadV request on the bridge");
1370 if (! XrdHttpProtocol::usingEC)
1376 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1391 if (m_transfer_encoding_chunked) {
1392 if (m_current_chunk_size == m_current_chunk_offset) {
1395 if (prot->BuffUsed() < 2)
return 1;
1396 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1397 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1400 prot->BuffConsume(2);
1401 if (m_current_chunk_size == 0) {
1405 m_transfer_encoding_chunked =
false;
1409 m_current_chunk_size = -1;
1410 m_current_chunk_offset = 0;
1412 if (!prot->BuffUsed())
return 1;
1414 if (-1 == m_current_chunk_size) {
1418 bool found_newline =
false;
1425 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1426 for (; idx < max_chunk_size_chars; idx++) {
1427 if (prot->myBuffStart[idx] ==
'\n') {
1428 found_newline =
true;
1434 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1435 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1436 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1439 if (found_newline) {
1440 char *endptr = NULL;
1441 std::string line_contents(prot->myBuffStart, idx);
1442 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1444 if (*endptr !=
';' && *endptr !=
'\r') {
1445 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1446 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1449 m_current_chunk_size = chunk_contents;
1450 m_current_chunk_offset = 0;
1451 prot->BuffConsume(idx + 1);
1452 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1459 if (m_current_chunk_size == 0) {
1468 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1469 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1470 chunk_bytes_remaining);
1475 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1476 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1477 mapXrdErrorToHttpStatus();
1478 return sendFooterError(
"Could not run write request on the bridge");
1482 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1492 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1498 TRACEI(REQ,
"Writing " << bytes_to_read);
1499 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1500 mapXrdErrorToHttpStatus();
1501 return sendFooterError(
"Could not run write request on the bridge");
1523 mapXrdErrorToHttpStatus();
1524 return 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, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1828 XrdHttpReq::PostProcessListing(
bool final_) {
1831 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1832 httpErrorBody.c_str(), httpErrorBody.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] !=
'/')
1939 std::unique_ptr<char, decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1945 p +=
"</a></td></tr>";
1951 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1952 if (pp) startp = pp+1;
1961 stringresp +=
"</table></div><br><br><hr size=1>"
1962 "<p><span id=\"requestby\">Request by ";
2024 XrdHttpReq::ReturnGetHeaders() {
2025 std::string responseHeader;
2030 if (!responseHeader.empty()) {
2031 responseHeader +=
"\r\n";
2033 addAgeHeader(responseHeader);
2045 if (m_transfer_encoding_chunked && m_trailer_headers) {
2047 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2049 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2057 if (uranges.size() != 1)
2062 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2064 std::string header =
"Content-Range: bytes ";
2065 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2067 if (!responseHeader.empty()) {
2069 header += responseHeader.c_str();
2072 if (m_transfer_encoding_chunked && m_trailer_headers) {
2074 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
2076 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
2083 for (
auto &ur : uranges) {
2084 cnt += ur.end - ur.start + 1;
2089 (
char *)
"123456").size();
2093 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2099 if (!header.empty()) {
2102 addAgeHeader(header);
2105 if (m_transfer_encoding_chunked && m_trailer_headers) {
2107 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2109 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2115 if (m_status_trailer) {
2116 if (header.empty()) {
2117 header +=
"Trailer: X-Transfer-Status";
2119 header +=
"\r\nTrailer: X-Transfer-Status";
2126 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2128 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2129 mapXrdErrorToHttpStatus();
2134 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2143 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2148 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2155 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2159 std::string response_headers;
2163 <<
" stat=" << (
char *)
iovP[0].iov_base);
2166 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2176 addAgeHeader(response_headers);
2177 response_headers +=
"\r\n";
2179 response_headers +=
"Accept-Ranges: bytes";
2180 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2185 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2188 return ret_keepalive ? 1 : -1;
2191 std::string response_headers;
2192 int response = PostProcessChecksum(response_headers);
2193 if (-1 == response) {
2196 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2198 addAgeHeader(response_headers);
2199 response_headers +=
"\r\n";
2201 response_headers +=
"Accept-Ranges: bytes";
2202 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2205 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2227 if (
iovP[1].iov_len > 1) {
2229 <<
" stat=" << (
char *)
iovP[1].iov_base);
2232 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2253 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2254 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2263 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2264 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2277 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2278 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2285 return PostProcessListing(final_);
2295 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2302 httpErrorBody = rrerror.
errMsg;
2305 if (m_transfer_encoding_chunked && m_trailer_headers) {
2306 if (prot->ChunkRespHeader(0))
2309 const std::string crlf =
"\r\n";
2310 std::stringstream ss;
2311 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpErrorBody << crlf;
2313 const auto header = ss.str();
2314 if (prot->SendData(header.c_str(), header.size()))
2317 if (prot->ChunkRespFooter())
2321 if (rrerror)
return -1;
2328 auto rc = sendFooterError(
"");
2337 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2340 getReadResponse(received);
2344 rc = sendReadResponseSingleRange(received);
2346 rc = sendReadResponsesMultiRanges(received);
2374 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2377 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2396 if (m_transfer_encoding_chunked) {
2397 m_current_chunk_offset += l;
2401 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2409 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2432 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2433 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2448 <<
" stat=" << (
char *)
iovP[0].iov_base);
2451 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2463 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2466 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2467 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2479 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2480 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2498 <<
" stat=" << (
char *)
iovP[0].iov_base);
2501 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2507 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2512 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2540 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2541 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2544 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2548 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2549 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2551 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2556 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2566 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";
2569 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2583 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2587 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2589 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2590 strncpy(entry, (
char *) startp, endp - startp);
2591 entry[endp - startp] = 0;
2598 <<
" stat=" << endp);
2601 sscanf(endp,
"%ld %lld %ld %ld",
2609 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2629 if (*p.rbegin() !=
'/') p +=
"/";
2633 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2657 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2658 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2661 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2665 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2666 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2668 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2671 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2679 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2680 if (pp) startp = pp+1;
2691 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";
2694 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2714 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2716 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2717 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2722 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2730 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2734 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2747 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2748 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2762 XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2763 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2771 if (prot->ChunkRespHeader(0))
2774 std::stringstream ss;
2776 ss << httpStatusCode;
2777 if (!httpErrorBody.empty()) {
2778 std::string_view statusView(httpErrorBody);
2781 if (statusView[statusView.size() - 1] ==
'\n') {
2782 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2784 ss <<
": " << httpErrorBody;
2788 if (!extra_text.empty())
2789 ss <<
": " << extra_text;
2793 const auto header =
"X-Transfer-Status: " + ss.str();
2794 if (prot->SendData(header.c_str(), header.size()))
2797 if (prot->ChunkRespFooter())
2802 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2808 void XrdHttpReq::addAgeHeader(std::string &headers) {
2810 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2815 TRACE(REQ,
" XrdHttpReq request ended.");
2858 m_transfer_encoding_chunked =
false;
2859 m_current_chunk_size = -1;
2860 m_current_chunk_offset = 0;
2862 m_trailer_headers =
false;
2863 m_status_trailer =
false;
2898 void XrdHttpReq::getfhandle() {
2901 TRACEI(REQ,
"fhandle:" <<
2915 for (
int i = 0; i <
iovN; i++) {
2917 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2919 len = ntohl(l->
rlen);
2932 for (
int i = 0; i <
iovN; i++) {
2933 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2938 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2940 if (received.size() == 0) {
2956 std::string st_header;
2957 std::string fin_header;
2964 std::vector<rinfo> rvec;
2967 rvec.reserve(received.size());
2969 for(
const auto &rcv: received) {
2978 rentry.start = start;
2979 rentry.finish = finish;
2988 rentry.st_header = s;
2989 sum_len += s.size();
2992 sum_len += rcv.size;
2996 rentry.fin_header = s;
2997 sum_len += s.size();
3000 rvec.push_back(rentry);
3005 if (m_transfer_encoding_chunked && m_trailer_headers) {
3006 prot->ChunkRespHeader(sum_len);
3010 for(
const auto &rentry: rvec) {
3013 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
3014 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
3020 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
3024 if (rentry.finish) {
3025 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
3032 if (m_transfer_encoding_chunked && m_trailer_headers) {
3033 prot->ChunkRespFooter();
3039 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
3042 if (received.size() == 0) {
3052 for(
const auto &rcv: received) {
3061 if (m_transfer_encoding_chunked && m_trailer_headers) {
3062 prot->ChunkRespHeader(sum);
3064 for(
const auto &rcv: received) {
3065 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3067 if (m_transfer_encoding_chunked && m_trailer_headers) {
3068 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)
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)
Utility functions for XrdHTTP.
std::string encode_opaque(const std::string &opaque)
std::string encode_str(const std::string &str)
std::vector< XrdOucIOVec2 > XrdHttpIOList
std::string decode_str(const std::string &str)
std::string obfuscateAuth(const std::string &input)
XrdHttpChecksumRawPtr getChecksumToRun(const std::string &userDigest) const
bool needsBase64Padding() const
std::string getXRootDConfigDigestName() const
std::string getHttpName() const
virtual int ProcessReq(XrdHttpExtReq &)=0
static char * secretkey
The key used to calculate the url hashes.
static kXR_int32 myRole
Our role.
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static XrdHttpChecksumHandler cksumHandler
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
XrdLink * Link
The link we are bound to.
int doStat(char *fname)
Perform a Stat request.
static bool isdesthttps
True if the redirections must be towards https targets.
static char * listredir
Url to redirect to in the case a listing is requested.
static bool listdeny
If true, any form of listing is denied.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
void reset()
resets this handler
const XrdHttpIOList & NextReadList()
return XrdHttpIOList for sending to read or readv
void ParseContentRange(const char *const line)
parse the line after a "Range: " http request header
int SetFilesize(const off_t sz)
sets the filesize, used during resolving and issuing range requests
void NotifyError()
Force handler to enter error state.
bool isFullFile()
indicates when there were no valid Range head ranges supplied
std::vector< UserRange > UserRangeList
int NotifyReadResult(const ssize_t ret, const UserRange **const urp, bool &start, bool &allend)
Advance internal counters concerning received bytes.
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.
ReqType
These are the HTTP/DAV requests that we support.
int parseLine(char *line, int len)
Parse the header.
std::string buildPartialHdrEnd(char *token)
Build the closing part for a multipart response.
XrdHttpChecksumHandler::XrdHttpChecksumRawPtr m_req_cksum
The checksum that was ran for this request.
void setTransferStatusHeader(std::string &header)
bool m_appended_hdr2cgistr
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
bool m_appended_asize
Track whether we already appended the oss.asize argument for PUTs.
XrdOucString m_resource_with_digest
virtual bool Redir(XrdXrootd::Bridge::Context &info, int port, const char *hname)
virtual int File(XrdXrootd::Bridge::Context &info, int dlen)
std::map< std::string, std::string > allheaders
void addCgi(const std::string &key, const std::string &value)
ClientRequest xrdreq
The last issued xrd request, often pending.
std::string buildPartialHdr(long long bytestart, long long byteend, long long filesize, char *token)
Build a partial header for a multipart response.
XrdHttpReadRangeHandler readRangeHandler
Tracking the next ranges of data to read during GET.
virtual bool Error(XrdXrootd::Bridge::Context &info, int ecode, const char *etext)
char * ID
Pointer to the client's link identity.
static const int minTotID
static const int maxTotID
char * Get(const char *varname)
void Put(const char *varname, const char *value)
const char * c_str() const
void assign(const char *s, int j, int k=-1)
int erasefromstart(int sz=0)
int erasefromend(int sz=0)
int erase(int start=0, int size=0)
int find(const char c, int start=0, bool forward=1)
static void trim(std::string &str)
char * vorg
Entity's virtual organization(s)
int credslen
Length of the 'creds' data.
char * creds
Raw entity credentials or cert.
char * grps
Entity's group name(s)
char * name
Entity's name.
char * role
Entity's role(s)
char * endorsements
Protocol specific endorsements.
char * moninfo
Information for monitoring.
char * host
Entity's host name dnr dependent.
virtual int Send(const struct iovec *headP, int headN, const struct iovec *tailP, int tailN)
virtual int setSF(kXR_char *fhandle, bool seton=false)=0
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0