39#include "XrdVersion.hh"
67#define MAX_RESOURCE_LEN 16384
70#define TRACELINK prot->Link
74const char *TraceID =
"Req";
87 memset(&t1, 0,
sizeof (t1));
90 strftime(datebuf, 127,
"%a, %d %b %Y %H:%M:%S GMT", &t1);
91 return (std::string) datebuf;
123 if (!line)
return -1;
126 char *p = strchr((
char *) line, (
int)
':');
142 char *val = line + pos + 1;
145 while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
149 std::string ss = val;
150 if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) !=
"\r\n") {
163 if (!strcasecmp(key,
"connection")) {
165 if (!strcasecmp(val,
"Keep-Alive\r\n")) {
167 }
else if (!strcasecmp(val,
"close\r\n")) {
171 }
else if (!strcasecmp(key,
"host")) {
173 }
else if (!strcasecmp(key,
"range")) {
178 }
else if (!strcasecmp(key,
"content-length")) {
181 }
else if (!strcasecmp(key,
"destination")) {
184 }
else if (!strcasecmp(key,
"want-digest")) {
189 }
else if (!strcasecmp(key,
"depth")) {
191 if (strcmp(val,
"infinity"))
194 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
196 }
else if (!strcasecmp(key,
"te") && strstr(val,
"trailers")) {
197 m_trailer_headers =
true;
198 }
else if (!strcasecmp(key,
"transfer-encoding") && strstr(val,
"chunked")) {
199 m_transfer_encoding_chunked =
true;
200 }
else if (!strcasecmp(key,
"x-transfer-status") && strstr(val,
"true")) {
201 m_transfer_encoding_chunked =
true;
202 m_status_trailer =
true;
203 }
else if (!strcasecmp(key,
"scitag")) {
204 if(prot->pmarkHandle !=
nullptr) {
207 }
else if (!strcasecmp(key,
"user-agent")) {
210 }
else if (!strcasecmp(key,
"origin")) {
215 auto it = std::find_if(prot->hdr2cgimap.begin(), prot->hdr2cgimap.end(),[key](
const auto & item) {
216 return !strcasecmp(key,item.first.c_str());
218 if (it != prot->hdr2cgimap.end() && (
opaque ? (0 ==
opaque->Get(it->second.c_str())) :
true)) {
220 s.assign(val, line+len-val);
233int XrdHttpReq::parseHost(
char *line) {
239void XrdHttpReq::parseScitag(
const std::string & val) {
243 std::string scitagS = val;
246 if(scitagS[0] !=
'-') {
248 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
272 if (!line)
return -1;
275 char *p = strchr((
char *) line, (
int)
' ');
299 char *val = line + pos + 1;
306 p = strchr((
char *) val, (
int)
' ');
320 if (!strcmp(key,
"GET")) {
322 }
else if (!strcmp(key,
"HEAD")) {
324 }
else if (!strcmp(key,
"PUT")) {
326 }
else if (!strcmp(key,
"POST")) {
328 }
else if (!strcmp(key,
"PATCH")) {
330 }
else if (!strcmp(key,
"OPTIONS")) {
332 }
else if (!strcmp(key,
"DELETE")) {
334 }
else if (!strcmp(key,
"PROPFIND")) {
337 }
else if (!strcmp(key,
"MKCOL")) {
340 }
else if (!strcmp(key,
"MOVE")) {
350 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
364void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
371 for (
int i = 0; i < nitems; i++) {
382void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
389 for (
int i = 0; i < nitems; i++) {
407 for (
const auto &c: cl) {
410 memcpy(&ra.fhandle, this->fhandle, 4);
412 ra.offset = c.offset;
426 clientMarshallReadAheadList(j);
435 std::ostringstream s;
437 s <<
"\r\n--" << token <<
"\r\n";
438 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
439 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
445 std::ostringstream s;
447 s <<
"\r\n--" << token <<
"--\r\n";
460 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
466 this->
final = final_;
468 if (PostProcessHTTPReq(final_))
reset();
482 int rc = info.
Send(0, 0, 0, 0);
483 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
500 TRACE(REQ,
" XrdHttpReq::Done");
506 int r = PostProcessHTTPReq(
true);
509 if (r < 0)
return false;
520 TRACE(REQ,
" XrdHttpReq::Error");
531 auto rc = PostProcessHTTPReq();
556 if (prot->isdesthttps)
564 if (strncmp(hname,
"file://", 7) == 0)
566 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
573 char *pp = strchr((
char *)hname,
'?');
579 int varlen = strlen(vardata);
582 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
591 sprintf(buf,
":%d", port);
610 if (!prot->isdesthttps && prot->ishttps) {
635 return ret_keepalive;
646 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
665 s +=
"&xrdhttptime=";
667 sprintf(buf,
"%lld", (
long long) tnow);
672 s +=
"&xrdhttpname=";
678 s +=
"&xrdhttpvorg=";
683 s +=
"&xrdhttphost=";
693 s +=
"&xrdhttprole=";
698 s +=
"&xrdhttpgrps=";
703 s +=
"&xrdhttpendorsements=";
708 s +=
"&xrdhttpcredslen=";
710 sprintf(buf,
"%d", secent->
credslen);
716 s +=
"&xrdhttpcreds=";
730void XrdHttpReq::sanitizeResourcePfx() {
739 if (
resource.beginswith(
"http://")) {
761void XrdHttpReq::parseResource(
char *res) {
767 char *p = strchr(res,
'?');
775 sanitizeResourcePfx();
800 sanitizeResourcePfx();
816 opaque =
new XrdOucEnv(decoded.c_str());
822void XrdHttpReq::generateWebdavErrMsg() {
828 httpStatusCode = 200;
829 httpErrorBody =
"OK";
835 httpErrorBody =
etext +
"\n";
847 int query_param_status = 0;
851 if (query_param_status == 0) {
855 query_param_status = 1;
856 auto length_str = std::to_string(
length);
862 opaque->Put(
"oss.asize", length_str.c_str());
868 if (query_param_status == 0) {
880 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
881 << header2cgistrObf.c_str() <<
"'");
896 if (r < 0)
return -1;
910 generateWebdavErrMsg();
911 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
920 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
945 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
955 if (
resource.beginswith(
"/static/")) {
966 if (prot->embeddedstatic) {
969 if (
resource ==
"/static/css/xrdhttp.css") {
970 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
974 if (
resource ==
"/static/icons/xrdhttp.ico") {
975 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
984 if (prot->staticredir) {
987 s.
append(prot->staticredir);
995 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1002 if (prot->staticpreload) {
1005 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1032 xrdreq.open.dlen = htonl(l);
1037 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1055 prot->SendSimpleResp(
HTTP_METHOD_NOT_ALLOWED, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1067 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1072 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1082 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1083 generateWebdavErrMsg();
1084 return sendFooterError(
"Could not run close request on the bridge");
1093 if (prot->listdeny) {
1094 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1098 if (prot->listredir) {
1100 s.
append(prot->listredir);
1108 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1119 l = res.length() + 1;
1120 xrdreq.dirlist.dlen = htonl(l);
1122 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) res.c_str(), l)) {
1123 generateWebdavErrMsg();
1124 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1125 sendFooterError(
"Could not run listing request on the bridge");
1138 auto retval = ReturnGetHeaders();
1158 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1159 TRACEI(REQ,
" Failed to run close request on the bridge.");
1173 if ( readChunkList.size() == 1 ) {
1185 offs = readChunkList[0].offset;
1186 l = readChunkList[0].size;
1188 xrdreq.read.offset = htonll(offs);
1189 xrdreq.read.rlen = htonl(l);
1194 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1197 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1206 TRACE(ALL,
" Data sizes mismatch.");
1210 TRACE(ALL,
" No more bytes to send.");
1217 httpStatusCode = 416;
1218 httpErrorBody =
"Range Not Satisfiable";
1219 std::stringstream ss;
1220 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1221 return sendFooterError(ss.str());
1224 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1225 generateWebdavErrMsg();
1226 return sendFooterError(
"Could not run read request on the bridge");
1234 generateWebdavErrMsg();
1235 return sendFooterError(
"Could not run ReadV request on the bridge");
1262 xrdreq.open.dlen = htonl(l);
1264 if (! XrdHttpProtocol::usingEC)
1270 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1285 if (m_transfer_encoding_chunked) {
1286 if (m_current_chunk_size == m_current_chunk_offset) {
1289 if (prot->BuffUsed() < 2)
return 1;
1290 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1291 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1294 prot->BuffConsume(2);
1295 if (m_current_chunk_size == 0) {
1299 m_transfer_encoding_chunked =
false;
1303 m_current_chunk_size = -1;
1304 m_current_chunk_offset = 0;
1306 if (!prot->BuffUsed())
return 1;
1308 if (-1 == m_current_chunk_size) {
1312 bool found_newline =
false;
1319 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1320 for (; idx < max_chunk_size_chars; idx++) {
1321 if (prot->myBuffStart[idx] ==
'\n') {
1322 found_newline =
true;
1328 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1329 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1330 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1333 if (found_newline) {
1334 char *endptr = NULL;
1335 std::string line_contents(prot->myBuffStart, idx);
1336 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1338 if (*endptr !=
';' && *endptr !=
'\r') {
1339 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1340 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1343 m_current_chunk_size = chunk_contents;
1344 m_current_chunk_offset = 0;
1345 prot->BuffConsume(idx + 1);
1346 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1353 if (m_current_chunk_size == 0) {
1362 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1363 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1364 chunk_bytes_remaining);
1367 xrdreq.write.dlen = htonl(bytes_to_write);
1369 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1370 if (!prot->Bridge->Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1371 generateWebdavErrMsg();
1372 return sendFooterError(
"Could not run write request on the bridge");
1376 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1386 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1390 xrdreq.write.dlen = htonl(bytes_to_read);
1392 TRACEI(REQ,
"Writing " << bytes_to_read);
1393 if (!prot->Bridge->Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1394 generateWebdavErrMsg();
1395 return sendFooterError(
"Could not run write request on the bridge");
1416 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1417 generateWebdavErrMsg();
1418 return sendFooterError(
"Could not run close request on the bridge");
1433 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);
1436 return ret_keepalive ? 1 : -1;
1455 xrdreq.stat.dlen = htonl(l);
1458 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1475 xrdreq.rmdir.dlen = htonl(l);
1477 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1478 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1489 xrdreq.rm.dlen = htonl(l);
1491 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1492 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1508 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1523 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1528 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1533 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1549 xrdreq.stat.dlen = htonl(l);
1552 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1581 xrdreq.dirlist.dlen = htonl(l);
1583 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1584 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1607 xrdreq.mkdir.dlen = htonl(l);
1609 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1610 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1621 skip = (skip == std::string::npos) ? 0 : skip + 3;
1625 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1636 size_t path_pos =
destination.find(
'/', skip + 1);
1638 if (path_pos == std::string::npos) {
1639 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Cannot determine destination path", 0,
false);
1646 l = mv_args.length() + 1;
1652 xrdreq.mv.dlen = htonl(l);
1654 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) mv_args.c_str(), l)) {
1655 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1664 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1675XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1678 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1683 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1684 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1686 bool convert_to_base64 =
m_req_cksum->needsBase64Padding();
1687 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1688 if (convert_to_base64) {
1689 size_t digest_length = strlen(digest_value);
1690 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1691 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1692 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1693 free(digest_binary_value);
1696 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1698 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1699 free(digest_binary_value);
1700 digest_value = digest_base64_value;
1703 digest_header =
"Digest: ";
1705 digest_header +=
"=";
1706 digest_header += digest_value;
1707 if (convert_to_base64) {free(digest_value);}
1710 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1716XrdHttpReq::PostProcessListing(
bool final_) {
1719 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1720 httpErrorBody.c_str(), httpErrorBody.length(),
false);
1726 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1727 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1729 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1730 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1731 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1752 "<th class=\"mode\">Mode</th>"
1753 "<th class=\"flags\">Flags</th>"
1754 "<th class=\"size\">Size</th>"
1755 "<th class=\"datetime\">Modified</th>"
1756 "<th class=\"name\">Name</th>"
1762 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1765 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
1767 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1768 strncpy(entry, (
char *) startp, endp - startp);
1769 entry[endp - startp] = 0;
1776 <<
" stat=" << endp);
1779 sscanf(endp,
"%ld %lld %ld %ld",
1785 strcpy(entry, (
char *) startp);
1787 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1789 std::string p =
"<tr>"
1790 "<td class=\"mode\">";
1811 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1812 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1814 "<td class=\"name\">"
1822 if (!p.empty() && p[p.size() - 1] !=
'/')
1827 std::unique_ptr<char,
decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1833 p +=
"</a></td></tr>";
1839 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1840 if (pp) startp = pp+1;
1849 stringresp +=
"</table></div><br><br><hr size=1>"
1850 "<p><span id=\"requestby\">Request by ";
1852 if (prot->SecEntity.name)
1857 if (prot->SecEntity.vorg ||
1858 prot->SecEntity.name ||
1859 prot->SecEntity.moninfo ||
1860 prot->SecEntity.role)
1863 if (prot->SecEntity.vorg) {
1868 if (prot->SecEntity.moninfo) {
1872 if (prot->SecEntity.name) {
1877 if (prot->SecEntity.role) {
1880 if (prot->SecEntity.endorsements) {
1887 if (prot->SecEntity.vorg ||
1888 prot->SecEntity.moninfo ||
1889 prot->SecEntity.role)
1892 if (prot->SecEntity.host) {
1912XrdHttpReq::ReturnGetHeaders() {
1913 std::string responseHeader;
1918 if (!responseHeader.empty()) {
1919 responseHeader +=
"\r\n";
1921 addAgeHeader(responseHeader);
1933 if (m_transfer_encoding_chunked && m_trailer_headers) {
1935 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
1937 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
1945 if (uranges.size() != 1)
1950 const off_t cnt = uranges[0].end - uranges[0].start + 1;
1952 std::string header =
"Content-Range: bytes ";
1953 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
1955 if (!responseHeader.empty()) {
1957 header += responseHeader.c_str();
1960 if (m_transfer_encoding_chunked && m_trailer_headers) {
1962 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
1964 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
1971 for (
auto &ur : uranges) {
1972 cnt += ur.end - ur.start + 1;
1977 (
char *)
"123456").size();
1981 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
1987 if (!header.empty()) {
1990 addAgeHeader(header);
1993 if (m_transfer_encoding_chunked && m_trailer_headers) {
1995 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
1997 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2003 if (m_status_trailer) {
2004 if (header.empty()) {
2005 header +=
"Trailer: X-Transfer-Status";
2007 header +=
"\r\nTrailer: X-Transfer-Status";
2014int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2016 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2017 generateWebdavErrMsg();
2022 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2031 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2036 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2043 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2047 std::string response_headers;
2051 <<
" stat=" << (
char *)
iovP[0].iov_base);
2054 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2064 addAgeHeader(response_headers);
2065 response_headers +=
"\r\n";
2067 response_headers +=
"Accept-Ranges: bytes";
2068 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2073 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2076 return ret_keepalive ? 1 : -1;
2079 std::string response_headers;
2080 int response = PostProcessChecksum(response_headers);
2081 if (-1 == response) {
2084 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2086 addAgeHeader(response_headers);
2087 response_headers +=
"\r\n";
2089 response_headers +=
"Accept-Ranges: bytes";
2090 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2093 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2115 if (
iovP[1].iov_len > 1) {
2117 <<
" stat=" << (
char *)
iovP[1].iov_base);
2120 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2141 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2142 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2151 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2152 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2165 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2166 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2173 return PostProcessListing(final_);
2183 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2187 const XrdHttpReadRangeHandler::Error &rrerror =
readRangeHandler.getError();
2190 httpErrorBody = rrerror.
errMsg;
2193 if (m_transfer_encoding_chunked && m_trailer_headers) {
2194 if (prot->ChunkRespHeader(0))
2197 const std::string crlf =
"\r\n";
2198 std::stringstream ss;
2199 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpErrorBody << crlf;
2201 const auto header = ss.str();
2202 if (prot->SendData(header.c_str(), header.size()))
2205 if (prot->ChunkRespFooter())
2209 if (rrerror)
return -1;
2216 auto rc = sendFooterError(
"");
2225 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2228 getReadResponse(received);
2232 rc = sendReadResponseSingleRange(received);
2234 rc = sendReadResponsesMultiRanges(received);
2253 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2261 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2264 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2275 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2280 int l = ntohl(
xrdreq.write.dlen);
2283 prot->BuffConsume(ntohl(
xrdreq.write.dlen));
2287 if (m_transfer_encoding_chunked) {
2288 m_current_chunk_offset += l;
2292 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2299 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2302 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2321 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2322 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2337 <<
" stat=" << (
char *)
iovP[0].iov_base);
2340 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2352 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2355 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2356 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2368 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2369 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2387 <<
" stat=" << (
char *)
iovP[0].iov_base);
2390 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2396 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2401 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2429 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2430 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2432 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2436 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2437 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2439 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2444 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2454 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";
2457 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2471 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2475 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
2477 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2478 strncpy(entry, (
char *) startp, endp - startp);
2479 entry[endp - startp] = 0;
2486 <<
" stat=" << endp);
2489 sscanf(endp,
"%ld %lld %ld %ld",
2497 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2517 if (*p.rbegin() !=
'/') p +=
"/";
2521 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2545 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2546 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2548 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2552 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2553 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2555 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2558 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2566 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2567 if (pp) startp = pp+1;
2578 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";
2581 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2601 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2603 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2604 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2609 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2617 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2621 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2634 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2635 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2649XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2650 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2658 if (prot->ChunkRespHeader(0))
2661 std::stringstream ss;
2663 ss << httpStatusCode;
2664 if (!httpErrorBody.empty()) {
2665 std::string_view statusView(httpErrorBody);
2668 if (statusView[statusView.size() - 1] ==
'\n') {
2669 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2671 ss <<
": " << httpErrorBody;
2675 if (!extra_text.empty())
2676 ss <<
": " << extra_text;
2680 const auto header =
"X-Transfer-Status: " + ss.str();
2681 if (prot->SendData(header.c_str(), header.size()))
2684 if (prot->ChunkRespFooter())
2689 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2695void XrdHttpReq::addAgeHeader(std::string &headers) {
2697 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2702 TRACE(REQ,
" XrdHttpReq request ended.");
2746 m_transfer_encoding_chunked =
false;
2747 m_current_chunk_size = -1;
2748 m_current_chunk_offset = 0;
2750 m_trailer_headers =
false;
2751 m_status_trailer =
false;
2785 httpStatusCode = -1;
2791void XrdHttpReq::getfhandle() {
2794 TRACEI(REQ,
"fhandle:" <<
2808 for (
int i = 0; i <
iovN; i++) {
2810 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2811 l = (readahead_list *) p;
2812 len = ntohl(l->
rlen);
2814 received.emplace_back(p+
sizeof(readahead_list), -1, len);
2816 p +=
sizeof (readahead_list);
2825 for (
int i = 0; i <
iovN; i++) {
2826 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2831int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2833 if (received.size() == 0) {
2847 const XrdOucIOVec2 *ci;
2848 const XrdHttpReadRangeHandler::UserRange *ur;
2849 std::string st_header;
2850 std::string fin_header;
2857 std::vector<rinfo> rvec;
2860 rvec.reserve(received.size());
2862 for(
const auto &rcv: received) {
2865 const XrdHttpReadRangeHandler::UserRange *ur;
2871 rentry.start = start;
2872 rentry.finish = finish;
2881 rentry.st_header = s;
2882 sum_len += s.size();
2885 sum_len += rcv.size;
2889 rentry.fin_header = s;
2890 sum_len += s.size();
2893 rvec.push_back(rentry);
2898 if (m_transfer_encoding_chunked && m_trailer_headers) {
2899 prot->ChunkRespHeader(sum_len);
2903 for(
const auto &rentry: rvec) {
2906 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2907 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2913 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2917 if (rentry.finish) {
2918 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2925 if (m_transfer_encoding_chunked && m_trailer_headers) {
2926 prot->ChunkRespFooter();
2932int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2935 if (received.size() == 0) {
2945 for(
const auto &rcv: received) {
2947 if (
readRangeHandler.NotifyReadResult(rcv.size,
nullptr, start, finish) < 0) {
2954 if (m_transfer_encoding_chunked && m_trailer_headers) {
2955 prot->ChunkRespHeader(sum);
2957 for(
const auto &rcv: received) {
2958 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2960 if (m_transfer_encoding_chunked && m_trailer_headers) {
2961 prot->ChunkRespFooter();
struct ClientSetRequest set
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.
void Tobase64(const unsigned char *input, int length, char *out)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
int mapXrdErrToHttp(XErrorCode xrdError)
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.
@ HTTP_METHOD_NOT_ALLOWED
std::string encode_opaque(const std::string &opaque)
std::string encode_str(const std::string &str)
std::vector< XrdOucIOVec2 > XrdHttpIOList
std::string decode_str(const std::string &str)
std::string obfuscateAuth(const std::string &input)
virtual int ProcessReq(XrdHttpExtReq &)=0
std::vector< UserRange > UserRangeList
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.
void setTransferStatusHeader(std::string &header)
bool m_appended_hdr2cgistr
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
bool m_appended_asize
Track whether we already appended the oss.asize argument for PUTs.
XrdOucString m_resource_with_digest
virtual bool Redir(XrdXrootd::Bridge::Context &info, int port, const char *hname)
virtual int File(XrdXrootd::Bridge::Context &info, int dlen)
std::map< std::string, std::string > allheaders
void addCgi(const std::string &key, const std::string &value)
ClientRequest xrdreq
The last issued xrd request, often pending.
std::string buildPartialHdr(long long bytestart, long long byteend, long long filesize, char *token)
Build a partial header for a multipart response.
XrdHttpReadRangeHandler readRangeHandler
Tracking the next ranges of data to read during GET.
virtual bool Error(XrdXrootd::Bridge::Context &info, int ecode, const char *etext)
static const int minTotID
static const int maxTotID
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)