39#include "XrdVersion.hh"
66#define MAX_RESOURCE_LEN 16384
69#define TRACELINK prot->Link
73const char *TraceID =
"Req";
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);
229int XrdHttpReq::parseHost(
char *line) {
235void XrdHttpReq::parseScitag(
const std::string & val) {
239 std::string scitagS = val;
242 if(scitagS[0] !=
'-') {
244 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
263 if (!line)
return -1;
266 char *p = strchr((
char *) line, (
int)
' ');
290 char *val = line + pos + 1;
297 p = strchr((
char *) val, (
int)
' ');
311 if (!strcmp(key,
"GET")) {
313 }
else if (!strcmp(key,
"HEAD")) {
315 }
else if (!strcmp(key,
"PUT")) {
317 }
else if (!strcmp(key,
"POST")) {
319 }
else if (!strcmp(key,
"PATCH")) {
321 }
else if (!strcmp(key,
"OPTIONS")) {
323 }
else if (!strcmp(key,
"DELETE")) {
325 }
else if (!strcmp(key,
"PROPFIND")) {
328 }
else if (!strcmp(key,
"MKCOL")) {
331 }
else if (!strcmp(key,
"MOVE")) {
341 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
355void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
362 for (
int i = 0; i < nitems; i++) {
373void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
380 for (
int i = 0; i < nitems; i++) {
398 for (
const auto &c: cl) {
401 memcpy(&ra.fhandle, this->fhandle, 4);
403 ra.offset = c.offset;
417 clientMarshallReadAheadList(j);
426 std::ostringstream s;
428 s <<
"\r\n--" << token <<
"\r\n";
429 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
430 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
436 std::ostringstream s;
438 s <<
"\r\n--" << token <<
"--\r\n";
451 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
457 this->
final = final_;
459 if (PostProcessHTTPReq(final_))
reset();
473 int rc = info.
Send(0, 0, 0, 0);
474 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
491 TRACE(REQ,
" XrdHttpReq::Done");
497 int r = PostProcessHTTPReq(
true);
500 if (r < 0)
return false;
511 TRACE(REQ,
" XrdHttpReq::Error");
522 if (PostProcessHTTPReq())
reset();
550 if (strncmp(hname,
"file://", 7) == 0)
552 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
559 char *pp = strchr((
char *)hname,
'?');
565 int varlen = strlen(vardata);
568 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
577 sprintf(buf,
":%d", port);
585 char *newvardata =
quote(vardata);
623 return ret_keepalive;
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);
767void XrdHttpReq::sanitizeResourcePfx() {
798void XrdHttpReq::parseResource(
char *res) {
804 char *p = strchr(res,
'?');
812 sanitizeResourcePfx();
837 sanitizeResourcePfx();
868void 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";
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);
1045 if (
resource ==
"/static/css/xrdhttp.css") {
1046 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1050 if (
resource ==
"/static/icons/xrdhttp.ico") {
1051 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1071 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1081 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1104 prot->SendSimpleResp(404, NULL, NULL, (
char *) errmsg.
c_str(), 0,
false);
1117 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1129 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1134 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1142 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"prot->Bridge is NULL.", 0,
false);
1149 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1163 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1176 l = res.length() + 1;
1180 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1200 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1222 if ( readChunkList.empty() )
1230 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1241 if ( readChunkList.size() == 1 ) {
1253 offs = readChunkList[0].offset;
1254 l = readChunkList[0].size;
1262 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1265 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1274 TRACE(ALL,
" Data sizes mismatch.");
1278 TRACE(ALL,
" No more bytes to send.");
1285 TRACE(ALL,
" Requested range " << l <<
"@" << offs <<
1286 " is past the end of file (" <<
filesize <<
")");
1292 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run read request.", 0,
false);
1301 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run read request.", 0,
false);
1331 if (! XrdHttpProtocol::usingEC)
1337 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1352 if (m_transfer_encoding_chunked) {
1353 if (m_current_chunk_size == m_current_chunk_offset) {
1356 if (prot->BuffUsed() < 2)
return 1;
1357 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1358 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1361 prot->BuffConsume(2);
1362 if (m_current_chunk_size == 0) {
1366 m_transfer_encoding_chunked =
false;
1370 m_current_chunk_size = -1;
1371 m_current_chunk_offset = 0;
1373 if (!prot->BuffUsed())
return 1;
1375 if (-1 == m_current_chunk_size) {
1379 bool found_newline =
false;
1386 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1387 for (; idx < max_chunk_size_chars; idx++) {
1388 if (prot->myBuffStart[idx] ==
'\n') {
1389 found_newline =
true;
1395 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1396 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1397 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1400 if (found_newline) {
1401 char *endptr = NULL;
1402 std::string line_contents(prot->myBuffStart, idx);
1403 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1405 if (*endptr !=
';' && *endptr !=
'\r') {
1406 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1407 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1410 m_current_chunk_size = chunk_contents;
1411 m_current_chunk_offset = 0;
1412 prot->BuffConsume(idx + 1);
1413 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1420 if (m_current_chunk_size == 0) {
1429 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1430 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1431 chunk_bytes_remaining);
1436 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1437 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1438 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run write request.", 0,
false);
1443 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1453 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1459 TRACEI(REQ,
"Writing " << bytes_to_read);
1460 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1461 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run write request.", 0,
false);
1484 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1500 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);
1503 return ret_keepalive ? 1 : -1;
1525 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1545 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1559 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1575 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1590 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1595 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1600 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1619 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1651 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1677 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1698 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1703 strcpy(buf2,
host.c_str());
1704 char *pos = strchr(buf2,
':');
1705 if (pos) *pos =
'\0';
1713 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1727 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1737 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1748XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1751 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1756 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1757 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1760 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1761 if (convert_to_base64) {
1762 size_t digest_length = strlen(digest_value);
1763 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1764 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1765 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1766 free(digest_binary_value);
1769 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1771 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1772 free(digest_binary_value);
1773 digest_value = digest_base64_value;
1776 digest_header =
"Digest: ";
1778 digest_header +=
"=";
1779 digest_header += digest_value;
1780 if (convert_to_base64) {free(digest_value);}
1783 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1791int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
1793 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
1794 mapXrdErrorToHttpStatus();
1799 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
1808 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
1813 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
1820 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
1827 <<
" stat=" << (
char *)
iovP[0].iov_base);
1830 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
1839 prot->SendSimpleResp(200, NULL,
"Accept-Ranges: bytes", NULL,
filesize,
keepalive);
1844 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
1847 return ret_keepalive ? 1 : -1;
1850 std::string response_headers;
1851 int response = PostProcessChecksum(response_headers);
1852 if (-1 == response) {
1855 if (!response_headers.empty()) {response_headers +=
"\r\n";}
1856 response_headers +=
"Accept-Ranges: bytes";
1857 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
1860 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
1872 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1873 httpStatusText.c_str(), httpStatusText.length(),
false);
1881 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1882 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1884 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1885 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1886 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1908 "<th class=\"mode\">Mode</th>"
1909 "<th class=\"flags\">Flags</th>"
1910 "<th class=\"size\">Size</th>"
1911 "<th class=\"datetime\">Modified</th>"
1912 "<th class=\"name\">Name</th>"
1919 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1922 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1924 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1925 strncpy(entry, (
char *) startp, endp - startp);
1926 entry[endp - startp] = 0;
1933 <<
" stat=" << endp);
1936 sscanf(endp,
"%ld %lld %ld %ld",
1942 strcpy(entry, (
char *) startp);
1945 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1947 std::string p =
"<tr>"
1948 "<td class=\"mode\">";
1969 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1970 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1972 "<td class=\"name\">"
1987 p += e.
path +
"\">";
1992 p +=
"</a></td></tr>";
2001 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2002 if (pp) startp = pp+1;
2011 stringresp +=
"</table></div><br><br><hr size=1>"
2012 "<p><span id=\"requestby\">Request by ";
2096 <<
" stat=" << (
char *)
iovP[0].iov_base);
2099 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2114 TRACEI(REQ,
"Can't find the stat information for '"
2122 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2123 httpStatusText.c_str(), httpStatusText.length(),
false);
2143 if (
iovP[1].iov_len > 1) {
2145 <<
" stat=" << (
char *)
iovP[1].iov_base);
2148 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2163 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2166 std::string responseHeader;
2172 if (!responseHeader.empty()) {
2173 responseHeader +=
"\r\n";
2176 responseHeader += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2188 if (m_transfer_encoding_chunked && m_trailer_headers) {
2189 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2191 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2199 if (uranges.size() != 1)
2204 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2207 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2209 if (!responseHeader.empty()) {
2211 s += responseHeader.
c_str();
2214 if (m_transfer_encoding_chunked && m_trailer_headers) {
2215 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2217 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2224 for (
auto &ur : uranges) {
2225 cnt += ur.end - ur.start + 1;
2230 (
char *)
"123456").size();
2234 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2240 if (m_transfer_encoding_chunked && m_trailer_headers) {
2241 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2243 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2250 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2251 httpStatusText.c_str(), httpStatusText.length(),
false);
2266 httpStatusText = rrerror.
errMsg;
2269 if (m_transfer_encoding_chunked && m_trailer_headers) {
2270 if (prot->ChunkRespHeader(0))
2273 const std::string crlf =
"\r\n";
2274 std::stringstream ss;
2275 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2277 const auto header = ss.str();
2278 if (prot->SendData(header.c_str(), header.size()))
2281 if (prot->ChunkRespFooter())
2285 if (rrerror)
return -1;
2292 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2299 if (prot->ChunkRespHeader(0))
2302 const std::string crlf =
"\r\n";
2303 std::stringstream ss;
2304 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2306 const auto header = ss.str();
2307 if (prot->SendData(header.c_str(), header.size()))
2310 if (prot->ChunkRespFooter())
2320 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2323 getReadResponse(received);
2327 rc = sendReadResponseSingleRange(received);
2329 rc = sendReadResponsesMultiRanges(received);
2355 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2356 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2364 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2367 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2386 if (m_transfer_encoding_chunked) {
2387 m_current_chunk_offset += l;
2391 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2398 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2401 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2402 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2423 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2424 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2439 <<
" stat=" << (
char *)
iovP[0].iov_base);
2442 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2454 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2457 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2458 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2470 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2471 httpStatusText.c_str(), httpStatusText.length(),
false);
2489 <<
" stat=" << (
char *)
iovP[0].iov_base);
2492 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2498 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2503 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2531 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2532 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2534 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2538 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2539 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2541 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2546 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2556 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";
2559 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2573 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2577 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2579 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2580 strncpy(entry, (
char *) startp, endp - startp);
2581 entry[endp - startp] = 0;
2588 <<
" stat=" << endp);
2591 sscanf(endp,
"%ld %lld %ld %ld",
2599 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2619 if (*p.rbegin() !=
'/') p +=
"/";
2623 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2647 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2648 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2650 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2654 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2655 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2657 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2660 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2668 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2669 if (pp) startp = pp+1;
2680 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";
2683 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2703 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2705 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2706 httpStatusText.c_str(), httpStatusText.length(),
false);
2711 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2719 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2723 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2736 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2737 httpStatusText.c_str(), httpStatusText.length(),
false);
2752 TRACE(REQ,
" XrdHttpReq request ended.");
2794 m_transfer_encoding_chunked =
false;
2795 m_current_chunk_size = -1;
2796 m_current_chunk_offset = 0;
2798 m_trailer_headers =
false;
2799 m_status_trailer =
false;
2833void XrdHttpReq::getfhandle() {
2836 TRACEI(REQ,
"fhandle:" <<
2850 for (
int i = 0; i <
iovN; i++) {
2852 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2854 len = ntohl(l->
rlen);
2867 for (
int i = 0; i <
iovN; i++) {
2868 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2873int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2875 if (received.size() == 0) {
2891 std::string st_header;
2892 std::string fin_header;
2899 std::vector<rinfo> rvec;
2902 rvec.reserve(received.size());
2904 for(
const auto &rcv: received) {
2913 rentry.start = start;
2914 rentry.finish = finish;
2923 rentry.st_header = s;
2924 sum_len += s.size();
2927 sum_len += rcv.size;
2931 rentry.fin_header = s;
2932 sum_len += s.size();
2935 rvec.push_back(rentry);
2940 if (m_transfer_encoding_chunked && m_trailer_headers) {
2941 prot->ChunkRespHeader(sum_len);
2945 for(
const auto &rentry: rvec) {
2948 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2949 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2955 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2959 if (rentry.finish) {
2960 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2967 if (m_transfer_encoding_chunked && m_trailer_headers) {
2968 prot->ChunkRespFooter();
2974int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2977 if (received.size() == 0) {
2987 for(
const auto &rcv: received) {
2996 if (m_transfer_encoding_chunked && m_trailer_headers) {
2997 prot->ChunkRespHeader(sum);
2999 for(
const auto &rcv: received) {
3000 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3002 if (m_transfer_encoding_chunked && m_trailer_headers) {
3003 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
std::string obfuscateAuth(const std::string &input)
XrdHttpChecksumRawPtr getChecksumToRun(const std::string &userDigest) const
bool needsBase64Padding() const
std::string getXRootDConfigDigestName() const
std::string getHttpName() const
virtual int ProcessReq(XrdHttpExtReq &)=0
static char * secretkey
The key used to calculate the url hashes.
static kXR_int32 myRole
Our role.
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static XrdHttpChecksumHandler cksumHandler
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
XrdLink * Link
The link we are bound to.
int doStat(char *fname)
Perform a Stat request.
static bool isdesthttps
True if the redirections must be towards https targets.
static char * listredir
Url to redirect to in the case a listing is requested.
static bool listdeny
If true, any form of listing is denied.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
void reset()
resets this handler
const XrdHttpIOList & NextReadList()
return XrdHttpIOList for sending to read or readv
void ParseContentRange(const char *const line)
parse the line after a "Range: " http request header
int SetFilesize(const off_t sz)
sets the filesize, used during resolving and issuing range requests
void NotifyError()
Force handler to enter error state.
bool isFullFile()
indicates when there were no valid Range head ranges supplied
std::vector< UserRange > UserRangeList
int NotifyReadResult(const ssize_t ret, const UserRange **const urp, bool &start, bool &allend)
Advance internal counters concerning received bytes.
const Error & getError() const
return the Error object
bool isSingleRange()
indicates a single range (implied whole file, or single range) or empty file
const UserRangeList & ListResolvedRanges()
return resolved (i.e. obsolute start and end) byte ranges desired
int reqstate
State machine to talk to the bridge.
int ReqReadV(const XrdHttpIOList &cl)
Prepare the buffers for sending a readv request.
int parseBody(char *body, long long len)
Parse the body of a request, assuming that it's XML and that it's entirely in memory.
std::vector< readahead_list > ralist
std::string destination
The destination field specified in the req.
XrdOucString resource
The resource specified by the request, stripped of opaque data.
bool headerok
Tells if we have finished reading the header.
std::string m_digest_header
The computed digest for the HTTP response header.
std::string stringresp
If we want to give a string as a response, we compose it here.
XResponseType xrdresp
The last response data we got.
ReqType request
The request we got.
long long writtenbytes
In a long write, we track where we have arrived.
XrdOucEnv * opaque
The opaque data, after parsing.
const struct iovec * iovP
The latest data chunks got from the xrd layer. These are valid only inside the callbacks!
std::string m_req_digest
The requested digest type.
XrdOucString resourceplusopaque
The resource specified by the request, including all the opaque data.
virtual bool Data(XrdXrootd::Bridge::Context &info, const struct iovec *iovP, int iovN, int iovL, bool final)
std::string hdr2cgistr
Additional opaque info that may come from the hdr2cgi directive.
virtual bool Done(XrdXrootd::Bridge::Context &info)
the result context
std::string host
The host field specified in the req.
int parseFirstLine(char *line, int len)
Parse the first line of the header.
int parseLine(char *line, int len)
Parse the header.
std::string buildPartialHdrEnd(char *token)
Build the closing part for a multipart response.
XrdHttpChecksumHandler::XrdHttpChecksumRawPtr m_req_cksum
The checksum that was ran for this request.
bool m_appended_hdr2cgistr
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
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 insert(const int i, int start=-1)
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 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