forked from boostorg/beast
message::prepare_payload replaces message::prepare (API Change):
Actions Required: * Change calls to msg.prepare to msg.prepare_payload. For messages with a user-defined Fields, provide the function prepare_payload_impl in the fields type according to the Fields requirements.
This commit is contained in:
@@ -38,7 +38,7 @@ struct empty_body
|
||||
/// Returns the content length of the body in a message.
|
||||
static
|
||||
std::uint64_t
|
||||
size(empty_body)
|
||||
size(value_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
@@ -39,7 +40,6 @@ namespace http {
|
||||
as a `std::multiset`; there will be a separate value for each occurrence
|
||||
of the field name.
|
||||
|
||||
|
||||
Meets the requirements of @b Fields
|
||||
|
||||
@tparam Allocator The allocator to use. This must meet the
|
||||
@@ -48,7 +48,7 @@ namespace http {
|
||||
template<class Allocator>
|
||||
class basic_fields
|
||||
{
|
||||
static std::size_t constexpr max_static_start_line = 4096;
|
||||
static std::size_t constexpr max_static_buffer = 4096;
|
||||
|
||||
using off_t = std::uint16_t;
|
||||
|
||||
@@ -591,22 +591,11 @@ protected:
|
||||
*/
|
||||
string_view get_reason_impl() const;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// for container
|
||||
//
|
||||
|
||||
/** Set the Content-Length field to the specified value.
|
||||
|
||||
@note This is called by the @ref header implementation.
|
||||
/** Adjusts the payload related fields
|
||||
*/
|
||||
void content_length_impl(std::uint64_t n);
|
||||
|
||||
/** Add chunked to the Transfer-Encoding field.
|
||||
|
||||
@note This is called by the @ref header implementation.
|
||||
*/
|
||||
void set_chunked_impl(bool v);
|
||||
void
|
||||
prepare_payload_impl(bool chunked,
|
||||
boost::optional<std::uint64_t> size);
|
||||
|
||||
private:
|
||||
template<class OtherAlloc>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define BEAST_HTTP_IMPL_FIELDS_IPP
|
||||
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <beast/http/verb.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
@@ -149,7 +150,7 @@ public:
|
||||
unsigned version, unsigned code);
|
||||
|
||||
basic_fields const& f_;
|
||||
static_string<max_static_start_line> ss_;
|
||||
static_string<max_static_buffer> ss_;
|
||||
string_view sv_;
|
||||
std::string s_;
|
||||
bool chunked_;
|
||||
@@ -199,7 +200,7 @@ get_chunked() const
|
||||
f_[field::transfer_encoding]};
|
||||
for(auto it = te.begin(); it != te.end();)
|
||||
{
|
||||
auto next = std::next(it);
|
||||
auto const next = std::next(it);
|
||||
if(next == te.end())
|
||||
return iequals(*it, "chunked");
|
||||
it = next;
|
||||
@@ -866,32 +867,119 @@ get_reason_impl() const
|
||||
return target_or_reason_;
|
||||
}
|
||||
|
||||
//---
|
||||
namespace detail {
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
// Builds a new string with "chunked" maybe taken off the end
|
||||
template<class String>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
content_length_impl(std::uint64_t n)
|
||||
without_chunked_last(String& s, string_view const& tokens)
|
||||
{
|
||||
erase(field::content_length);
|
||||
insert(field::content_length, n);
|
||||
token_list te{tokens};
|
||||
if(te.begin() != te.end())
|
||||
{
|
||||
auto it = te.begin();
|
||||
auto next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! iequals(*it, "chunked"))
|
||||
s.append(it->data(), it->size());
|
||||
return;
|
||||
}
|
||||
s.append(it->data(), it->size());
|
||||
for(;;)
|
||||
{
|
||||
it = next;
|
||||
next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! iequals(*it, "chunked"))
|
||||
{
|
||||
s.append(", ");
|
||||
s.append(it->data(), it->size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
s.append(", ");
|
||||
s.append(it->data(), it->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_chunked_impl(bool v)
|
||||
prepare_payload_impl(bool chunked,
|
||||
boost::optional<std::uint64_t> size)
|
||||
{
|
||||
// VFALCO We need to handle removing the chunked as well
|
||||
BOOST_ASSERT(v);
|
||||
auto it = find(field::transfer_encoding);
|
||||
if(it == end())
|
||||
insert(field::transfer_encoding, "chunked");
|
||||
if(chunked)
|
||||
{
|
||||
BOOST_ASSERT(! size);
|
||||
erase(field::content_length);
|
||||
auto it = find(field::transfer_encoding);
|
||||
if(it == end())
|
||||
{
|
||||
set(field::transfer_encoding, "chunked");
|
||||
return;
|
||||
}
|
||||
|
||||
static_string<max_static_buffer> temp;
|
||||
if(it->value().size() <= temp.size() + 9)
|
||||
{
|
||||
temp.append(it->value().data(), it->value().size());
|
||||
temp.append(", chunked", 9);
|
||||
set(field::transfer_encoding, temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(it->value().size() + 9);
|
||||
s.append(it->value().data(), it->value().size());
|
||||
s.append(", chunked", 9);
|
||||
set(field::transfer_encoding, s);
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto const clear_chunked =
|
||||
[this]()
|
||||
{
|
||||
auto it = find(field::transfer_encoding);
|
||||
if(it == end())
|
||||
return;
|
||||
|
||||
// We have to just try it because we can't
|
||||
// know ahead of time if there's enough room.
|
||||
try
|
||||
{
|
||||
static_string<max_static_buffer> temp;
|
||||
detail::without_chunked_last(temp, it->value());
|
||||
if(! temp.empty())
|
||||
set(field::transfer_encoding, temp);
|
||||
else
|
||||
erase(field::transfer_encoding);
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(it->value().size());
|
||||
detail::without_chunked_last(s, it->value());
|
||||
if(! s.empty())
|
||||
set(field::transfer_encoding, s);
|
||||
else
|
||||
erase(field::transfer_encoding);
|
||||
}
|
||||
};
|
||||
if(size)
|
||||
{
|
||||
clear_chunked();
|
||||
set(field::content_length, *size);
|
||||
}
|
||||
else
|
||||
set(field::transfer_encoding,
|
||||
it->value().to_string() + ", chunked");
|
||||
{
|
||||
clear_chunked();
|
||||
erase(field::content_length);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -252,69 +252,53 @@ message(std::piecewise_construct_t,
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
boost::optional<std::uint64_t>
|
||||
message<isRequest, Body, Fields>::
|
||||
size() const
|
||||
payload_size() const
|
||||
{
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
|
||||
return size(detail::is_body_sized<Body>{});
|
||||
return payload_size(detail::is_body_sized<Body>{});
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
content_length(std::uint64_t n)
|
||||
prepare_payload(std::true_type)
|
||||
{
|
||||
this->content_length_impl(n);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare()
|
||||
{
|
||||
prepare(typename header_type::is_request{});
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare(std::true_type)
|
||||
{
|
||||
auto const n = size();
|
||||
if(this->method_ == verb::trace && (
|
||||
! n || *n > 0))
|
||||
auto const n = payload_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->method() == verb::options ||
|
||||
this->method() == verb::put ||
|
||||
this->method() == verb::post)
|
||||
{
|
||||
this->content_length_impl(*n);
|
||||
this->prepare_payload_impl(false, *n);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->prepare_payload_impl(false, boost::none);
|
||||
}
|
||||
}
|
||||
else if(this->version >= 11)
|
||||
{
|
||||
this->set_chunked_impl(true);
|
||||
this->prepare_payload_impl(true, boost::none);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->prepare_payload_impl(false, boost::none);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare(std::false_type)
|
||||
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))
|
||||
auto const n = payload_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
|
||||
@@ -322,9 +306,13 @@ prepare(std::false_type)
|
||||
"invalid response body"});
|
||||
}
|
||||
if(n)
|
||||
this->content_length_impl(*n);
|
||||
else if(this->version >= 11)
|
||||
this->set_chunked_impl(true);
|
||||
{
|
||||
this->prepare_payload_impl(false, *n);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->prepare_payload_impl(true, boost::none);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -496,17 +496,8 @@ struct message : header<isRequest, Fields>
|
||||
is not inspected.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
size() const;
|
||||
payload_size() const;
|
||||
|
||||
/** Set the Content-Length field.
|
||||
|
||||
The value of the Content-Length field will be unconditionally
|
||||
set to the specified number of octets.
|
||||
|
||||
@param n The number of octets to set for the Content-Length field.
|
||||
*/
|
||||
void
|
||||
content_length(std::uint64_t n);
|
||||
|
||||
/** Prepare the message payload fields for the body.
|
||||
|
||||
@@ -522,11 +513,14 @@ struct message : header<isRequest, Fields>
|
||||
req.target("/");
|
||||
req.set(field::user_agent, "Beast");
|
||||
req.body = "Hello, world!";
|
||||
req.prepare();
|
||||
req.prepare_payload();
|
||||
@endcode
|
||||
*/
|
||||
void
|
||||
prepare();
|
||||
prepare_payload()
|
||||
{
|
||||
prepare_payload(typename header_type::is_request{});
|
||||
}
|
||||
|
||||
private:
|
||||
static_assert(is_body<Body>::value,
|
||||
@@ -552,22 +546,22 @@ private:
|
||||
}
|
||||
|
||||
boost::optional<std::uint64_t>
|
||||
size(std::true_type) const
|
||||
payload_size(std::true_type) const
|
||||
{
|
||||
return Body::size(body);
|
||||
}
|
||||
|
||||
boost::optional<std::uint64_t>
|
||||
size(std::false_type) const
|
||||
payload_size(std::false_type) const
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
void
|
||||
prepare(std::true_type);
|
||||
prepare_payload(std::true_type);
|
||||
|
||||
void
|
||||
prepare(std::false_type);
|
||||
prepare_payload(std::false_type);
|
||||
};
|
||||
|
||||
/// A typical HTTP request
|
||||
|
||||
@@ -218,7 +218,7 @@ build_response(http::header<true,
|
||||
res.version = req.version;
|
||||
res.result(http::status::bad_request);
|
||||
res.body = text;
|
||||
res.prepare();
|
||||
res.prepare_payload();
|
||||
decorate(res);
|
||||
return res;
|
||||
};
|
||||
@@ -248,7 +248,7 @@ build_response(http::header<true,
|
||||
res.result(http::status::upgrade_required);
|
||||
res.version = req.version;
|
||||
res.set(http::field::sec_websocket_version, "13");
|
||||
res.prepare();
|
||||
res.prepare_payload();
|
||||
decorate(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user