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:
Vinnie Falco
2017-06-19 15:37:39 -07:00
parent f9f6e1e0cc
commit 56a561da9a
20 changed files with 398 additions and 114 deletions

View File

@@ -12,6 +12,7 @@ API Changes:
* parser requires basic_fields * parser requires basic_fields
* Refine FieldsReader concept * Refine FieldsReader concept
* message::prepare_payload replaces message::prepare
Actions Required: Actions Required:
@@ -21,6 +22,10 @@ Actions Required:
* Implement chunked() and keep_alive() for user defined FieldsReader types. * Implement chunked() and keep_alive() for user defined FieldsReader types.
* 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.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Version 61: Version 61:

View File

@@ -151,7 +151,7 @@ request.
Here we create an HTTP response indicating success. Note that this Here we create an HTTP response indicating success. Note that this
message has a body. The function message has a body. The function
[link beast.ref.beast__http__message.prepare prepare] [link beast.ref.beast__http__message.prepare_payload `message::prepare_payload`]
automatically sets the Content-Length or Transfer-Encoding field automatically sets the Content-Length or Transfer-Encoding field
depending on the content and type of the `body` member. The use depending on the content and type of the `body` member. The use
of prepare is optional; these fields may also be set explicitly. of prepare is optional; these fields may also be set explicitly.

View File

@@ -53,7 +53,7 @@ In this table:
[ [
If present, returns the serialized size of `v` not including If present, returns the serialized size of `v` not including
any chunked transfer encoding. When this function is provided, any chunked transfer encoding. When this function is provided,
[link beast.ref.beast__http__message.prepare `message::prepare`] [link beast.ref.beast__http__message.prepare_payload `message::prepare_payload`]
will automatically set the content length field based on the will automatically set the content length field based on the
value. Otherwise, the chunked transfer encoding will be set. value. Otherwise, the chunked transfer encoding will be set.
] ]

View File

@@ -25,6 +25,10 @@ In this table:
* `s` is a value of type [link beast.ref.beast__string_view `string_view`]. * `s` is a value of type [link beast.ref.beast__string_view `string_view`].
* `b` is a value of type `bool`
* `n` is a value of type `boost::optional<std::uint64_t>`.
[table Fields requirements [table Fields requirements
[[expression][type][semantics, pre/post-conditions]] [[expression][type][semantics, pre/post-conditions]]
[ [
@@ -88,6 +92,29 @@ In this table:
This function may throw `std::invalid_argument` if the operation This function may throw `std::invalid_argument` if the operation
is not supported by the container. is not supported by the container.
] ]
][
[`a.prepare_payload_impl(b,n)`]
[]
[
Adjusts the Content-Length and Transfer-Encoding fields to
account for the payload metadata indicated by `b` and `n` as
follows:
* If `b` is `true`, the chunked Transfer-Encoding should be applied
to the end of the list of encodings if it is not already there
or if the field was not present. Any Content-Length fields should
be removed.
* If `b` is `false` and `n` contains a value, set the Content-Length
field to `*n`, replacing any previous Content-Length fields. Remove
the chunked encoding from the end of the Transfer-Encoding field
if the field is present ahd chunked is the last encoding.
* If `b` is `false` and `n` contains no value, remove all Content-Length
fields, and remove the chunked encoding from the end of the
Transfer-Encoding field if the field is present ahd chunked is the
last encoding.
]
] ]
] ]
@@ -132,6 +159,14 @@ protected:
@note Only called for responses. @note Only called for responses.
*/ */
string_view get_reason_impl() const; string_view get_reason_impl() const;
/** Updates the payload metadata.
@param b `true` if chunked
@param n The content length if known, otherwise `boost::none`
*/
void prepare_payload_impl(bool b, boost::optional<std::uint64_t> n)
}; };
``` ```

View File

