39 #include "XrdVersion.hh"
44 #include <arpa/inet.h>
65 #define MAX_TK_LEN 256
66 #define MAX_RESOURCE_LEN 16384
69 #define TRACELINK prot->Link
73 const char *TraceID =
"Req";
76 void trim(std::string &str)
86 memset(&t1, 0,
sizeof (t1));
89 strftime(datebuf, 127,
"%a, %d %b %Y %H:%M:%S GMT", &t1);
90 return (std::string) datebuf;
122 if (!line)
return -1;
125 char *p = strchr((
char *) line, (
int)
':');
141 char *val = line + pos + 1;
144 while ( (!isgraph(*val) || (!*val)) && (val < line+len)) val++;
148 std::string ss = val;
149 if(ss.length() >= 2 && ss.substr(ss.length() - 2, 2) !=
"\r\n") {
162 if (!strcasecmp(key,
"connection")) {
164 if (!strcasecmp(val,
"Keep-Alive\r\n")) {
166 }
else if (!strcasecmp(val,
"close\r\n")) {
170 }
else if (!strcasecmp(key,
"host")) {
172 }
else if (!strcasecmp(key,
"range")) {
177 }
else if (!strcasecmp(key,
"content-length")) {
180 }
else if (!strcasecmp(key,
"destination")) {
183 }
else if (!strcasecmp(key,
"want-digest")) {
188 }
else if (!strcasecmp(key,
"depth")) {
190 if (strcmp(val,
"infinity"))
193 }
else if (!strcasecmp(key,
"expect") && strstr(val,
"100-continue")) {
195 }
else if (!strcasecmp(key,
"te") && strstr(val,
"trailers")) {
196 m_trailer_headers =
true;
197 }
else if (!strcasecmp(key,
"transfer-encoding") && strstr(val,
"chunked")) {
198 m_transfer_encoding_chunked =
true;
199 }
else if (!strcasecmp(key,
"x-transfer-status") && strstr(val,
"true")) {
200 m_transfer_encoding_chunked =
true;
201 m_status_trailer =
true;
202 }
else if (!strcasecmp(key,
"scitag")) {
206 }
else if (!strcasecmp(key,
"user-agent")) {
211 auto it = std::find_if(prot->
hdr2cgimap.begin(), prot->
hdr2cgimap.end(),[key](
const auto & item) {
212 return !strcasecmp(key,item.first.c_str());
216 s.assign(val, line+len-val);
229 int XrdHttpReq::parseHost(
char *line) {
235 void XrdHttpReq::parseScitag(
const std::string & val) {
239 std::string scitagS = val;
242 if(scitagS[0] !=
'-') {
244 mScitag = std::stoi(scitagS.c_str(),
nullptr, 10);
263 if (!line)
return -1;
266 char *p = strchr((
char *) line, (
int)
' ');
290 char *val = line + pos + 1;
297 p = strchr((
char *) val, (
int)
' ');
311 if (!strcmp(key,
"GET")) {
313 }
else if (!strcmp(key,
"HEAD")) {
315 }
else if (!strcmp(key,
"PUT")) {
317 }
else if (!strcmp(key,
"POST")) {
319 }
else if (!strcmp(key,
"PATCH")) {
321 }
else if (!strcmp(key,
"OPTIONS")) {
323 }
else if (!strcmp(key,
"DELETE")) {
325 }
else if (!strcmp(key,
"PROPFIND")) {
328 }
else if (!strcmp(key,
"MKCOL")) {
331 }
else if (!strcmp(key,
"MOVE")) {
341 if (!strcmp(p+1,
"HTTP/1.0\r\n")) {
355 void XrdHttpReq::clientMarshallReadAheadList(
int nitems) {
362 for (
int i = 0; i < nitems; i++) {
373 void XrdHttpReq::clientUnMarshallReadAheadList(
int nitems) {
380 for (
int i = 0; i < nitems; i++) {
398 for (
const auto &c: cl) {
401 memcpy(&ra.fhandle, this->fhandle, 4);
403 ra.offset = c.offset;
417 clientMarshallReadAheadList(j);
426 std::ostringstream s;
428 s <<
"\r\n--" << token <<
"\r\n";
429 s <<
"Content-type: text/plain; charset=UTF-8\r\n";
430 s <<
"Content-range: bytes " << bytestart <<
"-" << byteend <<
"/" << fsz <<
"\r\n\r\n";
436 std::ostringstream s;
438 s <<
"\r\n--" << token <<
"--\r\n";
451 TRACE(REQ,
" XrdHttpReq::Data! final=" <<
final);
457 this->
final = final_;
459 if (PostProcessHTTPReq(final_))
reset();
473 int rc = info.
Send(0, 0, 0, 0);
474 TRACE(REQ,
" XrdHttpReq::File dlen:" << dlen <<
" send rc:" << rc);
491 TRACE(REQ,
" XrdHttpReq::Done");
497 int r = PostProcessHTTPReq(
true);
500 if (r < 0)
return false;
511 TRACE(REQ,
" XrdHttpReq::Error");
522 if (PostProcessHTTPReq())
reset();
552 if (strncmp(hname,
"file://", 7) == 0)
554 TRACE(REQ,
" XrdHttpReq::Redir Switching to file:// ");
561 char *pp = strchr((
char *)hname,
'?');
567 int varlen = strlen(vardata);
570 while(*vardata ==
'&' && varlen) {vardata++; varlen--;}
579 sprintf(buf,
":%d", port);
587 char *newvardata =
quote(vardata);
625 return ret_keepalive;
636 if (
hdr2cgistr.empty() && (l < 2) && !hash)
return;
648 char *s1 =
quote(p+1);
665 s +=
"&xrdhttptime=";
667 sprintf(buf,
"%lld", (
long long) tnow);
672 s +=
"&xrdhttpname=";
681 s +=
"&xrdhttpvorg=";
690 s +=
"&xrdhttphost=";
708 s +=
"&xrdhttprole=";
717 s +=
"&xrdhttpgrps=";
726 s +=
"&xrdhttpendorsements=";
735 s +=
"&xrdhttpcredslen=";
737 sprintf(buf,
"%d", secent->
credslen);
738 char *s1 =
quote(buf);
747 s +=
"&xrdhttpcreds=";
751 char *s1 =
quote(zerocreds);
769 void XrdHttpReq::sanitizeResourcePfx() {
800 void XrdHttpReq::parseResource(
char *res) {
806 char *p = strchr(res,
'?');
814 sanitizeResourcePfx();
839 sanitizeResourcePfx();
870 void XrdHttpReq::mapXrdErrorToHttpStatus() {
872 httpStatusCode = 500;
873 httpStatusText =
"Unrecognized error";
879 httpStatusCode = 401; httpStatusText =
"Unauthorized";
882 httpStatusCode = 403; httpStatusText =
"Operation not permitted";
885 httpStatusCode = 404; httpStatusText =
"File not found";
888 httpStatusCode = 405; httpStatusText =
"Operation not supported";
891 httpStatusCode = 423; httpStatusText =
"Resource is a locked";
894 httpStatusCode = 409; httpStatusText =
"Resource is a directory";
897 if(
request != ReqType::rtDELETE) {
898 httpStatusCode = 409; httpStatusText =
"File already exists";
903 httpStatusCode = 405;
907 httpStatusCode = 405; httpStatusText =
"Method is not allowed";
910 httpStatusCode = 504; httpStatusText =
"Gateway timeout";
919 <<
"] to status code [" << httpStatusCode <<
"]");
921 httpStatusText +=
"\n";
923 httpStatusCode = 200;
924 httpStatusText =
"OK";
948 TRACEI(
DEBUG,
"Appended header fields to opaque info: '"
949 << header2cgistrObf.c_str() <<
"'");
970 if (r < 0)
return -1;
984 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request unknown", 0,
false);
990 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed", 0,
false);
999 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1011 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1023 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to create initial checksum request.", 0,
false);
1047 if (
resource ==
"/static/css/xrdhttp.css") {
1048 prot->SendSimpleResp(200, NULL, NULL, (
char *) static_css_xrdhttp_css, static_css_xrdhttp_css_len,
keepalive);
1052 if (
resource ==
"/static/icons/xrdhttp.ico") {
1053 prot->SendSimpleResp(200, NULL, NULL, (
char *) favicon_ico, favicon_ico_len,
keepalive);
1073 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1083 prot->SendSimpleResp(200, NULL, NULL, (
char *) mydata->
data, mydata->
len,
keepalive);
1115 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1133 prot->SendSimpleResp(403, NULL, NULL, (
char *)
"No HTTP-IANA compatible checksums have been configured.", 0,
false);
1145 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to start internal checksum request to satisfy Want-Digest header.", 0,
false);
1150 TRACEI(
DEBUG,
"No checksum requested; skipping to request state 2");
1161 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1172 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1186 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1197 l = res.length() + 1;
1201 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1214 auto retval = ReturnGetHeaders();
1227 if ( readChunkList.empty() )
1235 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1246 if ( readChunkList.size() == 1 ) {
1258 offs = readChunkList[0].offset;
1259 l = readChunkList[0].size;
1267 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1270 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1279 TRACE(ALL,
" Data sizes mismatch.");
1283 TRACE(ALL,
" No more bytes to send.");
1290 TRACE(ALL,
" Requested range " << l <<
"@" << offs <<
1291 " is past the end of file (" <<
filesize <<
")");
1297 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run read request.", 0,
false);
1306 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run read request.", 0,
false);
1336 if (! XrdHttpProtocol::usingEC)
1342 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1357 if (m_transfer_encoding_chunked) {
1358 if (m_current_chunk_size == m_current_chunk_offset) {
1361 if (prot->BuffUsed() < 2)
return 1;
1362 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1363 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1366 prot->BuffConsume(2);
1367 if (m_current_chunk_size == 0) {
1371 m_transfer_encoding_chunked =
false;
1375 m_current_chunk_size = -1;
1376 m_current_chunk_offset = 0;
1378 if (!prot->BuffUsed())
return 1;
1380 if (-1 == m_current_chunk_size) {
1384 bool found_newline =
false;
1391 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1392 for (; idx < max_chunk_size_chars; idx++) {
1393 if (prot->myBuffStart[idx] ==
'\n') {
1394 found_newline =
true;
1400 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1401 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1402 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1405 if (found_newline) {
1406 char *endptr = NULL;
1407 std::string line_contents(prot->myBuffStart, idx);
1408 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1410 if (*endptr !=
';' && *endptr !=
'\r') {
1411 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1412 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1415 m_current_chunk_size = chunk_contents;
1416 m_current_chunk_offset = 0;
1417 prot->BuffConsume(idx + 1);
1418 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1425 if (m_current_chunk_size == 0) {
1434 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1435 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1436 chunk_bytes_remaining);
1441 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1442 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1443 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Could not run write request.", 0,
false);
1448 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1458 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1464 TRACEI(REQ,
"Writing " << bytes_to_read);
1465 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1466 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run write request.", 0,
false);
1489 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run close request.", 0,
false);
1505 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);
1508 return ret_keepalive ? 1 : -1;
1530 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1550 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1564 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1580 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1595 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1600 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1605 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1624 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1656 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1682 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1703 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1708 strcpy(buf2,
host.c_str());
1709 char *pos = strchr(buf2,
':');
1710 if (pos) *pos =
'\0';
1718 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1732 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1742 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1753 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1756 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1761 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1762 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1765 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1766 if (convert_to_base64) {
1767 size_t digest_length = strlen(digest_value);
1768 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1769 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1770 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1771 free(digest_binary_value);
1774 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1776 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1777 free(digest_binary_value);
1778 digest_value = digest_base64_value;
1781 digest_header =
"Digest: ";
1783 digest_header +=
"=";
1784 digest_header += digest_value;
1785 if (convert_to_base64) {free(digest_value);}
1788 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1794 XrdHttpReq::PostProcessListing(
bool final_) {
1797 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1798 httpStatusText.c_str(), httpStatusText.length(),
false);
1804 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1805 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1807 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1808 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1809 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1830 "<th class=\"mode\">Mode</th>"
1831 "<th class=\"flags\">Flags</th>"
1832 "<th class=\"size\">Size</th>"
1833 "<th class=\"datetime\">Modified</th>"
1834 "<th class=\"name\">Name</th>"
1840 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1843 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1845 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1846 strncpy(entry, (
char *) startp, endp - startp);
1847 entry[endp - startp] = 0;
1854 <<
" stat=" << endp);
1857 sscanf(endp,
"%ld %lld %ld %ld",
1863 strcpy(entry, (
char *) startp);
1865 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1867 std::string p =
"<tr>"
1868 "<td class=\"mode\">";
1889 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1890 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1892 "<td class=\"name\">"
1900 if (!p.empty() && p[p.size() - 1] !=
'/')
1908 p += e.
path +
"\">";
1913 p +=
"</a></td></tr>";
1919 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1920 if (pp) startp = pp+1;
1929 stringresp +=
"</table></div><br><br><hr size=1>"
1930 "<p><span id=\"requestby\">Request by ";
1992 XrdHttpReq::ReturnGetHeaders() {
1993 std::string responseHeader;
1999 if (!responseHeader.empty()) {
2000 responseHeader +=
"\r\n";
2003 responseHeader += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2015 if (m_transfer_encoding_chunked && m_trailer_headers) {
2016 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2018 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2026 if (uranges.size() != 1)
2031 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2034 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2036 if (!responseHeader.empty()) {
2038 s += responseHeader.
c_str();
2041 if (m_transfer_encoding_chunked && m_trailer_headers) {
2042 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2044 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2051 for (
auto &ur : uranges) {
2052 cnt += ur.end - ur.start + 1;
2057 (
char *)
"123456").size();
2061 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2067 if (m_transfer_encoding_chunked && m_trailer_headers) {
2068 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2070 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2077 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2079 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2080 mapXrdErrorToHttpStatus();
2085 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2094 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2099 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2106 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2113 <<
" stat=" << (
char *)
iovP[0].iov_base);
2116 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2125 prot->SendSimpleResp(200, NULL,
"Accept-Ranges: bytes", NULL,
filesize,
keepalive);
2130 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2133 return ret_keepalive ? 1 : -1;
2136 std::string response_headers;
2137 int response = PostProcessChecksum(response_headers);
2138 if (-1 == response) {
2141 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2142 response_headers +=
"Accept-Ranges: bytes";
2143 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2146 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2168 if (
iovP[1].iov_len > 1) {
2170 <<
" stat=" << (
char *)
iovP[1].iov_base);
2173 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2194 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2195 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2204 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2205 httpStatusText.c_str(), httpStatusText.length(),
false);
2218 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2219 httpStatusText.c_str(), httpStatusText.length(),
false);
2226 return PostProcessListing(final_);
2236 httpStatusText = rrerror.
errMsg;
2239 if (m_transfer_encoding_chunked && m_trailer_headers) {
2240 if (prot->ChunkRespHeader(0))
2243 const std::string crlf =
"\r\n";
2244 std::stringstream ss;
2245 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2247 const auto header = ss.str();
2248 if (prot->SendData(header.c_str(), header.size()))
2251 if (prot->ChunkRespFooter())
2255 if (rrerror)
return -1;
2262 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2269 if (prot->ChunkRespHeader(0))
2272 const std::string crlf =
"\r\n";
2273 std::stringstream ss;
2274 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2276 const auto header = ss.str();
2277 if (prot->SendData(header.c_str(), header.size()))
2280 if (prot->ChunkRespFooter())
2290 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2293 getReadResponse(received);
2297 rc = sendReadResponseSingleRange(received);
2299 rc = sendReadResponsesMultiRanges(received);
2320 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2321 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2329 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2332 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2351 if (m_transfer_encoding_chunked) {
2352 m_current_chunk_offset += l;
2356 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2363 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2366 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2367 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2388 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2389 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2404 <<
" stat=" << (
char *)
iovP[0].iov_base);
2407 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2419 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2422 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2423 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2435 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2436 httpStatusText.c_str(), httpStatusText.length(),
false);
2454 <<
" stat=" << (
char *)
iovP[0].iov_base);
2457 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2463 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2468 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2496 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2497 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2499 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2503 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2504 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2506 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2511 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2521 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";
2524 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2538 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2542 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2544 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2545 strncpy(entry, (
char *) startp, endp - startp);
2546 entry[endp - startp] = 0;
2553 <<
" stat=" << endp);
2556 sscanf(endp,
"%ld %lld %ld %ld",
2564 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2584 if (*p.rbegin() !=
'/') p +=
"/";
2588 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2612 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2613 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2615 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2619 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2620 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2622 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2625 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2633 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2634 if (pp) startp = pp+1;
2645 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";
2648 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2668 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2670 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2671 httpStatusText.c_str(), httpStatusText.length(),
false);
2676 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2684 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2688 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2701 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2702 httpStatusText.c_str(), httpStatusText.length(),
false);
2717 TRACE(REQ,
" XrdHttpReq request ended.");
2759 m_transfer_encoding_chunked =
false;
2760 m_current_chunk_size = -1;
2761 m_current_chunk_offset = 0;
2763 m_trailer_headers =
false;
2764 m_status_trailer =
false;
2798 void XrdHttpReq::getfhandle() {
2801 TRACEI(REQ,
"fhandle:" <<
2815 for (
int i = 0; i <
iovN; i++) {
2817 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2819 len = ntohl(l->
rlen);
2832 for (
int i = 0; i <
iovN; i++) {
2833 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2838 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2840 if (received.size() == 0) {
2856 std::string st_header;
2857 std::string fin_header;
2864 std::vector<rinfo> rvec;
2867 rvec.reserve(received.size());
2869 for(
const auto &rcv: received) {
2878 rentry.start = start;
2879 rentry.finish = finish;
2888 rentry.st_header = s;
2889 sum_len += s.size();
2892 sum_len += rcv.size;
2896 rentry.fin_header = s;
2897 sum_len += s.size();
2900 rvec.push_back(rentry);
2905 if (m_transfer_encoding_chunked && m_trailer_headers) {
2906 prot->ChunkRespHeader(sum_len);
2910 for(
const auto &rentry: rvec) {
2913 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2914 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2920 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2924 if (rentry.finish) {
2925 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2932 if (m_transfer_encoding_chunked && m_trailer_headers) {
2933 prot->ChunkRespFooter();
2939 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2942 if (received.size() == 0) {
2952 for(
const auto &rcv: received) {
2961 if (m_transfer_encoding_chunked && m_trailer_headers) {
2962 prot->ChunkRespHeader(sum);
2964 for(
const auto &rcv: received) {
2965 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2967 if (m_transfer_encoding_chunked && m_trailer_headers) {
2968 prot->ChunkRespFooter();
struct ClientCloseRequest close
struct ClientSetRequest set
struct ClientMkdirRequest mkdir
struct ClientDirlistRequest dirlist
struct ClientReadVRequest readv
struct ClientOpenRequest open
struct ClientRequestHdr header
struct ClientRmRequest rm
struct ClientReadRequest read
struct ClientMvRequest mv
struct ClientRmdirRequest rmdir
struct ClientStatRequest stat
struct ClientWriteRequest write
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
std::string ISOdatetime(time_t t)
void trim(std::string &str)
Main request/response class, handling the logical status of the communication.
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)
char * unquote(char *str)
bool Fromhexdigest(const unsigned char *input, int length, unsigned char *out)
void calcHashes(char *hash, const char *fn, kXR_int16 request, XrdSecEntity *secent, time_t tim, const char *key)
char * escapeXML(const char *str)
char * mystrchrnul(const char *s, int c)
char * quote(const char *str)
Utility functions for XrdHTTP.
std::vector< XrdOucIOVec2 > XrdHttpIOList
std::string obfuscateAuth(const std::string &input)
XrdHttpChecksumRawPtr getChecksumToRun(const std::string &userDigest) const
bool needsBase64Padding() const
std::string getXRootDConfigDigestName() const
std::string getHttpName() const
virtual int ProcessReq(XrdHttpExtReq &)=0
static char * secretkey
The key used to calculate the url hashes.
static kXR_int32 myRole
Our role.
static XrdNetPMark * pmarkHandle
Packet marking handler pointer (assigned from the environment during the Config() call)
XrdXrootd::Bridge * Bridge
The Bridge that we use to exercise the xrootd internals.
static char * staticredir
static XrdHttpChecksumHandler cksumHandler
int doChksum(const XrdOucString &fname)
Perform a checksum request.
static XrdOucHash< StaticPreloadInfo > * staticpreload
static std::map< std::string, std::string > hdr2cgimap
Rules that turn HTTP headers to cgi tokens in the URL, for internal comsumption.
XrdLink * Link
The link we are bound to.
int doStat(char *fname)
Perform a Stat request.
static bool isdesthttps
True if the redirections must be towards https targets.
static char * listredir
Url to redirect to in the case a listing is requested.
static bool listdeny
If true, any form of listing is denied.
XrdSecEntity SecEntity
Authentication area.
static bool embeddedstatic
If true, use the embedded css and icons.
void reset()
resets this handler
const XrdHttpIOList & NextReadList()
return XrdHttpIOList for sending to read or readv
void ParseContentRange(const char *const line)
parse the line after a "Range: " http request header
int SetFilesize(const off_t sz)
sets the filesize, used during resolving and issuing range requests
void NotifyError()
Force handler to enter error state.
bool isFullFile()
indicates when there were no valid Range head ranges supplied
std::vector< UserRange > UserRangeList
int NotifyReadResult(const ssize_t ret, const UserRange **const urp, bool &start, bool &allend)
Advance internal counters concerning received bytes.
const Error & getError() const
return the Error object
bool isSingleRange()
indicates a single range (implied whole file, or single range) or empty file
const UserRangeList & ListResolvedRanges()
return resolved (i.e. obsolute start and end) byte ranges desired
int reqstate
State machine to talk to the bridge.
int ReqReadV(const XrdHttpIOList &cl)
Prepare the buffers for sending a readv request.
int parseBody(char *body, long long len)
Parse the body of a request, assuming that it's XML and that it's entirely in memory.
std::vector< readahead_list > ralist
std::string destination
The destination field specified in the req.
XrdOucString resource
The resource specified by the request, stripped of opaque data.
bool headerok
Tells if we have finished reading the header.
std::string m_digest_header
The computed digest for the HTTP response header.
std::string stringresp
If we want to give a string as a response, we compose it here.
XResponseType xrdresp
The last response data we got.
ReqType request
The request we got.
long long writtenbytes
In a long write, we track where we have arrived.
XrdOucEnv * opaque
The opaque data, after parsing.
const struct iovec * iovP
The latest data chunks got from the xrd layer. These are valid only inside the callbacks!
std::string m_req_digest
The requested digest type.
XrdOucString resourceplusopaque
The resource specified by the request, including all the opaque data.
virtual bool Data(XrdXrootd::Bridge::Context &info, const struct iovec *iovP, int iovN, int iovL, bool final)
std::string hdr2cgistr
Additional opaque info that may come from the hdr2cgi directive.
virtual bool Done(XrdXrootd::Bridge::Context &info)
the result context
std::string host
The host field specified in the req.
int parseFirstLine(char *line, int len)
Parse the first line of the header.
int parseLine(char *line, int len)
Parse the header.
std::string buildPartialHdrEnd(char *token)
Build the closing part for a multipart response.
XrdHttpChecksumHandler::XrdHttpChecksumRawPtr m_req_cksum
The checksum that was ran for this request.
bool m_appended_hdr2cgistr
void appendOpaque(XrdOucString &s, XrdSecEntity *secent, char *hash, time_t tnow)
XrdOucString m_resource_with_digest
virtual bool Redir(XrdXrootd::Bridge::Context &info, int port, const char *hname)
virtual int File(XrdXrootd::Bridge::Context &info, int dlen)
std::map< std::string, std::string > allheaders
void addCgi(const std::string &key, const std::string &value)
ClientRequest xrdreq
The last issued xrd request, often pending.
std::string buildPartialHdr(long long bytestart, long long byteend, long long filesize, char *token)
Build a partial header for a multipart response.
XrdHttpReadRangeHandler readRangeHandler
Tracking the next ranges of data to read during GET.
virtual bool Error(XrdXrootd::Bridge::Context &info, int ecode, const char *etext)
char * ID
Pointer to the client's link identity.
static const int minTotID
static const int maxTotID
static bool Import(const char *var, char *&val)
char * Get(const char *varname)
const char * c_str() const
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)
static void trim(std::string &str)
char * vorg
Entity's virtual organization(s)
int credslen
Length of the 'creds' data.
char * creds
Raw entity credentials or cert.
char * grps
Entity's group name(s)
char * name
Entity's name.
char * role
Entity's role(s)
char * endorsements
Protocol specific endorsements.
char * moninfo
Information for monitoring.
char * host
Entity's host name dnr dependent.
virtual int Send(const struct iovec *headP, int headN, const struct iovec *tailP, int tailN)
virtual int setSF(kXR_char *fhandle, bool seton=false)=0
virtual bool Run(const char *xreqP, char *xdataP=0, int xdataL=0)=0