forked from boostorg/beast
Refactor prepare (API Change)
This commit is contained in:
1 parent
2af19481a9
commit
2df6783468
26 files changed
+543
-362
No files matched your search
@@ -8,6 +8,7 @@
|
||||
#ifndef BEAST_HTTP_IMPL_FIELDS_IPP
|
||||
#define BEAST_HTTP_IMPL_FIELDS_IPP
|
||||
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
@@ -74,7 +75,7 @@ target_impl(string_view s)
|
||||
if(s.empty())
|
||||
this->erase(":target");
|
||||
else
|
||||
return this->replace(":target", s);
|
||||
this->replace(":target", s);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
@@ -89,6 +90,73 @@ reason_impl(string_view s)
|
||||
this->replace(":reason", s);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
content_length_impl(std::uint64_t n)
|
||||
{
|
||||
this->erase("Content-Length");
|
||||
this->insert("Content-Length",
|
||||
to_static_string(n));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
connection_impl(close_t)
|
||||
{
|
||||
auto it = find("Connection");
|
||||
if(it == end())
|
||||
this->insert("Connection", "close");
|
||||
else
|
||||
this->replace("Connection",
|
||||
it->value().to_string() + ", close");
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
connection_impl(keep_alive_t)
|
||||
{
|
||||
auto it = find("Connection");
|
||||
if(it == end())
|
||||
this->insert("Connection", "keep-alive");
|
||||
else
|
||||
this->replace("Connection",
|
||||
it->value().to_string() + ", keep-alive");
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
connection_impl(upgrade_t)
|
||||
{
|
||||
auto it = find("Connection");
|
||||
if(it == end())
|
||||
this->insert("Connection", "upgrade");
|
||||
else
|
||||
this->replace("Connection",
|
||||
it->value().to_string() + ", upgrade");
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
chunked_impl()
|
||||
{
|
||||
auto it = find("Transfer-Encoding");
|
||||
if(it == end())
|
||||
this->insert("Transfer-Encoding", "chunked");
|
||||
else
|
||||
this->replace("Transfer-Encoding",
|
||||
it->value().to_string() + ", chunked");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
|
||||
+167
-175
@@ -9,14 +9,10 @@
|
||||
#define BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/string_view.hpp>
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
@@ -71,6 +67,170 @@ get_reason() const
|
||||
return obsolete_reason(result_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
} // detail
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
bool
|
||||
message<isRequest, Body, Fields>::
|
||||
chunked() const
|
||||
{
|
||||
auto const it0 = this->find("Transfer-Encoding");
|
||||
if(it0 == this->end())
|
||||
return false;
|
||||
token_list value{*it0};
|
||||
for(auto it = value.begin(); it != value.end();)
|
||||
{
|
||||
auto cur = it++;
|
||||
if(it == value.end())
|
||||
return *cur == "chunked";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
boost::optional<std::uint64_t>
|
||||
message<isRequest, Body, Fields>::
|
||||
size() const
|
||||
{
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
|
||||
return size(detail::is_body_sized<
|
||||
Body, decltype(*this)>{});
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... Args>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare(Args const&... args)
|
||||
{
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
|
||||
unsigned f = 0;
|
||||
prepare_opt(f, args...);
|
||||
|
||||
if(f & 1)
|
||||
{
|
||||
if(this->version > 10)
|
||||
this->connection_impl(connection::close);
|
||||
}
|
||||
|
||||
if(f & 2)
|
||||
{
|
||||
if(this->version < 11)
|
||||
this->connection_impl(connection::keep_alive);
|
||||
}
|
||||
|
||||
if(f & 4)
|
||||
{
|
||||
if(this->version < 11)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid connection upgrade"});
|
||||
this->connection_impl(connection::upgrade);
|
||||
}
|
||||
|
||||
prepare_payload(typename header<
|
||||
isRequest, Fields>::is_request{});
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Arg, class... Args>
|
||||
inline
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_opt(unsigned& f,
|
||||
Arg const& arg, Args const&... args)
|
||||
{
|
||||
prepare_opt(f, arg);
|
||||
prepare_opt(f, args...);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_opt(unsigned& f, close_t)
|
||||
{
|
||||
f |= 1;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_opt(unsigned& f, keep_alive_t)
|
||||
{
|
||||
f |= 2;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_opt(unsigned& f, upgrade_t)
|
||||
{
|
||||
f |= 4;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_payload(std::true_type)
|
||||
{
|
||||
auto const n = size();
|
||||
if(this->method_ == verb::trace &&
|
||||
(! n || *n > 0))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid request body"});
|
||||
if(n)
|
||||
{
|
||||
if(*n > 0 ||
|
||||
this->method_ == verb::options ||
|
||||
this->method_ == verb::put ||
|
||||
this->method_ == verb::post
|
||||
)
|
||||
{
|
||||
this->content_length_impl(*n);
|
||||
}
|
||||
}
|
||||
else if(this->version >= 11)
|
||||
{
|
||||
this->chunked_impl();
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_payload(std::false_type)
|
||||
{
|
||||
auto const n = size();
|
||||
if((status_class(this->result_) ==
|
||||
status_class::informational ||
|
||||
this->result_ == status::no_content ||
|
||||
this->result_ == status::not_modified))
|
||||
{
|
||||
if(! n || *n > 0)
|
||||
// The response body MUST BE empty for this case
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid response body"});
|
||||
}
|
||||
if(n)
|
||||
this->content_length_impl(*n);
|
||||
else if(this->version >= 11)
|
||||
this->chunked_impl();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
@@ -106,7 +266,9 @@ swap(
|
||||
message<isRequest, Body, Fields>& m2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(m1.base(), m2.base());
|
||||
swap(
|
||||
static_cast<header<isRequest, Fields>&>(m1),
|
||||
static_cast<header<isRequest, Fields>&>(m2));
|
||||
swap(m1.body, m2.body);
|
||||
}
|
||||
|
||||
@@ -138,176 +300,6 @@ is_upgrade(header<isRequest, Fields> const& msg)
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct prepare_info
|
||||
{
|
||||
boost::optional<connection> connection_value;
|
||||
boost::optional<std::uint64_t> content_length;
|
||||
};
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
prepare_options(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg)
|
||||
{
|
||||
beast::detail::ignore_unused(pi, msg);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_option(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
connection value)
|
||||
{
|
||||
beast::detail::ignore_unused(msg);
|
||||
pi.connection_value = value;
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Opt, class... Opts>
|
||||
void
|
||||
prepare_options(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
Opt&& opt, Opts&&... opts)
|
||||
{
|
||||
prepare_option(pi, msg, opt);
|
||||
prepare_options(pi, msg,
|
||||
std::forward<Opts>(opts)...);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_content_length(prepare_info& pi,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
std::true_type)
|
||||
{
|
||||
typename Body::reader w{msg};
|
||||
// VFALCO This is a design problem!
|
||||
error_code ec;
|
||||
w.init(ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
pi.content_length = w.content_length();
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_content_length(prepare_info& pi,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
std::false_type)
|
||||
{
|
||||
beast::detail::ignore_unused(msg);
|
||||
pi.content_length = boost::none;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class... Options>
|
||||
void
|
||||
prepare(message<isRequest, Body, Fields>& msg,
|
||||
Options&&... options)
|
||||
{
|
||||
// VFALCO TODO
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
detail::prepare_info pi;
|
||||
detail::prepare_content_length(pi, msg,
|
||||
detail::has_content_length<typename Body::reader>{});
|
||||
detail::prepare_options(pi, msg,
|
||||
std::forward<Options>(options)...);
|
||||
|
||||
if(msg.exists("Connection"))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"prepare called with Connection field set"});
|
||||
|
||||
if(msg.exists("Content-Length"))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"prepare called with Content-Length field set"});
|
||||
|
||||
if(token_list{msg["Transfer-Encoding"]}.exists("chunked"))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"prepare called with Transfer-Encoding: chunked set"});
|
||||
|
||||
if(pi.connection_value != connection::upgrade)
|
||||
{
|
||||
if(pi.content_length)
|
||||
{
|
||||
struct set_field
|
||||
{
|
||||
void
|
||||
operator()(message<true, Body, Fields>& msg,
|
||||
detail::prepare_info const& pi) const
|
||||
{
|
||||
using beast::detail::ci_equal;
|
||||
if(*pi.content_length > 0 ||
|
||||
msg.method() == verb::post)
|
||||
{
|
||||
msg.insert(
|
||||
"Content-Length", *pi.content_length);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
operator()(message<false, Body, Fields>& msg,
|
||||
detail::prepare_info const& pi) const
|
||||
{
|
||||
if(to_status_class(msg.result()) != status_class::informational &&
|
||||
msg.result() != status::no_content &&
|
||||
msg.result() != status::not_modified)
|
||||
{
|
||||
msg.insert(
|
||||
"Content-Length", *pi.content_length);
|
||||
}
|
||||
}
|
||||
};
|
||||
set_field{}(msg, pi);
|
||||
}
|
||||
else if(msg.version >= 11)
|
||||
{
|
||||
msg.insert("Transfer-Encoding", "chunked");
|
||||
}
|
||||
}
|
||||
|
||||
auto const content_length =
|
||||
msg.exists("Content-Length");
|
||||
|
||||
if(pi.connection_value)
|
||||
{
|
||||
switch(*pi.connection_value)
|
||||
{
|
||||
case connection::upgrade:
|
||||
msg.insert("Connection", "upgrade");
|
||||
break;
|
||||
|
||||
case connection::keep_alive:
|
||||
if(msg.version < 11)
|
||||
{
|
||||
if(content_length)
|
||||
msg.insert("Connection", "keep-alive");
|
||||
}
|
||||
break;
|
||||
|
||||
case connection::close:
|
||||
if(msg.version >= 11)
|
||||
msg.insert("Connection", "close");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rfc7230 6.7.
|
||||
if(msg.version < 11 && token_list{
|
||||
msg["Connection"]}.exists("upgrade"))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid version for Connection: upgrade"});
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ parser<isRequest, Body, Fields>::
|
||||
parser(parser<isRequest, OtherBody, Fields>&& parser,
|
||||
Args&&... args)
|
||||
: base_type(std::move(parser))
|
||||
, m_(parser.release().base(),
|
||||
std::forward<Args>(args)...)
|
||||
, m_(parser.release(), std::forward<Args>(args)...)
|
||||
{
|
||||
if(parser.wr_)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
|
||||
Reference in new issue
Block a user