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());
825void XrdHttpReq::sendWebdavErrorMessage(
827 XRequestTypes xrdOperation, std::string etext,
const char *desc,
828 const char *header_to_add,
bool keepalive) {
830 std::string errCode{
"Unknown"};
831 std::string statusText;
866 httpStatusCode = code;
867 httpErrorCode = errCode;
868 httpErrorBody =
"ERROR: " + errCode +
": " +
etext +
"\n";
870 prot->SendSimpleResp(httpStatusCode, desc, header_to_add,
871 httpErrorBody.c_str(), httpErrorBody.length(),
879void XrdHttpReq::mapXrdErrorToHttpStatus() {
881 httpStatusCode = 500;
882 httpErrorBody =
"Unrecognized error";
888 httpStatusCode = 401; httpErrorBody =
"Unauthorized";
891 httpStatusCode = 403; httpErrorBody =
"Operation not permitted";
894 httpStatusCode = 404; httpErrorBody =
"File not found";
897 httpStatusCode = 405; httpErrorBody =
"Operation not supported";
900 httpStatusCode = 423; httpErrorBody =
"Resource is a locked";
903 httpStatusCode = 409; httpErrorBody =
"Resource is a directory";
907 httpStatusCode = 409; httpErrorBody =
"File already exists";
912 httpStatusCode = 405;
916 httpStatusCode = 405; httpErrorBody =
"Method is not allowed";
919 httpStatusCode = 502; httpErrorBody =
"Bad Gateway";
922 httpStatusCode = 504; httpErrorBody =
"Gateway timeout";
931 <<
"] to status code [" << httpStatusCode <<
"]");
933 httpErrorBody +=
"\n";
935 httpStatusCode = 200;
936 httpErrorBody =
"OK";
948 int query_param_status = 0;
952 if (query_param_status == 0) {
956 query_param_status = 1;
957 auto length_str = std::to_string(
length);
963 opaque->Put(
"oss.asize", length_str.c_str());
969 if (query_param_status == 0) {
981 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
982 << header2cgistrObf.c_str() <<
"'");
997 if (r < 0)
return -1;
1011 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
1017 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
1026 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1038 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1050 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1060 if (
resource.beginswith(
"/static/")) {
1071 if (prot->embeddedstatic) {
1074 if (
resource ==
"/static/css/xrdhttp.css") {
1075 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1079 if (
resource ==
"/static/icons/xrdhttp.ico") {
1080 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1089 if (prot->staticredir) {
1092 s.
append(prot->staticredir);
1100 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1107 if (prot->staticpreload) {
1110 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1137 xrdreq.open.dlen = htonl(l);
1142 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1160 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1172 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1177 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1187 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1188 mapXrdErrorToHttpStatus();
1189 return sendFooterError(
"Could not run close request on the bridge");
1198 if (prot->listdeny) {
1199 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1203 if (prot->listredir) {
1205 s.
append(prot->listredir);
1213 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1224 l = res.length() + 1;
1225 xrdreq.dirlist.dlen = htonl(l);
1227 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) res.c_str(), l)) {
1228 mapXrdErrorToHttpStatus();
1229 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1230 sendFooterError(
"Could not run listing request on the bridge");
1243 auto retval = ReturnGetHeaders();
1263 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1264 TRACEI(REQ,
" Failed to run close request on the bridge.");
1278 if ( readChunkList.size() == 1 ) {
1290 offs = readChunkList[0].offset;
1291 l = readChunkList[0].size;
1293 xrdreq.read.offset = htonll(offs);
1294 xrdreq.read.rlen = htonl(l);
1299 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1302 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1311 TRACE(ALL,
" Data sizes mismatch.");
1315 TRACE(ALL,
" No more bytes to send.");
1322 httpStatusCode = 416;
1323 httpErrorBody =
"Range Not Satisfiable";
1324 std::stringstream ss;
1325 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1326 return sendFooterError(ss.str());
1329 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1330 mapXrdErrorToHttpStatus();
1331 return sendFooterError(
"Could not run read request on the bridge");
1339 mapXrdErrorToHttpStatus();
1340 return sendFooterError(
"Could not run ReadV request on the bridge");
1367 xrdreq.open.dlen = htonl(l);
1369 if (! XrdHttpProtocol::usingEC)
1375 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1390 if (m_transfer_encoding_chunked) {
1391 if (m_current_chunk_size == m_current_chunk_offset) {
1394 if (prot->BuffUsed() < 2)
return 1;
1395 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1396 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1399 prot->BuffConsume(2);
1400 if (m_current_chunk_size == 0) {
1404 m_transfer_encoding_chunked =
false;
1408 m_current_chunk_size = -1;
1409 m_current_chunk_offset = 0;
1411 if (!prot->BuffUsed())
return 1;
1413 if (-1 == m_current_chunk_size) {
1417 bool found_newline =
false;
1424 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1425 for (; idx < max_chunk_size_chars; idx++) {
1426 if (prot->myBuffStart[idx] ==
'\n') {
1427 found_newline =
true;
1433 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1434 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1435 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1438 if (found_newline) {
1439 char *endptr = NULL;
1440 std::string line_contents(prot->myBuffStart, idx);
1441 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1443 if (*endptr !=
';' && *endptr !=
'\r') {
1444 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1445 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1448 m_current_chunk_size = chunk_contents;
1449 m_current_chunk_offset = 0;
1450 prot->BuffConsume(idx + 1);
1451 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1458 if (m_current_chunk_size == 0) {
1467 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1468 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1469 chunk_bytes_remaining);
1472 xrdreq.write.dlen = htonl(bytes_to_write);
1474 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1475 if (!prot->Bridge->Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1476 mapXrdErrorToHttpStatus();
1477 return sendFooterError(
"Could not run write request on the bridge");
1481 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1491 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1495 xrdreq.write.dlen = htonl(bytes_to_read);
1497 TRACEI(REQ,
"Writing " << bytes_to_read);
1498 if (!prot->Bridge->Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1499 mapXrdErrorToHttpStatus();
1500 return sendFooterError(
"Could not run write request on the bridge");
1521 if (!prot->Bridge->Run((
char *) &
xrdreq, 0, 0)) {
1522 mapXrdErrorToHttpStatus();
1523 return sendFooterError(
"Could not run close request on the bridge");
1538 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);
1541 return ret_keepalive ? 1 : -1;
1560 xrdreq.stat.dlen = htonl(l);
1563 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1580 xrdreq.rmdir.dlen = htonl(l);
1582 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1583 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1594 xrdreq.rm.dlen = htonl(l);
1596 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1597 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1613 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1628 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1633 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1638 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1654 xrdreq.stat.dlen = htonl(l);
1657 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1686 xrdreq.dirlist.dlen = htonl(l);
1688 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1689 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1712 xrdreq.mkdir.dlen = htonl(l);
1714 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1715 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1745 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1750 strcpy(buf2,
host.c_str());
1751 char *pos = strchr(buf2,
':');
1752 if (pos) *pos =
'\0';
1760 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1770 xrdreq.mv.dlen = htonl(l);
1773 if (!prot->Bridge->Run((
char *) &
xrdreq, (
char *) s.c_str(), l)) {
1774 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1784 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1795XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1798 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1803 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1804 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1806 bool convert_to_base64 =
m_req_cksum->needsBase64Padding();
1807 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1808 if (convert_to_base64) {
1809 size_t digest_length = strlen(digest_value);
1810 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1811 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1812 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1813 free(digest_binary_value);
1816 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1818 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1819 free(digest_binary_value);
1820 digest_value = digest_base64_value;
1823 digest_header =
"Digest: ";
1825 digest_header +=
"=";
1826 digest_header += digest_value;
1827 if (convert_to_base64) {free(digest_value);}
1830 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpErrorBody.c_str(), httpErrorBody.length(),
false);
1836XrdHttpReq::PostProcessListing(
bool final_) {
1839 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1840 httpErrorBody.c_str(), httpErrorBody.length(),
false);
1846 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1847 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1849 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1850 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1851 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1872 "<th class=\"mode\">Mode</th>"
1873 "<th class=\"flags\">Flags</th>"
1874 "<th class=\"size\">Size</th>"
1875 "<th class=\"datetime\">Modified</th>"
1876 "<th class=\"name\">Name</th>"
1882 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1885 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
1887 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1888 strncpy(entry, (
char *) startp, endp - startp);
1889 entry[endp - startp] = 0;
1896 <<
" stat=" << endp);
1899 sscanf(endp,
"%ld %lld %ld %ld",
1905 strcpy(entry, (
char *) startp);
1907 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1909 std::string p =
"<tr>"
1910 "<td class=\"mode\">";
1931 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1932 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1934 "<td class=\"name\">"
1942 if (!p.empty() && p[p.size() - 1] !=
'/')
1947 std::unique_ptr<char,
decltype(&free)> estr(
escapeXML(e.
path.c_str()), &free);
1953 p +=
"</a></td></tr>";
1959 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1960 if (pp) startp = pp+1;
1969 stringresp +=
"</table></div><br><br><hr size=1>"
1970 "<p><span id=\"requestby\">Request by ";
1972 if (prot->SecEntity.name)
1977 if (prot->SecEntity.vorg ||
1978 prot->SecEntity.name ||
1979 prot->SecEntity.moninfo ||
1980 prot->SecEntity.role)
1983 if (prot->SecEntity.vorg) {
1988 if (prot->SecEntity.moninfo) {
1992 if (prot->SecEntity.name) {
1997 if (prot->SecEntity.role) {
2000 if (prot->SecEntity.endorsements) {
2007 if (prot->SecEntity.vorg ||
2008 prot->SecEntity.moninfo ||
2009 prot->SecEntity.role)
2012 if (prot->SecEntity.host) {
2032XrdHttpReq::ReturnGetHeaders() {
2033 std::string responseHeader;
2038 if (!responseHeader.empty()) {
2039 responseHeader +=
"\r\n";
2041 addAgeHeader(responseHeader);
2053 if (m_transfer_encoding_chunked && m_trailer_headers) {
2055 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2057 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2065 if (uranges.size() != 1)
2070 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2072 std::string header =
"Content-Range: bytes ";
2073 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2075 if (!responseHeader.empty()) {
2077 header += responseHeader.c_str();
2080 if (m_transfer_encoding_chunked && m_trailer_headers) {
2082 prot->StartChunkedResp(206, NULL, header.empty() ?
nullptr : header.c_str(), -1,
keepalive);
2084 prot->SendSimpleResp(206, NULL, header.empty() ?
nullptr : header.c_str(), NULL, cnt,
keepalive);
2091 for (
auto &ur : uranges) {
2092 cnt += ur.end - ur.start + 1;
2097 (
char *)
"123456").size();
2101 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2107 if (!header.empty()) {
2110 addAgeHeader(header);
2113 if (m_transfer_encoding_chunked && m_trailer_headers) {
2115 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2117 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2123 if (m_status_trailer) {
2124 if (header.empty()) {
2125 header +=
"Trailer: X-Transfer-Status";
2127 header +=
"\r\nTrailer: X-Transfer-Status";
2134int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2136 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2137 mapXrdErrorToHttpStatus();
2142 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2151 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2156 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2163 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2167 std::string response_headers;
2171 <<
" stat=" << (
char *)
iovP[0].iov_base);
2174 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2184 addAgeHeader(response_headers);
2185 response_headers +=
"\r\n";
2187 response_headers +=
"Accept-Ranges: bytes";
2188 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2193 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2196 return ret_keepalive ? 1 : -1;
2199 std::string response_headers;
2200 int response = PostProcessChecksum(response_headers);
2201 if (-1 == response) {
2204 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2206 addAgeHeader(response_headers);
2207 response_headers +=
"\r\n";
2209 response_headers +=
"Accept-Ranges: bytes";
2210 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2213 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2235 if (
iovP[1].iov_len > 1) {
2237 <<
" stat=" << (
char *)
iovP[1].iov_base);
2240 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2261 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2262 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2271 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2272 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2285 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2286 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2293 return PostProcessListing(final_);
2303 TRACEI(REQ,
"Close was completed after an error: " <<
xrdresp);
2307 const XrdHttpReadRangeHandler::Error &rrerror =
readRangeHandler.getError();
2310 httpErrorBody = rrerror.
errMsg;
2313 if (m_transfer_encoding_chunked && m_trailer_headers) {
2314 if (prot->ChunkRespHeader(0))
2317 const std::string crlf =
"\r\n";
2318 std::stringstream ss;
2319 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpErrorBody << crlf;
2321 const auto header = ss.str();
2322 if (prot->SendData(header.c_str(), header.size()))
2325 if (prot->ChunkRespFooter())
2329 if (rrerror)
return -1;
2336 auto rc = sendFooterError(
"");
2345 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2348 getReadResponse(received);
2352 rc = sendReadResponseSingleRange(received);
2354 rc = sendReadResponsesMultiRanges(received);
2382 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2385 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2397 int l = ntohl(
xrdreq.write.dlen);
2400 prot->BuffConsume(ntohl(
xrdreq.write.dlen));
2404 if (m_transfer_encoding_chunked) {
2405 m_current_chunk_offset += l;
2409 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2416 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2439 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2440 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2455 <<
" stat=" << (
char *)
iovP[0].iov_base);
2458 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2470 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2473 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2474 httpErrorBody.c_str(), httpErrorBody.length(),
keepalive);
2486 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2487 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2505 <<
" stat=" << (
char *)
iovP[0].iov_base);
2508 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2514 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2519 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2547 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2548 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2550 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2554 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2555 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2557 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2562 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2572 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";
2575 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2589 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2593 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (
size_t)(
iovP[0].iov_len - 1) ) {
2595 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2596 strncpy(entry, (
char *) startp, endp - startp);
2597 entry[endp - startp] = 0;
2604 <<
" stat=" << endp);
2607 sscanf(endp,
"%ld %lld %ld %ld",
2615 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2635 if (*p.rbegin() !=
'/') p +=
"/";
2639 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2663 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2664 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2666 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2670 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2671 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2673 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2676 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2684 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2685 if (pp) startp = pp+1;
2696 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";
2699 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2719 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2721 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2722 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2727 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2735 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2739 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2752 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2753 httpErrorBody.c_str(), httpErrorBody.length(),
false);
2767XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2768 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2776 if (prot->ChunkRespHeader(0))
2779 std::stringstream ss;
2781 ss << httpStatusCode;
2782 if (!httpErrorBody.empty()) {
2783 std::string_view statusView(httpErrorBody);
2786 if (statusView[statusView.size() - 1] ==
'\n') {
2787 ss <<
": " << statusView.substr(0, statusView.size() - 1);
2789 ss <<
": " << httpErrorBody;
2793 if (!extra_text.empty())
2794 ss <<
": " << extra_text;
2798 const auto header =
"X-Transfer-Status: " + ss.str();
2799 if (prot->SendData(header.c_str(), header.size()))
2802 if (prot->ChunkRespFooter())
2807 TRACEI(REQ,
"Failure during response: " << httpStatusCode <<
": " << httpErrorBody << (extra_text.empty() ?
"" : (
": " + extra_text)));
2813void XrdHttpReq::addAgeHeader(std::string &headers) {
2815 headers += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2820 TRACE(REQ,
" XrdHttpReq request ended.");
2864 m_transfer_encoding_chunked =
false;
2865 m_current_chunk_size = -1;
2866 m_current_chunk_offset = 0;
2868 m_trailer_headers =
false;
2869 m_status_trailer =
false;
2904void XrdHttpReq::getfhandle() {
2907 TRACEI(REQ,
"fhandle:" <<
2921 for (
int i = 0; i <
iovN; i++) {
2923 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2924 l = (readahead_list *) p;
2925 len = ntohl(l->
rlen);
2927 received.emplace_back(p+
sizeof(readahead_list), -1, len);
2929 p +=
sizeof (readahead_list);
2938 for (
int i = 0; i <
iovN; i++) {
2939 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2944int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2946 if (received.size() == 0) {
2960 const XrdOucIOVec2 *ci;
2961 const XrdHttpReadRangeHandler::UserRange *ur;
2962 std::string st_header;
2963 std::string fin_header;
2970 std::vector<rinfo> rvec;
2973 rvec.reserve(received.size());
2975 for(
const auto &rcv: received) {
2978 const XrdHttpReadRangeHandler::UserRange *ur;
2984 rentry.start = start;
2985 rentry.finish = finish;
2994 rentry.st_header = s;
2995 sum_len += s.size();
2998 sum_len += rcv.size;
3002 rentry.fin_header = s;
3003 sum_len += s.size();
3006 rvec.push_back(rentry);
3011 if (m_transfer_encoding_chunked && m_trailer_headers) {
3012 prot->ChunkRespHeader(sum_len);
3016 for(
const auto &rentry: rvec) {
3019 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
3020 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
3026 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
3030 if (rentry.finish) {
3031 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
3038 if (m_transfer_encoding_chunked && m_trailer_headers) {
3039 prot->ChunkRespFooter();
3045int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
3048 if (received.size() == 0) {
3058 for(
const auto &rcv: received) {
3060 if (
readRangeHandler.NotifyReadResult(rcv.size,
nullptr, start, finish) < 0) {
3067 if (m_transfer_encoding_chunked && m_trailer_headers) {
3068 prot->ChunkRespHeader(sum);
3070 for(
const auto &rcv: received) {
3071 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
3073 if (m_transfer_encoding_chunked && m_trailer_headers) {
3074 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)
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::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.
ReqType
These are the HTTP/DAV requests that we support.
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)