0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-26 15:33:54 +01:00

ircd::http: Massage additional header related into response interface/stack.

This commit is contained in:
Jason Volk 2017-12-24 14:25:09 -07:00
parent c20a2927d0
commit 36142718f6
4 changed files with 138 additions and 92 deletions

View file

@ -32,6 +32,7 @@ namespace ircd::http
struct error;
struct line;
struct query;
struct header;
struct headers;
struct content;
struct request;
@ -39,6 +40,14 @@ namespace ircd::http
string_view status(const enum code &);
enum code status(const string_view &);
void writeline(stream_buffer &);
void writeline(stream_buffer &, const stream_buffer::closure &);
void write(stream_buffer &out, const header &);
void write(stream_buffer &out, const vector_view<const header> &);
size_t serialized(const vector_view<const header> &);
std::string strung(const vector_view<const header> &);
}
//
@ -92,8 +101,10 @@ struct ircd::http::error
{
enum code code;
std::string content;
std::string headers;
error(const enum code &, std::string content = {});
error(const enum code &, std::string content = {}, std::string headers = {});
error(const enum code &, std::string content, const vector_view<const header> &);
};
/// Represents a single \r\n delimited line used in HTTP.
@ -108,17 +119,11 @@ struct ircd::http::line
{
struct request;
struct response;
struct header;
using string_view::string_view;
line(parse::capstan &);
};
namespace ircd::http
{
using header = line::header;
}
/// Represents a 'request line' or the first line a client sends to a server.
///
/// This is a dual-use class. For HTTP clients, one may simply connect the
@ -199,7 +204,7 @@ struct ircd::http::query::string
/// components of the std::pair. Those receiving headers can pass the ctor an
/// ircd::http::line which will construct the pair using the formal grammars.
///
struct ircd::http::line::header
struct ircd::http::header
:std::pair<string_view, string_view>
{
bool operator<(const string_view &s) const { return iless(first, s); }
@ -219,7 +224,6 @@ struct ircd::http::line::header
struct ircd::http::headers
:string_view
{
using header = line::header;
using closure = std::function<void (const header &)>;
headers(parse::capstan &, const closure & = {});
@ -255,7 +259,6 @@ struct ircd::http::request
struct content;
using proffer = std::function<void (const head &)>;
using header = line::header;
// send
request(stream_buffer &,
@ -316,14 +319,13 @@ struct ircd::http::response
using write_closure = std::function<void (const ilist<const_buffer> &)>;
using proffer = std::function<void (const head &)>;
using header = line::header;
// send
response(stream_buffer &,
const code & = code::OK,
const size_t &content_length = 0,
const string_view &content_type = {},
const string_view &cache_control = {},
const string_view &headers = {},
const vector_view<const header> & = {},
const bool &termination = true);

View file

@ -130,7 +130,8 @@ struct ircd::resource::request::object
struct ircd::resource::response
{
response(client &, const string_view &str, const string_view &content_type, const http::code & = http::OK);
response(client &, const string_view &str, const string_view &content_type, const http::code &, const vector_view<const http::header> &);
response(client &, const string_view &str, const string_view &content_type, const http::code & = http::OK, const string_view &headers = {});
response(client &, const json::object &str, const http::code & = http::OK);
response(client &, const json::array &str, const http::code & = http::OK);
response(client &, const json::members & = {}, const http::code & = http::OK);

View file

@ -49,10 +49,6 @@ namespace ircd::http
struct parser extern const parser;
extern const std::unordered_map<ircd::http::code, ircd::string_view> reason;
size_t serialized(const vector_view<const line::header> &headers);
void writeline(stream_buffer &, const stream_buffer::closure &);
void writeline(stream_buffer &);
}
BOOST_FUSION_ADAPT_STRUCT
@ -64,9 +60,9 @@ BOOST_FUSION_ADAPT_STRUCT
BOOST_FUSION_ADAPT_STRUCT
(
ircd::http::line::header,
( decltype(ircd::http::line::header::first), first )
( decltype(ircd::http::line::header::second), second )
ircd::http::header,
( decltype(ircd::http::header::first), first )
( decltype(ircd::http::header::second), second )
)
BOOST_FUSION_ADAPT_STRUCT
@ -164,7 +160,7 @@ struct ircd::http::grammar
rule<string_view> head_key { raw[+(char_ - (illegal | ws | colon))] ,"head key" };
rule<string_view> head_val { string ,"head value" };
rule<line::header> header { head_key >> *ws >> colon >> *ws >> head_val ,"header" };
rule<http::header> header { head_key >> *ws >> colon >> *ws >> head_val ,"header" };
rule<unused_type> headers { (header % (*ws >> CRLF)) ,"headers" };
rule<> query_terminator { equal | question | ampersand | pound ,"query terminator" };
@ -314,18 +310,7 @@ ircd::http::request::request(stream_buffer &out,
};
});
for(const auto &header : headers)
{
assert(!header.first.empty());
assert(!header.second.empty());
writeline(out, [&header](const mutable_buffer &out) -> size_t
{
return fmt::sprintf
{
out, "%s: %s", header.first, header.second
};
});
}
write(out, headers);
if(termination)
writeline(out);
@ -383,8 +368,8 @@ ircd::http::response::response(stream_buffer &out,
const code &code,
const size_t &content_length,
const string_view &content_type,
const string_view &cache_control,
const vector_view<const header> &headers,
const string_view &headers_string,
const vector_view<const header> &headers_vector,
const bool &termination)
{
writeline(out, [&code](const mutable_buffer &out) -> size_t
@ -414,25 +399,7 @@ ircd::http::response::response(stream_buffer &out,
};
});
if((code >= 200 && code < 300) || (code >= 403 && code <= 405) || (code >= 300 && code < 400))
writeline(out, [&cache_control](const mutable_buffer &out) -> size_t
{
return fmt::sprintf
{
out, "Cache-Control: %s", cache_control?: "no-cache"
};
});
const bool has_transfer_encoding
{
std::any_of(std::begin(headers), std::end(headers), []
(const auto &header)
{
return iequals(header.first, "transfer-encoding"s);
})
};
if((content_length && code != NO_CONTENT) || has_transfer_encoding)
if(code != NO_CONTENT && content_type && content_length)
writeline(out, [&content_type](const mutable_buffer &out) -> size_t
{
return fmt::sprintf
@ -441,7 +408,7 @@ ircd::http::response::response(stream_buffer &out,
};
});
if(code != NO_CONTENT && !has_transfer_encoding)
if(code != NO_CONTENT && content_length != std::numeric_limits<size_t>::max())
writeline(out, [&content_length](const mutable_buffer &out) -> size_t
{
return fmt::sprintf
@ -450,18 +417,14 @@ ircd::http::response::response(stream_buffer &out,
};
});
for(const auto &header : headers)
{
assert(!header.first.empty());
assert(!header.second.empty());
writeline(out, [&header](const mutable_buffer &out) -> size_t
if(!headers_string.empty())
out([&headers_string](const mutable_buffer &out)
{
return fmt::sprintf
{
out, "%s: %s", header.first, header.second
};
return copy(out, headers_string);
});
}
if(!headers_vector.empty())
write(out, headers_vector);
if(termination)
writeline(out);
@ -477,7 +440,7 @@ ircd::http::response::chunked::chunked(const code &code,
user_headers.size() + 1
};
line::header headers[num_headers]
header headers[num_headers]
{
{ "Transfer-Encoding", "chunked" }
};
@ -647,9 +610,9 @@ ircd::http::headers::headers(parse::capstan &pc,
:string_view{[&pc, &c]
() -> string_view
{
line::header h{pc};
header h{pc};
const char *const &started{h.first.data()}, *stopped{started};
for(; !h.first.empty(); stopped = h.second.data() + h.second.size(), h = line::header{pc})
for(; !h.first.empty(); stopped = h.second.data() + h.second.size(), h = header{pc})
if(c)
c(h);
@ -658,7 +621,7 @@ ircd::http::headers::headers(parse::capstan &pc,
{
}
ircd::http::line::header::header(const line &line)
ircd::http::header::header(const line &line)
try
{
static const auto grammar
@ -846,6 +809,65 @@ ircd::http::parser::content_length(const string_view &str)
return ret;
}
std::string
ircd::http::strung(const vector_view<const header> &headers)
{
std::string ret(serialized(headers), char{});
stream_buffer out{ret};
write(out, headers);
assert(out.consumed() <= ret.size());
ret.resize(out.consumed());
assert(out.consumed() == ret.size());
return ret;
}
/// Indicates the buffer size required to write these headers. This size
/// may include room for a terminating null character which may be written
/// by write(headers). Only use write(headers) to know the actually written
/// string size (without null) not this.
size_t
ircd::http::serialized(const vector_view<const header> &headers)
{
// Because the write(header) functions use fmt::sprintf we have to
// indicate an extra space for a null string terminator to not overlof
static const size_t initial{1};
return std::accumulate(std::begin(headers), std::end(headers), initial, []
(auto &ret, const auto &pair)
{
// key : SP value CRLF
return ret += pair.first.size() + 1 + 1 + pair.second.size() + 2;
});
}
void
ircd::http::write(stream_buffer &out,
const vector_view<const header> &headers)
{
for(const auto &header : headers)
write(out, header);
}
void
ircd::http::write(stream_buffer &out,
const header &header)
{
if(header.second.empty())
return;
assert(!header.first.empty());
if(unlikely(header.first.empty()))
return;
writeline(out, [&header](const mutable_buffer &out) -> size_t
{
return fmt::sprintf
{
out, "%s: %s", header.first, header.second
};
});
}
/// Close over the user's closure to append a newline.
void
ircd::http::writeline(stream_buffer &write,
@ -875,22 +897,23 @@ ircd::http::writeline(stream_buffer &write)
});
}
size_t
ircd::http::serialized(const vector_view<const line::header> &headers)
ircd::http::error::error(const enum code &code,
std::string content,
const vector_view<const header> &headers)
:error
{
code, std::move(content), strung(headers)
}
{
return std::accumulate(std::begin(headers), std::end(headers), size_t{0}, []
(auto &ret, const auto &pair)
{
// key : SP value CRLF
return ret += pair.first.size() + 1 + 1 + pair.second.size() + 2;
});
}
ircd::http::error::error(const enum code &code,
std::string content)
std::string content,
std::string headers)
:ircd::error{generate_skip}
,code{code}
,content{std::move(content)}
,headers{std::move(headers)}
{
snprintf(buf, sizeof(buf), "%d %s", int(code), status(code).c_str());
}

View file

@ -62,15 +62,9 @@ try
}
catch(const http::error &e)
{
log::debug("client[%s] HTTP %s in %ld$us %s",
string(remote(client)),
e.what(),
client.request_timer.at<microseconds>().count(),
e.content);
resource::response
{
client, e.content, "text/html; charset=utf8", e.code
client, e.content, "text/html; charset=utf8", e.code, e.headers
};
switch(e.code)
@ -593,16 +587,41 @@ ircd::resource::response::response(client &client,
ircd::resource::response::response(client &client,
const string_view &content,
const string_view &content_type,
const http::code &code)
const http::code &code,
const vector_view<const http::header> &headers)
{
char buf[serialized(headers)];
const mutable_buffer mb{buf, sizeof(buf)};
stream_buffer sb{mb};
write(sb, headers);
response
{
client, content, content_type, code, sb.completed()
};
}
ircd::resource::response::response(client &client,
const string_view &content,
const string_view &content_type,
const http::code &code,
const string_view &headers)
{
const auto request_time
{
client.request_timer.at<microseconds>().count()
};
char rtime[64]; const auto rtime_len
const fmt::bsprintf<64> rtime
{
snprintf(rtime, sizeof(rtime), "%zdus", request_time)
"%zdus", request_time
};
const string_view cache_control
{
(code >= 200 && code < 300) ||
(code >= 403 && code <= 405) ||
(code >= 300 && code < 400)? "no-cache":
""
};
char head_buf[2048];
@ -613,11 +632,12 @@ ircd::resource::response::response(client &client,
code,
content.size(),
content_type,
string_view{}, // cache_control
headers,
{
{ "Access-Control-Allow-Origin", "*" }, //TODO: XXX
{ "X-IRCd-Request-Timer", string_view{rtime, size_t(rtime_len)} }
}
{ "Access-Control-Allow-Origin", "*" }, //TODO: XXX
{ "Cache-Control", cache_control },
{ "X-IRCd-Request-Timer", rtime, },
},
};
const ilist<const_buffer> vector