39#include "XrdVersion.hh"
65#define MAX_RESOURCE_LEN 16384
68#define TRACELINK prot->Link
72const char *TraceID =
"Req";
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);
228int XrdHttpReq::parseHost(
char *line) {
234void 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")) {
354void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
361 for (
int i = 0; i < nitems; i++) {
372void 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();
549 if (strncmp(hname,
"file://", 7) == 0)
551 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
558 char *pp = strchr((
char *)hname,
'?');
564 int varlen = strlen(vardata);
567 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
576 sprintf(buf,
":%d", port);
584 char *newvardata =
quote(vardata);
632 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
644 char *s1 =
quote(p+1);
661 s +=
"&xrdhttptime=";
663 sprintf(buf,
"%lld", (
long long) tnow);
668 s +=
"&xrdhttpname=";
677 s +=
"&xrdhttpvorg=";
686 s +=
"&xrdhttphost=";
704 s +=
"&xrdhttprole=";
713 s +=
"&xrdhttpgrps=";
722 s +=
"&xrdhttpendorsements=";
731 s +=
"&xrdhttpcredslen=";
733 sprintf(buf,
"%d", secent->
credslen);
734 char *s1 =
quote(buf);
743 s +=
"&xrdhttpcreds=";
747 char *s1 =
quote(zerocreds);
765void XrdHttpReq::sanitizeResourcePfx() {
796void XrdHttpReq::parseResource(
char *res) {
802 char *p = strchr(res,
'?');
810 sanitizeResourcePfx();
835 sanitizeResourcePfx();
866void XrdHttpReq::mapXrdErrorToHttpStatus() {
868 httpStatusCode = 500;
869 httpStatusText =
"Unrecognized error";
875 httpStatusCode = 401; httpStatusText =
"Unauthorized";
878 httpStatusCode = 403; httpStatusText =
"Operation not permitted";
881 httpStatusCode = 404; httpStatusText =
"File not found";
884 httpStatusCode = 405; httpStatusText =
"Operation not supported";
887 httpStatusCode = 423; httpStatusText =
"Resource is a locked";
890 httpStatusCode = 409; httpStatusText =
"Resource is a directory";
894 httpStatusCode = 409; httpStatusText =
"File already exists";
899 httpStatusCode = 405;
903 httpStatusCode = 405; httpStatusText =
"Method is not allowed";
906 httpStatusCode = 504; httpStatusText =
"Gateway timeout";
915 <<
"] to status code [" << httpStatusCode <<
"]");
917 httpStatusText +=
"\n";
919 httpStatusCode = 200;
920 httpStatusText =
"OK";
944 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
945 << header2cgistrObf.c_str() <<
"'");
966 if (r < 0)
return -1;
980 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
986 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
995 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1007 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1019 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1042 if (
resource ==
"/static/css/xrdhttp.css") {
1043 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1047 if (
resource ==
"/static/icons/xrdhttp.ico") {
1048 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1068 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1078 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1101 prot->SendSimpleResp(404, NULL, NULL, (
char *) errmsg.
c_str(), 0,
false);
1114 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1126 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1131 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1139 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"prot->Bridge is NULL.", 0,
false);
1146 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1160 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1173 l = res.length() + 1;
1177 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1197 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1219 if ( readChunkList.empty() )
1227 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1238 if ( readChunkList.size() == 1 ) {
1250 offs = readChunkList[0].offset;
1251 l = readChunkList[0].size;
1259 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1262 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1271 TRACE(ALL,
" Data sizes mismatch.");
1275 TRACE(ALL,
" No more bytes to send.");
1282 TRACE(ALL,
" Requested range " << l <<
"@" << offs <<
1283 " is past the end of file (" <<
filesize <<
")");
1289 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run read request.", 0,
false);
1298 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run read request.", 0,
false);
1328 if (! XrdHttpProtocol::usingEC)
1334 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1349 if (m_transfer_encoding_chunked) {
1350 if (m_current_chunk_size == m_current_chunk_offset) {
1353 if (prot->BuffUsed() < 2)
return 1;
1354 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1355 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1358 prot->BuffConsume(2);
1359 if (m_current_chunk_size == 0) {
1363 m_transfer_encoding_chunked =
false;
1367 m_current_chunk_size = -1;
1368 m_current_chunk_offset = 0;
1370 if (!prot->BuffUsed())
return 1;
1372 if (-1 == m_current_chunk_size) {
1376 bool found_newline =
false;
1383 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1384 for (; idx < max_chunk_size_chars; idx++) {
1385 if (prot->myBuffStart[idx] ==
'\n') {
1386 found_newline =
true;
1392 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1393 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1394 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1397 if (found_newline) {
1398 char *endptr = NULL;
1399 std::string line_contents(prot->myBuffStart, idx);
1400 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1402 if (*endptr !=
';' && *endptr !=
'\r') {
1403 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1404 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1407 m_current_chunk_size = chunk_contents;
1408 m_current_chunk_offset = 0;
1409 prot->BuffConsume(idx + 1);
1410 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1417 if (m_current_chunk_size == 0) {
1426 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1427 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1428 chunk_bytes_remaining);
1433 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1434 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1435 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run write request.", 0,
false);
1440 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1450 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1456 TRACEI(REQ,
"Writing " << bytes_to_read);
1457 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1458 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run write request.", 0,
false);
1481 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1497 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);
1521 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1541 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1555 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1571 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1586 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1591 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1596 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1615 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1647 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1673 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1694 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1699 strcpy(buf2,
host.c_str());
1700 char *pos = strchr(buf2,
':');
1701 if (pos) *pos =
'\0';
1709 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1723 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1733 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1744XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1747 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1752 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1753 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1756 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1757 if (convert_to_base64) {
1758 size_t digest_length = strlen(digest_value);
1759 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1760 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1761 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1762 free(digest_binary_value);
1765 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1767 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1768 free(digest_binary_value);
1769 digest_value = digest_base64_value;
1772 digest_header =
"Digest: ";
1774 digest_header +=
"=";
1775 digest_header += digest_value;
1776 if (convert_to_base64) {free(digest_value);}
1779 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1787int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
1789 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
1790 mapXrdErrorToHttpStatus();
1795 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
1804 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
1809 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
1816 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
1823 <<
" stat=" << (
char *)
iovP[0].iov_base);
1826 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
1835 prot->SendSimpleResp(200, NULL,
"Accept-Ranges: bytes", NULL,
filesize,
keepalive);
1840 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
1845 std::string response_headers;
1846 int response = PostProcessChecksum(response_headers);
1847 if (-1 == response) {
1850 if (!response_headers.empty()) {response_headers +=
"\r\n";}
1851 response_headers +=
"Accept-Ranges: bytes";
1852 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
1855 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
1867 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1868 httpStatusText.c_str(), httpStatusText.length(),
false);
1876 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1877 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1879 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1880 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1881 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1903 "<th class=\"mode\">Mode</th>"
1904 "<th class=\"flags\">Flags</th>"
1905 "<th class=\"size\">Size</th>"
1906 "<th class=\"datetime\">Modified</th>"
1907 "<th class=\"name\">Name</th>"
1914 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1917 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1919 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1920 strncpy(entry, (
char *) startp, endp - startp);
1921 entry[endp - startp] = 0;
1928 <<
" stat=" << endp);
1931 sscanf(endp,
"%ld %lld %ld %ld",
1937 strcpy(entry, (
char *) startp);
1940 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1942 std::string p =
"<tr>"
1943 "<td class=\"mode\">";
1964 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1965 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1967 "<td class=\"name\">"
1982 p += e.
path +
"\">";
1987 p +=
"</a></td></tr>";
1996 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1997 if (pp) startp = pp+1;
2006 stringresp +=
"</table></div><br><br><hr size=1>"
2007 "<p><span id=\"requestby\">Request by ";
2091 <<
" stat=" << (
char *)
iovP[0].iov_base);
2094 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2109 TRACEI(REQ,
"Can't find the stat information for '"
2117 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2118 httpStatusText.c_str(), httpStatusText.length(),
false);
2138 if (
iovP[1].iov_len > 1) {
2140 <<
" stat=" << (
char *)
iovP[1].iov_base);
2143 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2158 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2161 std::string responseHeader;
2167 if (!responseHeader.empty()) {
2168 responseHeader +=
"\r\n";
2171 responseHeader += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2183 if (m_transfer_encoding_chunked && m_trailer_headers) {
2184 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2186 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2194 if (uranges.size() != 1)
2199 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2202 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2204 if (!responseHeader.empty()) {
2206 s += responseHeader.c_str();
2209 if (m_transfer_encoding_chunked && m_trailer_headers) {
2210 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2212 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2219 for (
auto &ur : uranges) {
2220 cnt += ur.end - ur.start + 1;
2225 (
char *)
"123456").size();
2229 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2235 if (m_transfer_encoding_chunked && m_trailer_headers) {
2236 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2238 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2245 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2246 httpStatusText.c_str(), httpStatusText.length(),
false);
2261 httpStatusText = rrerror.
errMsg;
2264 if (m_transfer_encoding_chunked && m_trailer_headers) {
2265 if (prot->ChunkRespHeader(0))
2268 const std::string crlf =
"\r\n";
2269 std::stringstream ss;
2270 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2272 const auto header = ss.str();
2273 if (prot->SendData(header.c_str(), header.size()))
2276 if (prot->ChunkRespFooter())
2280 if (rrerror)
return -1;
2287 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2294 if (prot->ChunkRespHeader(0))
2297 const std::string crlf =
"\r\n";
2298 std::stringstream ss;
2299 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2301 const auto header = ss.str();
2302 if (prot->SendData(header.c_str(), header.size()))
2305 if (prot->ChunkRespFooter())
2315 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2318 getReadResponse(received);
2322 rc = sendReadResponseSingleRange(received);
2324 rc = sendReadResponsesMultiRanges(received);
2350 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2351 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2359 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2362 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2381 if (m_transfer_encoding_chunked) {
2382 m_current_chunk_offset += l;
2386 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2393 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2396 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2397 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2418 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2419 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2434 <<
" stat=" << (
char *)
iovP[0].iov_base);
2437 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2449 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2452 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2453 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2465 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2466 httpStatusText.c_str(), httpStatusText.length(),
false);
2484 <<
" stat=" << (
char *)
iovP[0].iov_base);
2487 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2493 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2498 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2526 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2527 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2529 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2533 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2534 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2536 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2541 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2551 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";
2554 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2568 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2572 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2574 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2575 strncpy(entry, (
char *) startp, endp - startp);
2576 entry[endp - startp] = 0;
2583 <<
" stat=" << endp);
2586 sscanf(endp,
"%ld %lld %ld %ld",
2594 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2614 if (*p.rbegin() !=
'/') p +=
"/";
2618 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2642 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2643 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2645 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2649 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2650 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2652 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2655 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2663 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2664 if (pp) startp = pp+1;
2675 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";
2678 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2698 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2700 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2701 httpStatusText.c_str(), httpStatusText.length(),
false);
2706 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2714 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2718 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2731 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2732 httpStatusText.c_str(), httpStatusText.length(),
false);
2747 TRACE(REQ,
" XrdHttpReq request ended.");
2789 m_transfer_encoding_chunked =
false;
2790 m_current_chunk_size = -1;
2791 m_current_chunk_offset = 0;
2793 m_trailer_headers =
false;
2794 m_status_trailer =
false;
2828void XrdHttpReq::getfhandle() {
2831 TRACEI(REQ,
"fhandle:" <<
2845 for (
int i = 0; i <
iovN; i++) {
2847 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2849 len = ntohl(l->
rlen);
2862 for (
int i = 0; i <
iovN; i++) {
2863 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2868int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2870 if (received.size() == 0) {
2886 std::string st_header;
2887 std::string fin_header;
2894 std::vector<rinfo> rvec;
2897 rvec.reserve(received.size());
2899 for(
const auto &rcv: received) {
2908 rentry.start = start;
2909 rentry.finish = finish;
2918 rentry.st_header = s;
2919 sum_len += s.size();
2922 sum_len += rcv.size;
2926 rentry.fin_header = s;
2927 sum_len += s.size();
2930 rvec.push_back(rentry);
2935 if (m_transfer_encoding_chunked && m_trailer_headers) {
2936 prot->ChunkRespHeader(sum_len);
2940 for(
const auto &rentry: rvec) {
2943 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2944 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2950 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2954 if (rentry.finish) {
2955 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2962 if (m_transfer_encoding_chunked && m_trailer_headers) {
2963 prot->ChunkRespFooter();
2969int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2972 if (received.size() == 0) {
2982 for(
const auto &rcv: received) {
2991 if (m_transfer_encoding_chunked && m_trailer_headers) {
2992 prot->ChunkRespHeader(sum);
2994 for(
const auto &rcv: received) {
2995 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2997 if (m_transfer_encoding_chunked && m_trailer_headers) {
2998 prot->ChunkRespFooter();
struct ClientCloseRequest close
struct ClientSetRequest set
struct ClientMkdirRequest mkdir
struct ClientDirlistRequest dirlist
struct ClientReadVRequest readv
struct ClientOpenRequest open
struct ClientRequestHdr header
struct ClientRmRequest rm
struct ClientReadRequest read
struct ClientMvRequest mv
struct ClientRmdirRequest rmdir
struct ClientStatRequest stat
struct ClientWriteRequest write
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
std::string ISOdatetime(time_t t)
void trim(std::string &str)
Main request/response class, handling the logical status of the communication.
void trim(std::string &str)
Static resources, here for performance and ease of setup.
int parseURL(char *url, char *host, int &port, char **path)
void Tobase64(const unsigned char *input, int length, char *out)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
char * unquote(char *str)
char * quote(const char *str)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
Utility functions for XrdHTTP.
std::vector< XrdOucIOVec2 > XrdHttpIOList
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)
void assign(const char *s, int j, int k=-1)
int erasefromstart(int sz=0)
int erasefromend(int sz=0)
int erase(int start=0, int size=0)
int find(const char c, int start=0, bool forward=1)
const char * c_str() const
static 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