39 #include "XrdVersion.hh"
44 #include <arpa/inet.h>
64 #define MAX_TK_LEN 256
65 #define MAX_RESOURCE_LEN 16384
68 #define TRACELINK prot->Link
72 const char *TraceID =
"Req";
75 void trim(std::string &str)
85 memset(&t1, 0,
sizeof (t1));
88 strftime(datebuf, 127,
"%a, %d %b %Y %H:%M:%S GMT", &t1);
89 return (std::string) datebuf;
121 if (!line)
return -1;
124 char *p = strchr((
char *) line, (
int)
':');
140 char *val = line + pos + 1;
143 while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
147 std::string ss = val;
148 if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) !=
"\r\n") {
161 if (!strcasecmp(key,
"connection")) {
163 if (!strcasecmp(val,
"Keep-Alive\r\n")) {
165 }
else if (!strcasecmp(val,
"close\r\n")) {
169 }
else if (!strcasecmp(key,
"host")) {
171 }
else if (!strcasecmp(key,
"range")) {
176 }
else if (!strcasecmp(key,
"content-length")) {
179 }
else if (!strcasecmp(key,
"destination")) {
182 }
else if (!strcasecmp(key,
"want-digest")) {
187 }
else if (!strcasecmp(key,
"depth")) {
189 if (strcmp(val,
"infinity"))
192 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
194 }
else if (!strcasecmp(key,
"te") && strstr(val,
"trailers")) {
195 m_trailer_headers =
true;
196 }
else if (!strcasecmp(key,
"transfer-encoding") && strstr(val,
"chunked")) {
197 m_transfer_encoding_chunked =
true;
198 }
else if (!strcasecmp(key,
"x-transfer-status") && strstr(val,
"true")) {
199 m_transfer_encoding_chunked =
true;
200 m_status_trailer =
true;
201 }
else if (!strcasecmp(key,
"scitag")) {
205 }
else if (!strcasecmp(key,
"user-agent")) {
210 auto it = std::find_if(prot->
hdr2cgimap.begin(), prot->
hdr2cgimap.end(),[key](
const auto & item) {
211 return !strcasecmp(key,item.first.c_str());
215 s.assign(val, line+len-val);
228 int XrdHttpReq::parseHost(
char *line) {
234 void XrdHttpReq::parseScitag(
const std::string & val) {
238 std::string scitagS = val;
241 if(scitagS[0] !=
'-') {
243 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
262 if (!line)
return -1;
265 char *p = strchr((
char *) line, (
int)
' ');
289 char *val = line + pos + 1;
296 p = strchr((
char *) val, (
int)
' ');
310 if (!strcmp(key,
"GET")) {
312 }
else if (!strcmp(key,
"HEAD")) {
314 }
else if (!strcmp(key,
"PUT")) {
316 }
else if (!strcmp(key,
"POST")) {
318 }
else if (!strcmp(key,
"PATCH")) {
320 }
else if (!strcmp(key,
"OPTIONS")) {
322 }
else if (!strcmp(key,
"DELETE")) {
324 }
else if (!strcmp(key,
"PROPFIND")) {
327 }
else if (!strcmp(key,
"MKCOL")) {
330 }
else if (!strcmp(key,
"MOVE")) {
340 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
354 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
361 for (
int i = 0; i < nitems; i++) {
372 void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
379 for (
int i = 0; i < nitems; i++) {
397 for (
const auto &c: cl) {
400 memcpy(&ra.fhandle, this->fhandle, 4);
402 ra.offset = c.offset;
416 clientMarshallReadAheadList(j);
425 std::ostringstream s;
427 s <<
"\r\n--" << token <<
"\r\n";
428 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
429 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
435 std::ostringstream s;
437 s <<
"\r\n--" << token <<
"--\r\n";
450 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
456 this->
final = final_;
458 if (PostProcessHTTPReq(final_))
reset();
472 int rc = info.
Send(0, 0, 0, 0);
473 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
490 TRACE(REQ,
" XrdHttpReq::Done");
496 int r = PostProcessHTTPReq(
true);
499 if (r < 0)
return false;
510 TRACE(REQ,
" XrdHttpReq::Error");
521 if (PostProcessHTTPReq())
reset();
551 if (strncmp(hname,
"file://", 7) == 0)
553 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
560 char *pp = strchr((
char *)hname,
'?');
566 int varlen = strlen(vardata);
569 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
578 sprintf(buf,
":%d", port);
586 char *newvardata =
quote(vardata);
634 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
646 char *s1 =
quote(p+1);
663 s +=
"&xrdhttptime=";
665 sprintf(buf,
"%lld", (
long long) tnow);
670 s +=
"&xrdhttpname=";
679 s +=
"&xrdhttpvorg=";
688 s +=
"&xrdhttphost=";
706 s +=
"&xrdhttprole=";
715 s +=
"&xrdhttpgrps=";
724 s +=
"&xrdhttpendorsements=";
733 s +=
"&xrdhttpcredslen=";
735 sprintf(buf,
"%d", secent->
credslen);
736 char *s1 =
quote(buf);
745 s +=
"&xrdhttpcreds=";
749 char *s1 =
quote(zerocreds);
767 void XrdHttpReq::sanitizeResourcePfx() {
798 void XrdHttpReq::parseResource(
char *res) {
804 char *p = strchr(res,
'?');
812 sanitizeResourcePfx();
837 sanitizeResourcePfx();
868 void XrdHttpReq::mapXrdErrorToHttpStatus() {
870 httpStatusCode = 500;
871 httpStatusText =
"Unrecognized error";
877 httpStatusCode = 401; httpStatusText =
"Unauthorized";
880 httpStatusCode = 403; httpStatusText =
"Operation not permitted";
883 httpStatusCode = 404; httpStatusText =
"File not found";
886 httpStatusCode = 405; httpStatusText =
"Operation not supported";
889 httpStatusCode = 423; httpStatusText =
"Resource is a locked";
892 httpStatusCode = 409; httpStatusText =
"Resource is a directory";
895 if(
request != ReqType::rtDELETE) {
896 httpStatusCode = 409; httpStatusText =
"File already exists";
901 httpStatusCode = 405;
905 httpStatusCode = 405; httpStatusText =
"Method is not allowed";
908 httpStatusCode = 504; httpStatusText =
"Gateway timeout";
917 <<
"] to status code [" << httpStatusCode <<
"]");
919 httpStatusText +=
"\n";
921 httpStatusCode = 200;
922 httpStatusText =
"OK";
946 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
947 << header2cgistrObf.c_str() <<
"'");
968 if (r < 0)
return -1;
982 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
988 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
997 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1009 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1021 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1044 if (
resource ==
"/static/css/xrdhttp.css") {
1045 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1049 if (
resource ==
"/static/icons/xrdhttp.ico") {
1050 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1070 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1080 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1112 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1130 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1142 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1147 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1158 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1169 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1183 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1194 l = res.length() + 1;
1198 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1211 auto retval = ReturnGetHeaders();
1224 if ( readChunkList.empty() )
1232 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1243 if ( readChunkList.size() == 1 ) {
1255 offs = readChunkList[0].offset;
1256 l = readChunkList[0].size;
1264 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1267 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1276 TRACE(ALL,
" Data sizes mismatch.");
1280 TRACE(ALL,
" No more bytes to send.");
1287 TRACE(ALL,
" Requested range " << l <<
"@" << offs <<
1288 " is past the end of file (" <<
filesize <<
")");
1294 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run read request.", 0,
false);
1303 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run read request.", 0,
false);
1333 if (! XrdHttpProtocol::usingEC)
1339 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1354 if (m_transfer_encoding_chunked) {
1355 if (m_current_chunk_size == m_current_chunk_offset) {
1358 if (prot->BuffUsed() < 2)
return 1;
1359 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1360 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1363 prot->BuffConsume(2);
1364 if (m_current_chunk_size == 0) {
1368 m_transfer_encoding_chunked =
false;
1372 m_current_chunk_size = -1;
1373 m_current_chunk_offset = 0;
1375 if (!prot->BuffUsed())
return 1;
1377 if (-1 == m_current_chunk_size) {
1381 bool found_newline =
false;
1388 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1389 for (; idx < max_chunk_size_chars; idx++) {
1390 if (prot->myBuffStart[idx] ==
'\n') {
1391 found_newline =
true;
1397 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1398 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1399 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1402 if (found_newline) {
1403 char *endptr = NULL;
1404 std::string line_contents(prot->myBuffStart, idx);
1405 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1407 if (*endptr !=
';' && *endptr !=
'\r') {
1408 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1409 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1412 m_current_chunk_size = chunk_contents;
1413 m_current_chunk_offset = 0;
1414 prot->BuffConsume(idx + 1);
1415 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1422 if (m_current_chunk_size == 0) {
1431 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1432 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1433 chunk_bytes_remaining);
1438 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1439 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1440 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run write request.", 0,
false);
1445 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1455 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1461 TRACEI(REQ,
"Writing " << bytes_to_read);
1462 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1463 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run write request.", 0,
false);
1486 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1502 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);
1526 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1546 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1560 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1576 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1591 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1596 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1601 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1620 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1652 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1678 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1699 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1704 strcpy(buf2,
host.c_str());
1705 char *pos = strchr(buf2,
':');
1706 if (pos) *pos =
'\0';
1714 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1728 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1738 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1749 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1752 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1757 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1758 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1761 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1762 if (convert_to_base64) {
1763 size_t digest_length = strlen(digest_value);
1764 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1765 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1766 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1767 free(digest_binary_value);
1770 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1772 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1773 free(digest_binary_value);
1774 digest_value = digest_base64_value;
1777 digest_header =
"Digest: ";
1779 digest_header +=
"=";
1780 digest_header += digest_value;
1781 if (convert_to_base64) {free(digest_value);}
1784 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1790 XrdHttpReq::PostProcessListing(
bool final_) {
1793 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1794 httpStatusText.c_str(), httpStatusText.length(),
false);
1800 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1801 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1803 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1804 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1805 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1826 "<th class=\"mode\">Mode</th>"
1827 "<th class=\"flags\">Flags</th>"
1828 "<th class=\"size\">Size</th>"
1829 "<th class=\"datetime\">Modified</th>"
1830 "<th class=\"name\">Name</th>"
1836 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1839 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1841 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1842 strncpy(entry, (
char *) startp, endp - startp);
1843 entry[endp - startp] = 0;
1850 <<
" stat=" << endp);
1853 sscanf(endp,
"%ld %lld %ld %ld",
1859 strcpy(entry, (
char *) startp);
1861 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1863 std::string p =
"<tr>"
1864 "<td class=\"mode\">";
1885 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1886 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1888 "<td class=\"name\">"
1896 if (!p.empty() && p[p.size() - 1] !=
'/')
1904 p += e.
path +
"\">";
1909 p +=
"</a></td></tr>";
1915 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1916 if (pp) startp = pp+1;
1925 stringresp +=
"</table></div><br><br><hr size=1>"
1926 "<p><span id=\"requestby\">Request by ";
1988 XrdHttpReq::ReturnGetHeaders() {
1989 std::string responseHeader;
1995 if (!responseHeader.empty()) {
1996 responseHeader +=
"\r\n";
1999 responseHeader += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2011 if (m_transfer_encoding_chunked && m_trailer_headers) {
2012 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2014 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2022 if (uranges.size() != 1)
2027 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2030 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2032 if (!responseHeader.empty()) {
2034 s += responseHeader.
c_str();
2037 if (m_transfer_encoding_chunked && m_trailer_headers) {
2038 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2040 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2047 for (
auto &ur : uranges) {
2048 cnt += ur.end - ur.start + 1;
2053 (
char *)
"123456").size();
2057 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2063 if (m_transfer_encoding_chunked && m_trailer_headers) {
2064 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2066 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2073 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2075 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2076 mapXrdErrorToHttpStatus();
2081 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2090 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2095 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2102 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2109 <<
" stat=" << (
char *)
iovP[0].iov_base);
2112 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2121 prot->SendSimpleResp(200, NULL,
"Accept-Ranges: bytes", NULL,
filesize,
keepalive);
2126 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2131 std::string response_headers;
2132 int response = PostProcessChecksum(response_headers);
2133 if (-1 == response) {
2136 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2137 response_headers +=
"Accept-Ranges: bytes";
2138 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2141 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2163 if (
iovP[1].iov_len > 1) {
2165 <<
" stat=" << (
char *)
iovP[1].iov_base);
2168 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2189 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2190 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2199 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2200 httpStatusText.c_str(), httpStatusText.length(),
false);
2213 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2214 httpStatusText.c_str(), httpStatusText.length(),
false);
2221 return PostProcessListing(final_);
2231 httpStatusText = rrerror.
errMsg;
2234 if (m_transfer_encoding_chunked && m_trailer_headers) {
2235 if (prot->ChunkRespHeader(0))
2238 const std::string crlf =
"\r\n";
2239 std::stringstream ss;
2240 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2242 const auto header = ss.str();
2243 if (prot->SendData(header.c_str(), header.size()))
2246 if (prot->ChunkRespFooter())
2250 if (rrerror)
return -1;
2257 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2264 if (prot->ChunkRespHeader(0))
2267 const std::string crlf =
"\r\n";
2268 std::stringstream ss;
2269 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2271 const auto header = ss.str();
2272 if (prot->SendData(header.c_str(), header.size()))
2275 if (prot->ChunkRespFooter())
2285 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2288 getReadResponse(received);
2292 rc = sendReadResponseSingleRange(received);
2294 rc = sendReadResponsesMultiRanges(received);
2315 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2316 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2324 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2327 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2346 if (m_transfer_encoding_chunked) {
2347 m_current_chunk_offset += l;
2351 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2358 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2361 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2362 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2383 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2384 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2399 <<
" stat=" << (
char *)
iovP[0].iov_base);
2402 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2414 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2417 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2418 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2430 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2431 httpStatusText.c_str(), httpStatusText.length(),
false);
2449 <<
" stat=" << (
char *)
iovP[0].iov_base);
2452 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2458 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2463 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2491 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2492 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2494 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2498 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2499 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2501 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2506 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2516 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";
2519 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2533 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2537 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2539 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2540 strncpy(entry, (
char *) startp, endp - startp);
2541 entry[endp - startp] = 0;
2548 <<
" stat=" << endp);
2551 sscanf(endp,
"%ld %lld %ld %ld",
2559 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2579 if (*p.rbegin() !=
'/') p +=
"/";
2583 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2607 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2608 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2610 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2614 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2615 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2617 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2620 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2628 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2629 if (pp) startp = pp+1;
2640 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";
2643 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2663 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2665 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2666 httpStatusText.c_str(), httpStatusText.length(),
false);
2671 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2679 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2683 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2696 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2697 httpStatusText.c_str(), httpStatusText.length(),
false);
2712 TRACE(REQ,
" XrdHttpReq request ended.");
2754 m_transfer_encoding_chunked =
false;
2755 m_current_chunk_size = -1;
2756 m_current_chunk_offset = 0;
2758 m_trailer_headers =
false;
2759 m_status_trailer =
false;
2793 void XrdHttpReq::getfhandle() {
2796 TRACEI(REQ,
"fhandle:" <<
2810 for (
int i = 0; i <
iovN; i++) {
2812 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2814 len = ntohl(l->
rlen);
2827 for (
int i = 0; i <
iovN; i++) {
2828 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2833 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2835 if (received.size() == 0) {
2851 std::string st_header;
2852 std::string fin_header;
2859 std::vector<rinfo> rvec;
2862 rvec.reserve(received.size());
2864 for(
const auto &rcv: received) {
2873 rentry.start = start;
2874 rentry.finish = finish;
2883 rentry.st_header = s;
2884 sum_len += s.size();
2887 sum_len += rcv.size;
2891 rentry.fin_header = s;
2892 sum_len += s.size();
2895 rvec.push_back(rentry);
2900 if (m_transfer_encoding_chunked && m_trailer_headers) {
2901 prot->ChunkRespHeader(sum_len);
2905 for(
const auto &rentry: rvec) {
2908 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2909 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2915 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2919 if (rentry.finish) {
2920 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2927 if (m_transfer_encoding_chunked && m_trailer_headers) {
2928 prot->ChunkRespFooter();
2934 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2937 if (received.size() == 0) {
2947 for(
const auto &rcv: received) {
2956 if (m_transfer_encoding_chunked && m_trailer_headers) {
2957 prot->ChunkRespHeader(sum);
2959 for(
const auto &rcv: received) {
2960 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2962 if (m_transfer_encoding_chunked && m_trailer_headers) {
2963 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
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)
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)
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 std::string obfuscate(const std::string &input, const std::unordered_set< std::string > &keysToObfuscate, const char keyValueDelimiter, const char listDelimiter)
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