@@ -320,7 +320,7 @@ void do_server_head(
// set of headers that would be sent for a GET request, // set of headers that would be sent for a GET request,
// including the Content-Length, except for the body. // including the Content-Length, except for the body.
res.result(status::ok); res.result(status::ok);
res.content_length(payload.size()); res.set(field::content_length, payload.size());
// For GET requests, we include the body // For GET requests, we include the body
if(req.method() == verb::get) if(req.method() == verb::get)

View File

@@ -64,7 +64,7 @@ int main()
req.set(http::field::host, host + ":" + req.set(http::field::host, host + ":" +
boost::lexical_cast<std::string>(sock.remote_endpoint().port())); boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
req.set(http::field::user_agent, "Beast"); req.set(http::field::user_agent, "Beast");
req.prepare(); req.prepare_payload();
// Write the HTTP request to the remote host // Write the HTTP request to the remote host
http::write(stream, req, ec); http::write(stream, req, ec);

View File

@@ -55,7 +55,7 @@ int main()
req.set(http::field::host, host + ":" + req.set(http::field::host, host + ":" +
boost::lexical_cast<std::string>(sock.remote_endpoint().port())); boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
req.set(http::field::user_agent, "Beast"); req.set(http::field::user_agent, "Beast");
req.prepare(); req.prepare_payload();
// Write the HTTP request to the remote host // Write the HTTP request to the remote host
http::write(sock, req, ec); http::write(sock, req, ec);

View File

@@ -225,7 +225,7 @@ private:
res.set(beast::http::field::server, server_); res.set(beast::http::field::server, server_);
res.set(beast::http::field::content_type, "text/html"); res.set(beast::http::field::content_type, "text/html");
res.body = "The file was not found"; // VFALCO append rel_path res.body = "The file was not found"; // VFALCO append rel_path
res.prepare(); res.prepare_payload();
return res; return res;
} }
@@ -243,7 +243,7 @@ private:
res.set(beast::http::field::server, server_); res.set(beast::http::field::server, server_);
res.set(beast::http::field::content_type, mime_type(full_path)); res.set(beast::http::field::content_type, mime_type(full_path));
res.body = full_path; res.body = full_path;
res.prepare(); res.prepare_payload();
return res; return res;
} }

View File

@@ -50,7 +50,7 @@ protected:
res.set(beast::http::field::server, server_name_); res.set(beast::http::field::server, server_name_);
res.set(beast::http::field::content_type, "text/html"); res.set(beast::http::field::content_type, "text/html");
res.body = "Bad request"; res.body = "Bad request";
res.prepare(); res.prepare_payload();
return res; return res;
} }

View File

@@ -38,7 +38,7 @@ struct empty_body
/// Returns the content length of the body in a message. /// Returns the content length of the body in a message.
static static
std::uint64_t std::uint64_t
size(empty_body) size(value_type)
{ {
return 0; return 0;
} }

View File

