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:
parent
c20a2927d0
commit
36142718f6
4 changed files with 138 additions and 92 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
153
ircd/http.cc
153
ircd/http.cc
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue