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);
1640 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1645 strcpy(buf2,
host.c_str());
1646 char *pos = strchr(buf2,
':');
1647 if (pos) *pos =
'\0';
1655 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1665 xrdreq.mv.dlen = htonl(l);
1668 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1669 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1679 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1690XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1693 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1698 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1699 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1701 bool convert_to_base64 =
m_req_cksum->needsBase64Padding();
1702 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1703 if (convert_to_base64) {
1704 size_t digest_length = strlen(digest_value);
1705 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1706 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1707 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1708 free(digest_binary_value);
1711 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1713 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1714 free(digest_binary_value);
1715 digest_value = digest_base64_value;
1718 digest_header =
"Digest: ";
1720 digest_header +=
"=";
1721 digest_header += digest_value;
1722 if (convert_to_base64) {free(digest_value);}
1725 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1731XrdHttpReq::PostProcessListing(
bool final_) {
1734 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1735 httpErrorBody.c_str(), httpErrorBody.length(),
false);
1741 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1742 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1744 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1745 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1746 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1767 "<th class=\"mode\">Mode</th>"
1768 "<th class=\"flags\">Flags</th>"
1769 "<th class=\"size\">Size</th>"
1770 "<th class=\"datetime\">Modified</th>"
1771 "<th class=\"name\">Name</th>"
1777 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1780 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
1782 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1783 strncpy(entry, (
char *) startp, endp - startp);
1784 entry[endp - startp] = 0;
1791 <<
" stat=" << endp);
1794 sscanf(endp,
"%ld %lld %ld %ld",
1800 strcpy(entry, (
char *) startp);
1802 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1804 std::string p =
"<tr>"
1805 "<td class=\"mode\">";
1826 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1827 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1829 "<td class=\"name\">"
1837 if (!p.empty() && p[p.size() - 1] !=
'/')
1842 std::unique_ptr<char,
decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1848 p +=
"</a></td></tr>";
1854 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1855 if (pp) startp = pp+1;
1864 stringresp +=
"</table></div><br><br><hr size=1>"
1865 "<p><span id=\"requestby\">Request by ";
1867 if (prot->SecEntity.name)
1872 if (prot->SecEntity.vorg ||
1873 prot->SecEntity.name ||
1874 prot->SecEntity.moninfo ||
1875 prot->SecEntity.role)
1878 if (prot->SecEntity.vorg) {
1883 if (prot->SecEntity.moninfo) {
1887 if (prot->SecEntity.name) {
1892 if (prot->SecEntity.role) {
1895 if (prot->SecEntity.endorsements) {
1902 if (prot->SecEntity.vorg ||
1903 prot->SecEntity.moninfo ||
1904 prot->SecEntity.role)
1907 if (prot->SecEntity.host) {
1927XrdHttpReq::ReturnGetHeaders() {
1928 std::string responseHeader;
1933 if (!responseHeader.empty()) {
1934 responseHeader +=
"\r\n";
1936 addAgeHeader(responseHeader);
1948 if (m_transfer_encoding_chunked && m_trailer_headers) {
1950 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
1952 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
1960 if (uranges.size() != 1)
1965 const off_t cnt = uranges[0].end - uranges[0].start + 1;
1967 std::string header =
"Content-Range: bytes ";
1968 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
1970 if (!responseHeader.empty()) {
1972 header += responseHeader.c_str();
1975 if (m_transfer_encoding_chunked && m_trailer_headers) {
1977 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
1979 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
1986 for (
auto &ur : uranges) {
1987 cnt += ur.end - ur.start + 1;
1992 (
char *)
"123456").size();
1996 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2002 if (!header.empty()) {
2005 addAgeHeader(header);
2008 if (m_transfer_encoding_chunked && m_trailer_headers) {
2010 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2012 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2018 if (m_status_trailer) {
2019 if (header.empty()) {
2020 header +=
"Trailer: X-Transfer-Status";
2022 header +=
"\r\nTrailer: X-Transfer-Status";
2029int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2031 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2032 generateWebdavErrMsg();
2037 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2046 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2051 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2058 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2062 std::string response_headers;
2066 <<
" stat=" << (
char *)
iovP[0].iov_base);
2069 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2079 addAgeHeader(response_headers);
2080 response_headers +=
"\r\n";
2082 response_headers +=
"Accept-Ranges: bytes";
2083 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2088 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2091 return ret_keepalive ? 1 : -1;
2094 std::string response_headers;
2095 int response = PostProcessChecksum(response_headers);
2096 if (-1 == response) {
2099 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2101 addAgeHeader(response_headers);
2102 response_headers +=
"\r\n";
2104 response_headers +=
"Accept-Ranges: bytes";
2105 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2108 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2130 if (
iovP[1].iov_len > 1) {
2132 <<
" stat=" << (
char *)
iovP[1].iov_base);
2135 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2156 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2157 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2166 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2167 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2180 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2181 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2188 return PostProcessListing(final_);
2198 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2202 const XrdHttpReadRangeHandler::Error &rrerror =
readRangeHandler.getError();
2205 httpErrorBody = rrerror.
errMsg;
2208 if (m_transfer_encoding_chunked && m_trailer_headers) {
2209 if (prot->ChunkRespHeader(0))
2212 const std::string crlf =
"\r\n";
2213 std::stringstream ss;
2214 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpErrorBody << crlf;
2216 const auto header = ss.str();
2217 if (prot->SendData(header.c_str(), header.size()))
2220 if (prot->ChunkRespFooter())
2224 if (rrerror)
return -1;
2231 auto rc = sendFooterError(
"");
2240 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2243 getReadResponse(received);
2247 rc = sendReadResponseSingleRange(received);
2249 rc = sendReadResponsesMultiRanges(received);
2268 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2276 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2279 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2290 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2295 int l = ntohl(
xrdreq.write.dlen);
2298 prot->BuffConsume(ntohl(
xrdreq.write.dlen));
2302 if (m_transfer_encoding_chunked) {
2303 m_current_chunk_offset += l;
2307 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2314 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2317 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2336 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2337 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2352 <<
" stat=" << (
char *)
iovP[0].iov_base);
2355 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2367 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2370 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2371 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2383 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2384 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2402 <<
" stat=" << (
char *)
iovP[0].iov_base);
2405 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2411 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2416 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2444 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2445 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2447 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2451 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2452 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2454 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2459 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2469 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";
2472 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2486 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2490 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
2492 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2493 strncpy(entry, (
char *) startp, endp - startp);
2494 entry[endp - startp] = 0;
2501 <<
" stat=" << endp);
2504 sscanf(endp,
"%ld %lld %ld %ld",
2512 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2532 if (*p.rbegin() !=
'/') p +=
"/";
2536 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2560 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2561 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2563 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2567 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2568 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2570 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2573 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2581 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2582 if (pp) startp = pp+1;
2593 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";
2596 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2616 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2618 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2619 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2624 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2632 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2636 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2649 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2650 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2664XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2665 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2673 if (prot->ChunkRespHeader(0))
2676 std::stringstream ss;
2678 ss << httpStatusCode;
2679 if (!httpErrorBody.empty()) {
2680 std::string_view statusView(httpErrorBody);
2683 if (statusView[statusView.size() - 1] ==
'\n') {
2684 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2686 ss <<
": " << httpErrorBody;
2690 if (!extra_text.empty())
2691 ss <<
": " << extra_text;
2695 const auto header =
"X-Transfer-Status: " + ss.str();
2696 if (prot->SendData(header.c_str(), header.size()))
2699 if (prot->ChunkRespFooter())
2704 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2710void XrdHttpReq::addAgeHeader(std::string &headers) {
2712 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2717 TRACE(REQ,
" XrdHttpReq request ended.");
2761 m_transfer_encoding_chunked =
false;
2762 m_current_chunk_size = -1;
2763 m_current_chunk_offset = 0;
2765 m_trailer_headers =
false;
2766 m_status_trailer =
false;
2800 httpStatusCode = -1;
2806void XrdHttpReq::getfhandle() {
2809 TRACEI(REQ,
"fhandle:" <<
2823 for (
int i = 0; i <
iovN; i++) {
2825 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2826 l = (readahead_list *) p;
2827 len = ntohl(l->
rlen);
2829 received.emplace_back(p+
sizeof(readahead_list), -1, len);
2831 p +=
sizeof (readahead_list);
2840 for (
int i = 0; i <
iovN; i++) {
2841 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2846int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2848 if (received.size() == 0) {
2862 const XrdOucIOVec2 *ci;
2863 const XrdHttpReadRangeHandler::UserRange *ur;
2864 std::string st_header;
2865 std::string fin_header;
2872 std::vector<rinfo> rvec;
2875 rvec.reserve(received.size());
2877 for(
const auto &rcv: received) {
2880 const XrdHttpReadRangeHandler::UserRange *ur;
2886 rentry.start = start;
2887 rentry.finish = finish;
2896 rentry.st_header = s;
2897 sum_len += s.size();
2900 sum_len += rcv.size;
2904 rentry.fin_header = s;
2905 sum_len += s.size();
2908 rvec.push_back(rentry);
2913 if (m_transfer_encoding_chunked && m_trailer_headers) {
2914 prot->ChunkRespHeader(sum_len);
2918 for(
const auto &rentry: rvec) {
2921 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2922 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2928 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2932 if (rentry.finish) {
2933 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2940 if (m_transfer_encoding_chunked && m_trailer_headers) {
2941 prot->ChunkRespFooter();
2947int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2950 if (received.size() == 0) {
2960 for(
const auto &rcv: received) {
2962 if (
readRangeHandler.NotifyReadResult(rcv.size,
nullptr, start, finish) < 0) {
2969 if (m_transfer_encoding_chunked && m_trailer_headers) {
2970 prot->ChunkRespHeader(sum);
2972 for(
const auto &rcv: received) {
2973 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2975 if (m_transfer_encoding_chunked && m_trailer_headers) {
2976 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.
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)
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)