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 mapXrdErrorToHttpStatus();
1162 sendFooterError(
"Could not run close request on the bridge");
1173 prot->SendSimpleResp(503, NULL, NULL, (
char *)
"Listings are disabled.", 0,
false);
1187 prot->SendSimpleResp(302, NULL, (
char *) s.
c_str(), 0, 0,
false);
1198 l = res.length() + 1;
1202 mapXrdErrorToHttpStatus();
1203 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1204 sendFooterError(
"Could not run listing request on the bridge");
1217 auto retval = ReturnGetHeaders();
1230 if ( readChunkList.empty() )
1238 TRACEI(REQ,
" Failed to run close request on the bridge.");
1252 if ( readChunkList.size() == 1 ) {
1264 offs = readChunkList[0].offset;
1265 l = readChunkList[0].size;
1273 if (prot->ishttps || (m_transfer_encoding_chunked && m_trailer_headers) ||
1276 TRACE(REQ,
" XrdBridge::SetSF(false) failed.");
1285 TRACE(ALL,
" Data sizes mismatch.");
1289 TRACE(ALL,
" No more bytes to send.");
1296 httpStatusCode = 416;
1297 httpStatusText =
"Range Not Satisfiable";
1298 std::stringstream ss;
1299 ss <<
"Requested range " << l <<
"@" << offs <<
" is past the end of file (" <<
filesize <<
")";
1300 sendFooterError(ss.str());
1305 mapXrdErrorToHttpStatus();
1306 sendFooterError(
"Could not run read request on the bridge");
1315 mapXrdErrorToHttpStatus();
1316 sendFooterError(
"Could not run ReadV request on the bridge");
1346 if (! XrdHttpProtocol::usingEC)
1352 prot->SendSimpleResp(404, NULL, NULL, (
char *)
"Could not run request.", 0,
keepalive);
1367 if (m_transfer_encoding_chunked) {
1368 if (m_current_chunk_size == m_current_chunk_offset) {
1371 if (prot->BuffUsed() < 2)
return 1;
1372 if (prot->myBuffStart[0] !=
'\r' || prot->myBuffStart[1] !=
'\n') {
1373 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid trailing chunk encoding.", 0,
keepalive);
1376 prot->BuffConsume(2);
1377 if (m_current_chunk_size == 0) {
1381 m_transfer_encoding_chunked =
false;
1385 m_current_chunk_size = -1;
1386 m_current_chunk_offset = 0;
1388 if (!prot->BuffUsed())
return 1;
1390 if (-1 == m_current_chunk_size) {
1394 bool found_newline =
false;
1401 long long max_chunk_size_chars = std::min(
static_cast<long long>(prot->BuffUsed()),
static_cast<long long>(13));
1402 for (; idx < max_chunk_size_chars; idx++) {
1403 if (prot->myBuffStart[idx] ==
'\n') {
1404 found_newline =
true;
1410 if (found_newline && ((idx == 0) || prot->myBuffStart[idx-1] !=
'\r')) {
1411 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1412 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Start of chunk should have had a length, followed by a CRLF.");
1415 if (found_newline) {
1416 char *endptr = NULL;
1417 std::string line_contents(prot->myBuffStart, idx);
1418 long long chunk_contents = strtol(line_contents.c_str(), &endptr, 16);
1420 if (*endptr !=
';' && *endptr !=
'\r') {
1421 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Invalid chunked encoding", 0,
false);
1422 TRACE(REQ,
"XrdHTTP PUT: Sending invalid chunk encoding. Chunk size was not followed by a ';' or CR." << __LINE__);
1425 m_current_chunk_size = chunk_contents;
1426 m_current_chunk_offset = 0;
1427 prot->BuffConsume(idx + 1);
1428 TRACE(REQ,
"XrdHTTP PUT: next chunk from client will be " << m_current_chunk_size <<
" bytes");
1435 if (m_current_chunk_size == 0) {
1444 long long chunk_bytes_remaining = m_current_chunk_size - m_current_chunk_offset;
1445 long long bytes_to_write = std::min(
static_cast<long long>(prot->BuffUsed()),
1446 chunk_bytes_remaining);
1451 TRACEI(REQ,
"XrdHTTP PUT: Writing chunk of size " << bytes_to_write <<
" starting with '" << *(prot->myBuffStart) <<
"'" <<
" with " << chunk_bytes_remaining <<
" bytes remaining in the chunk");
1452 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_write)) {
1453 mapXrdErrorToHttpStatus();
1454 sendFooterError(
"Could not run write request on the bridge");
1459 return (prot->BuffUsed() > chunk_bytes_remaining) ? 0 : 1;
1469 long long bytes_to_read = std::min(
static_cast<long long>(prot->BuffUsed()),
1475 TRACEI(REQ,
"Writing " << bytes_to_read);
1476 if (!prot->
Bridge->
Run((
char *) &
xrdreq, prot->myBuffStart, bytes_to_read)) {
1477 mapXrdErrorToHttpStatus();
1478 sendFooterError(
"Could not run write request on the bridge");
1501 mapXrdErrorToHttpStatus();
1502 sendFooterError(
"Could not run close request on the bridge");
1518 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);
1521 return ret_keepalive ? 1 : -1;
1543 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1563 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rmdir request.", 0,
false);
1577 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run rm request.", 0,
false);
1593 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported yet.", 0,
false);
1608 TRACE(REQ,
"Reading request body " <<
length <<
" bytes.");
1613 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Error in getting the PROPFIND request body.", 0,
false);
1618 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Invalid depth value.", 0,
false);
1637 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1669 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1695 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1716 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Cannot parse destination url.", 0,
false);
1721 strcpy(buf2,
host.c_str());
1722 char *pos = strchr(buf2,
':');
1723 if (pos) *pos =
'\0';
1731 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Only in-place renaming is supported for MOVE.", 0,
false);
1745 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Could not run request.", 0,
false);
1755 prot->SendSimpleResp(501, NULL, NULL, (
char *)
"Request not supported.", 0,
false);
1766 XrdHttpReq::PostProcessChecksum(std::string &digest_header) {
1769 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
"Failed to determine checksum", 0,
false);
1774 <<
reinterpret_cast<char *
>(
iovP[0].iov_base) <<
"="
1775 <<
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base));
1778 char *digest_value =
reinterpret_cast<char *
>(
iovP[
iovN-1].iov_base);
1779 if (convert_to_base64) {
1780 size_t digest_length = strlen(digest_value);
1781 unsigned char *digest_binary_value = (
unsigned char *)malloc(digest_length);
1782 if (!
Fromhexdigest(
reinterpret_cast<unsigned char *
>(digest_value), digest_length, digest_binary_value)) {
1783 prot->SendSimpleResp(500, NULL, NULL, (
char *)
"Failed to convert checksum hexdigest to base64.", 0,
false);
1784 free(digest_binary_value);
1787 char *digest_base64_value = (
char *)malloc(digest_length + 1);
1789 Tobase64(digest_binary_value, digest_length/2, digest_base64_value);
1790 free(digest_binary_value);
1791 digest_value = digest_base64_value;
1794 digest_header =
"Digest: ";
1796 digest_header +=
"=";
1797 digest_header += digest_value;
1798 if (convert_to_base64) {free(digest_value);}
1801 prot->SendSimpleResp(httpStatusCode, NULL, NULL, httpStatusText.c_str(), httpStatusText.length(),
false);
1807 XrdHttpReq::PostProcessListing(
bool final_) {
1810 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
1811 httpStatusText.c_str(), httpStatusText.length(),
false);
1817 stringresp =
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
1818 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1820 "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"/>\n"
1821 "<link rel=\"stylesheet\" type=\"text/css\" href=\"/static/css/xrdhttp.css\"/>\n"
1822 "<link rel=\"icon\" type=\"image/png\" href=\"/static/icons/xrdhttp.ico\"/>\n";
1843 "<th class=\"mode\">Mode</th>"
1844 "<th class=\"flags\">Flags</th>"
1845 "<th class=\"size\">Size</th>"
1846 "<th class=\"datetime\">Modified</th>"
1847 "<th class=\"name\">Name</th>"
1853 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
1856 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
1858 if ((endp = (
char *) strchr((
const char*) startp,
'\n'))) {
1859 strncpy(entry, (
char *) startp, endp - startp);
1860 entry[endp - startp] = 0;
1867 <<
" stat=" << endp);
1870 sscanf(endp,
"%ld %lld %ld %ld",
1876 strcpy(entry, (
char *) startp);
1878 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
1880 std::string p =
"<tr>"
1881 "<td class=\"mode\">";
1902 p +=
"<td class=\"mode\">" +
itos(e.
flags) +
"</td>"
1903 "<td class=\"size\">" +
itos(e.
size) +
"</td>"
1905 "<td class=\"name\">"
1913 if (!p.empty() && p[p.size() - 1] !=
'/')
1921 p += e.
path +
"\">";
1926 p +=
"</a></td></tr>";
1932 char *pp = (
char *)strchr((
const char *)endp,
'\n');
1933 if (pp) startp = pp+1;
1942 stringresp +=
"</table></div><br><br><hr size=1>"
1943 "<p><span id=\"requestby\">Request by ";
2005 XrdHttpReq::ReturnGetHeaders() {
2006 std::string responseHeader;
2012 if (!responseHeader.empty()) {
2013 responseHeader +=
"\r\n";
2016 responseHeader += std::string(
"Age: ") + std::to_string(object_age < 0 ? 0 : object_age);
2028 if (m_transfer_encoding_chunked && m_trailer_headers) {
2029 prot->StartChunkedResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), -1,
keepalive);
2031 prot->SendSimpleResp(200, NULL, responseHeader.empty() ? NULL : responseHeader.c_str(), NULL,
filesize,
keepalive);
2039 if (uranges.size() != 1)
2044 const off_t cnt = uranges[0].end - uranges[0].start + 1;
2047 sprintf(buf,
"%lld-%lld/%lld", (
long long int)uranges[0].start, (
long long int)uranges[0].end,
filesize);
2049 if (!responseHeader.empty()) {
2051 s += responseHeader.
c_str();
2054 if (m_transfer_encoding_chunked && m_trailer_headers) {
2055 prot->StartChunkedResp(206, NULL, (
char *)s.
c_str(), -1,
keepalive);
2057 prot->SendSimpleResp(206, NULL, (
char *)s.
c_str(), NULL, cnt,
keepalive);
2064 for (
auto &ur : uranges) {
2065 cnt += ur.end - ur.start + 1;
2070 (
char *)
"123456").size();
2074 std::string header =
"Content-Type: multipart/byteranges; boundary=123456";
2080 if (m_transfer_encoding_chunked && m_trailer_headers) {
2081 prot->StartChunkedResp(206, NULL, header.c_str(), -1,
keepalive);
2083 prot->SendSimpleResp(206, NULL, header.c_str(), NULL, cnt,
keepalive);
2090 int XrdHttpReq::PostProcessHTTPReq(
bool final_) {
2092 TRACEI(REQ,
"PostProcessHTTPReq req: " <<
request <<
" reqstate: " <<
reqstate <<
" final_:" << final_);
2093 mapXrdErrorToHttpStatus();
2098 prot->SendSimpleResp(500,
nullptr,
nullptr,
"Could not set user agent.", 0,
false);
2107 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 1", 0,
false);
2112 prot->SendSimpleResp(400, NULL, NULL, (
char *)
"Request malformed 2", 0,
false);
2119 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
false);
2126 <<
" stat=" << (
char *)
iovP[0].iov_base);
2129 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2138 prot->SendSimpleResp(200, NULL,
"Accept-Ranges: bytes", NULL,
filesize,
keepalive);
2143 prot->SendSimpleResp(httpStatusCode, NULL, NULL, NULL, 0,
keepalive);
2146 return ret_keepalive ? 1 : -1;
2149 std::string response_headers;
2150 int response = PostProcessChecksum(response_headers);
2151 if (-1 == response) {
2154 if (!response_headers.empty()) {response_headers +=
"\r\n";}
2155 response_headers +=
"Accept-Ranges: bytes";
2156 prot->SendSimpleResp(200, NULL, response_headers.c_str(), NULL,
filesize,
keepalive);
2159 prot->SendSimpleResp(500, NULL, NULL,
"Underlying filesystem failed to calculate checksum.", 0,
false);
2181 if (
iovP[1].iov_len > 1) {
2183 <<
" stat=" << (
char *)
iovP[1].iov_base);
2186 sscanf((
const char *)
iovP[1].iov_base,
"%ld %lld %ld %ld",
2207 TRACEI(ALL,
"GET returned no STAT information. Internal error?");
2208 prot->SendSimpleResp(500, NULL, NULL,
"Storage system did not return stat info.", 0,
false);
2217 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2218 httpStatusText.c_str(), httpStatusText.length(),
false);
2231 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2232 httpStatusText.c_str(), httpStatusText.length(),
false);
2239 return PostProcessListing(final_);
2249 httpStatusText = rrerror.
errMsg;
2252 if (m_transfer_encoding_chunked && m_trailer_headers) {
2253 if (prot->ChunkRespHeader(0))
2256 const std::string crlf =
"\r\n";
2257 std::stringstream ss;
2258 ss <<
"X-Transfer-Status: " << httpStatusCode <<
": " << httpStatusText << crlf;
2260 const auto header = ss.str();
2261 if (prot->SendData(header.c_str(), header.size()))
2264 if (prot->ChunkRespFooter())
2268 if (rrerror)
return -1;
2275 sendFooterError(
"");
2280 TRACEI(REQ,
"Got data vectors to send:" <<
iovN);
2283 getReadResponse(received);
2287 rc = sendReadResponseSingleRange(received);
2289 rc = sendReadResponsesMultiRanges(received);
2310 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2311 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2319 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2322 prot->SendSimpleResp(100, NULL, NULL, 0, 0,
keepalive);
2341 if (m_transfer_encoding_chunked) {
2342 m_current_chunk_offset += l;
2346 prot->ResumeBytes = std::min(
length -
writtenbytes, (
long long) prot->BuffAvailable());
2353 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2356 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2357 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2378 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2379 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2394 <<
" stat=" << (
char *)
iovP[0].iov_base);
2397 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2409 prot->SendSimpleResp(200, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2412 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2413 httpStatusText.c_str(), httpStatusText.length(),
keepalive);
2425 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2426 httpStatusText.c_str(), httpStatusText.length(),
false);
2444 <<
" stat=" << (
char *)
iovP[0].iov_base);
2447 sscanf((
const char *)
iovP[0].iov_base,
"%ld %lld %ld %ld",
2453 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2458 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2486 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2487 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2489 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2493 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2494 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2496 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2501 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2511 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";
2514 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2528 char *startp = (
char *)
iovP[0].iov_base, *endp = 0;
2532 while ( (
size_t)(startp - (
char *)
iovP[0].iov_base) < (size_t)(
iovP[0].iov_len - 1) ) {
2534 if ((endp = (
char *)
mystrchrnul((
const char*) startp,
'\n'))) {
2535 strncpy(entry, (
char *) startp, endp - startp);
2536 entry[endp - startp] = 0;
2543 <<
" stat=" << endp);
2546 sscanf(endp,
"%ld %lld %ld %ld",
2554 if (e.
path.length() && (e.
path !=
".") && (e.
path !=
"..")) {
2574 if (*p.rbegin() !=
'/') p +=
"/";
2578 stringresp +=
"<D:response xmlns:lp1=\"DAV:\" xmlns:lp2=\"http://apache.org/dav/props/\" xmlns:lp3=\"LCGDM:\">\n";
2602 stringresp +=
"<lp1:resourcetype><D:collection/></lp1:resourcetype>\n";
2603 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2605 stringresp +=
"<lp1:iscollection>0</lp1:iscollection>\n";
2609 stringresp +=
"<lp1:executable>T</lp1:executable>\n";
2610 stringresp +=
"<lp1:iscollection>1</lp1:iscollection>\n";
2612 stringresp +=
"<lp1:executable>F</lp1:executable>\n";
2615 stringresp +=
"</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n</D:propstat>\n</D:response>\n";
2623 char *pp = (
char *)strchr((
const char *)endp,
'\n');
2624 if (pp) startp = pp+1;
2635 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";
2638 prot->SendSimpleResp(207, (
char *)
"Multi-Status", (
char *)
"Content-Type: text/xml; charset=\"utf-8\"",
2658 prot->SendSimpleResp(405, NULL, NULL, (
char *)
"Method is not allowed; resource already exists.", 0,
false);
2660 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2661 httpStatusText.c_str(), httpStatusText.length(),
false);
2666 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2674 prot->SendSimpleResp(httpStatusCode, NULL, NULL, (
char *)
etext.c_str(), 0,
false);
2678 prot->SendSimpleResp(201, NULL, NULL, (
char *)
":-)", 0,
keepalive);
2691 prot->SendSimpleResp(httpStatusCode, NULL, NULL,
2692 httpStatusText.c_str(), httpStatusText.length(),
false);
2706 XrdHttpReq::sendFooterError(
const std::string &extra_text) {
2707 if (m_transfer_encoding_chunked && m_trailer_headers && m_status_trailer) {
2715 if (prot->ChunkRespHeader(0))
2718 std::stringstream ss;
2719 ss << httpStatusCode <<
": " << httpStatusText;
2720 if (!extra_text.empty())
2721 ss <<
": " << extra_text;
2725 const auto header =
"X-Transfer-Status: " + ss.str();
2726 if (prot->SendData(header.c_str(), header.size()))
2729 prot->ChunkRespFooter();
2731 TRACEI(REQ, httpStatusCode <<
": " << httpStatusText << (extra_text.empty() ?
"" : (
": " + extra_text)));
2737 TRACE(REQ,
" XrdHttpReq request ended.");
2779 m_transfer_encoding_chunked =
false;
2780 m_current_chunk_size = -1;
2781 m_current_chunk_offset = 0;
2783 m_trailer_headers =
false;
2784 m_status_trailer =
false;
2818 void XrdHttpReq::getfhandle() {
2821 TRACEI(REQ,
"fhandle:" <<
2835 for (
int i = 0; i <
iovN; i++) {
2837 for (p = (
char *)
iovP[i].iov_base; p < (
char *)
iovP[i].iov_base +
iovP[i].iov_len;) {
2839 len = ntohl(l->
rlen);
2852 for (
int i = 0; i <
iovN; i++) {
2853 received.emplace_back((
char*)
iovP[i].iov_base, -1,
iovP[i].iov_len);
2858 int XrdHttpReq::sendReadResponsesMultiRanges(
const XrdHttpIOList &received) {
2860 if (received.size() == 0) {
2876 std::string st_header;
2877 std::string fin_header;
2884 std::vector<rinfo> rvec;
2887 rvec.reserve(received.size());
2889 for(
const auto &rcv: received) {
2898 rentry.start = start;
2899 rentry.finish = finish;
2908 rentry.st_header = s;
2909 sum_len += s.size();
2912 sum_len += rcv.size;
2916 rentry.fin_header = s;
2917 sum_len += s.size();
2920 rvec.push_back(rentry);
2925 if (m_transfer_encoding_chunked && m_trailer_headers) {
2926 prot->ChunkRespHeader(sum_len);
2930 for(
const auto &rentry: rvec) {
2933 TRACEI(REQ,
"Sending multipart: " << rentry.ur->start <<
"-" << rentry.ur->end);
2934 if (prot->SendData((
char *) rentry.st_header.c_str(), rentry.st_header.size())) {
2940 if (prot->SendData((
char *) rentry.ci->data, rentry.ci->size)) {
2944 if (rentry.finish) {
2945 if (prot->SendData((
char *) rentry.fin_header.c_str(), rentry.fin_header.size())) {
2952 if (m_transfer_encoding_chunked && m_trailer_headers) {
2953 prot->ChunkRespFooter();
2959 int XrdHttpReq::sendReadResponseSingleRange(
const XrdHttpIOList &received) {
2962 if (received.size() == 0) {
2972 for(
const auto &rcv: received) {
2981 if (m_transfer_encoding_chunked && m_trailer_headers) {
2982 prot->ChunkRespHeader(sum);
2984 for(
const auto &rcv: received) {
2985 if (prot->SendData((
char *) rcv.data, rcv.size))
return -1;
2987 if (m_transfer_encoding_chunked && m_trailer_headers) {
2988 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