@@ -15,6 +15,7 @@
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/intrusive/list.hpp> #include <boost/intrusive/list.hpp>
#include <boost/intrusive/set.hpp> #include <boost/intrusive/set.hpp>
#include <boost/optional.hpp>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cstring> #include <cstring>
@@ -39,7 +40,6 @@ namespace http {
as a `std::multiset`; there will be a separate value for each occurrence as a `std::multiset`; there will be a separate value for each occurrence
of the field name. of the field name.
Meets the requirements of @b Fields Meets the requirements of @b Fields
@tparam Allocator The allocator to use. This must meet the @tparam Allocator The allocator to use. This must meet the
@@ -48,7 +48,7 @@ namespace http {
template<class Allocator> template<class Allocator>
class basic_fields 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; using off_t = std::uint16_t;
@@ -591,22 +591,11 @@ protected:
*/ */
string_view get_reason_impl() const; string_view get_reason_impl() const;
//-------------------------------------------------------------------------- /** Adjusts the payload related fields
//
// for container
//
/** Set the Content-Length field to the specified value.
@note This is called by the @ref header implementation.
*/ */
void content_length_impl(std::uint64_t n); void
prepare_payload_impl(bool chunked,
/** Add chunked to the Transfer-Encoding field. boost::optional<std::uint64_t> size);
@note This is called by the @ref header implementation.
*/
void set_chunked_impl(bool v);
private: private:
template<class OtherAlloc> template<class OtherAlloc>

View File

@@ -9,6 +9,7 @@
#define BEAST_HTTP_IMPL_FIELDS_IPP #define BEAST_HTTP_IMPL_FIELDS_IPP
#include <beast/core/buffer_cat.hpp> #include <beast/core/buffer_cat.hpp>
#include <beast/core/string.hpp>
#include <beast/core/static_string.hpp> #include <beast/core/static_string.hpp>
#include <beast/http/verb.hpp> #include <beast/http/verb.hpp>
#include <beast/http/rfc7230.hpp> #include <beast/http/rfc7230.hpp>
@@ -149,7 +150,7 @@ public:
unsigned version, unsigned code); unsigned version, unsigned code);
basic_fields const& f_; basic_fields const& f_;
static_string<max_static_start_line> ss_; static_string<max_static_buffer> ss_;
string_view sv_; string_view sv_;
std::string s_; std::string s_;
bool chunked_; bool chunked_;
@@ -199,7 +200,7 @@ get_chunked() const
f_[field::transfer_encoding]}; f_[field::transfer_encoding]};
for(auto it = te.begin(); it != te.end();) for(auto it = te.begin(); it != te.end();)
{ {
auto next = std::next(it); auto const next = std::next(it);
if(next == te.end()) if(next == te.end())
return iequals(*it, "chunked"); return iequals(*it, "chunked");
it = next; it = next;
@@ -866,32 +867,119 @@ get_reason_impl() const
return target_or_reason_; return target_or_reason_;
} }
//--- namespace detail {
template<class Allocator> // Builds a new string with "chunked" maybe taken off the end
inline template<class String>
void void
basic_fields<Allocator>:: without_chunked_last(String& s, string_view const& tokens)
content_length_impl(std::uint64_t n)
{ {
erase(field::content_length); token_list te{tokens};
insert(field::content_length, n); 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> template<class Allocator>
inline
void void
basic_fields<Allocator>:: 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 if(chunked)
BOOST_ASSERT(v); {
BOOST_ASSERT(! size);
erase(field::content_length);
auto it = find(field::transfer_encoding); auto it = find(field::transfer_encoding);
if(it == end()) if(it == end())
insert(field::transfer_encoding, "chunked"); {
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 else
set(field::transfer_encoding, {
it->value().to_string() + ", chunked"); 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
{
clear_chunked();
erase(field::content_length);
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -252,69 +252,53 @@ message(std::piecewise_construct_t,
template<bool isRequest, class Body, class Fields> template<bool isRequest, class Body, class Fields>
boost::optional<std::uint64_t> boost::optional<std::uint64_t>
message<isRequest, Body, Fields>:: message<isRequest, Body, Fields>::
size() const payload_size() const
{ {
static_assert(is_body_reader<Body>::value, return payload_size(detail::is_body_sized<Body>{});
"BodyReader requirements not met");
return size(detail::is_body_sized<Body>{});
} }
template<bool isRequest, class Body, class Fields> template<bool isRequest, class Body, class Fields>
void void
message<isRequest, Body, Fields>:: message<isRequest, Body, Fields>::
content_length(std::uint64_t n) prepare_payload(std::true_type)
{ {
this->content_length_impl(n); auto const n = payload_size();
} if(this->method() == verb::trace && (! n || *n > 0))
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))
BOOST_THROW_EXCEPTION(std::invalid_argument{ BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid request body"}); "invalid request body"});
if(n) if(n)
{ {
if(*n > 0 || if(*n > 0 ||
this->method_ == verb::options || this->method() == verb::options ||
this->method_ == verb::put || this->method() == verb::put ||
this->method_ == verb::post 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) 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> template<bool isRequest, class Body, class Fields>
inline
void void
message<isRequest, Body, Fields>:: message<isRequest, Body, Fields>::
prepare(std::false_type) prepare_payload(std::false_type)
{ {
auto const n = size(); auto const n = payload_size();
if((status_class(this->result_) == if((status_class(this->result()) == status_class::informational ||
status_class::informational || this->result() == status::no_content ||
this->result_ == status::no_content || this->result() == status::not_modified))
this->result_ == status::not_modified))
{ {
if(! n || *n > 0) if(! n || *n > 0)
// The response body MUST BE empty for this case // The response body MUST BE empty for this case
@@ -322,9 +306,13 @@ prepare(std::false_type)
"invalid response body"}); "invalid response body"});
} }
if(n) if(n)
this->content_length_impl(*n); {
else if(this->version >= 11) this->prepare_payload_impl(false, *n);
this->set_chunked_impl(true); }
else
{
this->prepare_payload_impl(true, boost::none);
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -496,17 +496,8 @@ struct message : header<isRequest, Fields>
is not inspected. is not inspected.
*/ */
boost::optional<std::uint64_t> 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. /** Prepare the message payload fields for the body.
@@ -522,11 +513,14 @@ struct message : header<isRequest, Fields>
req.target("/"); req.target("/");
req.set(field::user_agent, "Beast"); req.set(field::user_agent, "Beast");
req.body = "Hello, world!"; req.body = "Hello, world!";
req.prepare(); req.prepare_payload();
@endcode @endcode
*/ */
void void
prepare(); prepare_payload()
{
prepare_payload(typename header_type::is_request{});
}
private: private:
static_assert(is_body<Body>::value, static_assert(is_body<Body>::value,
@@ -552,22 +546,22 @@ private:
} }
boost::optional<std::uint64_t> boost::optional<std::uint64_t>
size(std::true_type) const payload_size(std::true_type) const
{ {
return Body::size(body); return Body::size(body);
} }
boost::optional<std::uint64_t> boost::optional<std::uint64_t>
size(std::false_type) const payload_size(std::false_type) const
{ {
return boost::none; return boost::none;
} }
void void
prepare(std::true_type); prepare_payload(std::true_type);
void void
prepare(std::false_type); prepare_payload(std::false_type);
}; };
/// A typical HTTP request /// A typical HTTP request

View File

@@ -218,7 +218,7 @@ build_response(http::header<true,
res.version = req.version; res.version = req.version;
res.result(http::status::bad_request); res.result(http::status::bad_request);
res.body = text; res.body = text;
res.prepare(); res.prepare_payload();
decorate(res); decorate(res);
return res; return res;
}; };
@@ -248,7 +248,7 @@ build_response(http::header<true,
res.result(http::status::upgrade_required); res.result(http::status::upgrade_required);
res.version = req.version; res.version = req.version;
res.set(http::field::sec_websocket_version, "13"); res.set(http::field::sec_websocket_version, "13");
res.prepare(); res.prepare_payload();
decorate(res); decorate(res);
return res; return res;
} }

View File

@@ -73,7 +73,7 @@ public:
req.target("/"); req.target("/");
req.insert(field::user_agent, "test"); req.insert(field::user_agent, "test");
req.body = "Hello, world!"; req.body = "Hello, world!";
req.prepare(); req.prepare_payload();
error_code ec; error_code ec;
send_expect_100_continue( send_expect_100_continue(
@@ -106,7 +106,7 @@ public:
req.target("/"); req.target("/");
req.insert(field::user_agent, "test"); req.insert(field::user_agent, "test");
req.body = "Hello, world!"; req.body = "Hello, world!";
req.prepare(); req.prepare_payload();
test::pipe downstream{ios_}; test::pipe downstream{ios_};
downstream.server.read_size(3); downstream.server.read_size(3);
@@ -272,7 +272,7 @@ public:
req.method(verb::put); req.method(verb::put);
req.target("/"); req.target("/");
req.body = body; req.body = body;
req.prepare(); req.prepare_payload();
write(c.client, req); write(c.client, req);
} }
{ {
@@ -293,7 +293,7 @@ public:
res.result(status::ok); res.result(status::ok);
res.insert(field::server, "test"); res.insert(field::server, "test");
res.body = path; res.body = path;
res.prepare(); res.prepare_payload();
write(c.server, res); write(c.server, res);
} }
{ {

View File

@@ -49,7 +49,7 @@ void fxx() {
res.result(status::ok); res.result(status::ok);
res.set(field::server, "Beast"); res.set(field::server, "Beast");
res.body = "Hello, world!"; res.body = "Hello, world!";
res.prepare(); res.prepare_payload();
//] //]
} }

View File

@@ -8,6 +8,8 @@
// Test that header file is self-contained. // Test that header file is self-contained.
#include <beast/http/fields.hpp> #include <beast/http/fields.hpp>
#include <beast/http/empty_body.hpp>
#include <beast/http/message.hpp>
#include <beast/test/test_allocator.hpp> #include <beast/test/test_allocator.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@@ -397,6 +399,188 @@ public:
} }
} }
struct sized_body
{
using value_type = std::uint64_t;
static
std::uint64_t
size(value_type const& v)
{
return v;
}
};
struct unsized_body
{
struct value_type {};
};
void
testPreparePayload()
{
// GET, empty
{
request<empty_body> req;
req.version = 11;
req.method(verb::get);
req.prepare_payload();
BEAST_EXPECT(req.count(field::content_length) == 0);
BEAST_EXPECT(req.count(field::transfer_encoding) == 0);
req.set(field::content_length, "0");
req.set(field::transfer_encoding, "chunked");
req.prepare_payload();
BEAST_EXPECT(req.count(field::content_length) == 0);
BEAST_EXPECT(req.count(field::transfer_encoding) == 0);
req.set(field::transfer_encoding, "deflate");
req.prepare_payload();
BEAST_EXPECT(req.count(field::content_length) == 0);
BEAST_EXPECT(req[field::transfer_encoding] == "deflate");
req.set(field::transfer_encoding, "deflate, chunked");
req.prepare_payload();
BEAST_EXPECT(req.count(field::content_length) == 0);
BEAST_EXPECT(req[field::transfer_encoding] == "deflate");
}
// GET, sized
{
request<sized_body> req;
req.version = 11;
req.method(verb::get);
req.body = 50;
req.prepare_payload();
BEAST_EXPECT(req[field::content_length] == "50");
BEAST_EXPECT(req[field::transfer_encoding] == "");
req.set(field::content_length, "0");
req.set(field::transfer_encoding, "chunked");
req.prepare_payload();
BEAST_EXPECT(req[field::content_length] == "50");
BEAST_EXPECT(req.count(field::transfer_encoding) == 0);
req.set(field::transfer_encoding, "deflate, chunked");
req.prepare_payload();
BEAST_EXPECT(req[field::content_length] == "50");
BEAST_EXPECT(req[field::transfer_encoding] == "deflate");
}
// PUT, empty
{
request<empty_body> req;
req.version = 11;
req.method(verb::put);
req.prepare_payload();
BEAST_EXPECT(req[field::content_length] == "0");
BEAST_EXPECT(req.count(field::transfer_encoding) == 0);
req.set(field::content_length, "50");
req.set(field::transfer_encoding, "deflate, chunked");
req.prepare_payload();
BEAST_EXPECT(req[field::content_length] == "0");
BEAST_EXPECT(req[field::transfer_encoding] == "deflate");
}
// PUT, sized
{
request<sized_body> req;
req.version = 11;
req.method(verb::put);
req.body = 50;
req.prepare_payload();
BEAST_EXPECT(req[field::content_length] == "50");
BEAST_EXPECT(req.count(field::transfer_encoding) == 0);
req.set(field::content_length, "25");
req.set(field::transfer_encoding, "deflate, chunked");
req.prepare_payload();
BEAST_EXPECT(req[field::content_length] == "50");
BEAST_EXPECT(req[field::transfer_encoding] == "deflate");
}
// POST, unsized
{
request<unsized_body> req;
req.version = 11;
req.method(verb::post);
req.prepare_payload();
BEAST_EXPECT(req.count(field::content_length) == 0);
BEAST_EXPECT(req[field::transfer_encoding] == "chunked");
req.set(field::transfer_encoding, "deflate");
req.prepare_payload();
BEAST_EXPECT(req.count(field::content_length) == 0);
BEAST_EXPECT(req[field::transfer_encoding] == "deflate, chunked");
}
// POST, unsized HTTP/1.0
{
request<unsized_body> req;
req.version = 10;
req.method(verb::post);
req.prepare_payload();
BEAST_EXPECT(req.count(field::content_length) == 0);
BEAST_EXPECT(req.count(field::transfer_encoding) == 0);
req.set(field::transfer_encoding, "deflate");
req.prepare_payload();
BEAST_EXPECT(req.count(field::content_length) == 0);
BEAST_EXPECT(req[field::transfer_encoding] == "deflate");
}
// OK, empty
{
response<empty_body> res;
res.version = 11;
res.prepare_payload();
BEAST_EXPECT(res[field::content_length] == "0");
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
res.erase(field::content_length);
res.set(field::transfer_encoding, "chunked");
res.prepare_payload();
BEAST_EXPECT(res[field::content_length] == "0");
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
}
// OK, sized
{
response<sized_body> res;
res.version = 11;
res.body = 50;
res.prepare_payload();
BEAST_EXPECT(res[field::content_length] == "50");
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
res.erase(field::content_length);
res.set(field::transfer_encoding, "chunked");
res.prepare_payload();
BEAST_EXPECT(res[field::content_length] == "50");
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
}
// OK, unsized
{
response<unsized_body> res;
res.version = 11;
res.prepare_payload();
BEAST_EXPECT(res.count(field::content_length) == 0);
BEAST_EXPECT(res[field::transfer_encoding] == "chunked");
}
}
void run() override void run() override
{ {
testMembers(); testMembers();
@@ -404,6 +588,7 @@ public:
testRFC2616(); testRFC2616();
testErase(); testErase();
testContainer(); testContainer();
testPreparePayload();
} }
}; };

View File

@@ -31,7 +31,7 @@ public:
req.version = 11; req.version = 11;
req.method(verb::post); req.method(verb::post);
req.target("/"); req.target("/");
req.prepare(); req.prepare_payload();
static_buffer_n<512> b; static_buffer_n<512> b;
ostream(b) << req; ostream(b) << req;
string_view const s{ string_view const s{

View File

@@ -532,7 +532,7 @@ public:
m.version = 10; m.version = 10;
m.insert(field::user_agent, "test"); m.insert(field::user_agent, "test");
m.body = "*"; m.body = "*";
m.prepare(); m.prepare_payload();
BEAST_EXPECT(str(m) == BEAST_EXPECT(str(m) ==
"GET / HTTP/1.0\r\n" "GET / HTTP/1.0\r\n"
"User-Agent: test\r\n" "User-Agent: test\r\n"
@@ -549,7 +549,7 @@ public:
m.version = 10; m.version = 10;
m.insert(field::user_agent, "test"); m.insert(field::user_agent, "test");
m.body = "*"; m.body = "*";
m.prepare(); m.prepare_payload();
test::string_ostream ss(ios_); test::string_ostream ss(ios_);
error_code ec; error_code ec;
write(ss, m, ec); write(ss, m, ec);
@@ -569,7 +569,7 @@ public:
m.version = 11; m.version = 11;
m.insert(field::user_agent, "test"); m.insert(field::user_agent, "test");
m.body = "*"; m.body = "*";
m.prepare(); m.prepare_payload();
BEAST_EXPECT(str(m) == BEAST_EXPECT(str(m) ==
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"User-Agent: test\r\n" "User-Agent: test\r\n"
@@ -586,7 +586,7 @@ public:
m.version = 11; m.version = 11;
m.insert(field::user_agent, "test"); m.insert(field::user_agent, "test");
m.body = "*"; m.body = "*";
m.prepare(); m.prepare_payload();
test::string_ostream ss(ios_); test::string_ostream ss(ios_);
error_code ec; error_code ec;
write(ss, m, ec); write(ss, m, ec);