mirror of
https://github.com/boostorg/beast.git
synced 2025-08-01 05:44:38 +02:00
Refactor serialization algorithms:
serializer interface is changed to be buffer-only, no streams, and placed in its own header file. Operations on serializers are moved to free functions as part of the HTTP write family of synchronous and asynchronous algorithms.
This commit is contained in:
@@ -61,6 +61,7 @@
|
||||
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
|
||||
<member><link linkend="beast.ref.http__async_read_some">async_read_some</link></member>
|
||||
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
|
||||
<member><link linkend="beast.ref.http__async_write_some">async_write_some</link></member>
|
||||
<member><link linkend="beast.ref.http__is_keep_alive">is_keep_alive</link></member>
|
||||
<member><link linkend="beast.ref.http__is_upgrade">is_upgrade</link></member>
|
||||
<member><link linkend="beast.ref.http__make_serializer">make_serializer</link></member>
|
||||
@@ -71,6 +72,7 @@
|
||||
<member><link linkend="beast.ref.http__reason_string">reason_string</link></member>
|
||||
<member><link linkend="beast.ref.http__swap">swap</link></member>
|
||||
<member><link linkend="beast.ref.http__write">write</link></member>
|
||||
<member><link linkend="beast.ref.http__write_some">write_some</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <beast/http/message_parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/serializer.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
|
||||
|
407
include/beast/http/impl/serializer.ipp
Normal file
407
include/beast/http/impl/serializer.ipp
Normal file
@@ -0,0 +1,407 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_SERIALIZER_IPP
|
||||
#define BEAST_HTTP_IMPL_SERIALIZER_IPP
|
||||
|
||||
#include <beast/http/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
write_start_line(std::ostream& os,
|
||||
header<true, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
os << msg.method() << " " << msg.target();
|
||||
switch(msg.version)
|
||||
{
|
||||
case 10: os << " HTTP/1.0\r\n"; break;
|
||||
case 11: os << " HTTP/1.1\r\n"; break;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
write_start_line(std::ostream& os,
|
||||
header<false, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
switch(msg.version)
|
||||
{
|
||||
case 10: os << "HTTP/1.0 "; break;
|
||||
case 11: os << "HTTP/1.1 "; break;
|
||||
}
|
||||
os << msg.status << " " << msg.reason() << "\r\n";
|
||||
}
|
||||
|
||||
template<class FieldSequence>
|
||||
void
|
||||
write_fields(std::ostream& os,
|
||||
FieldSequence const& fields)
|
||||
{
|
||||
//static_assert(is_FieldSequence<FieldSequence>::value,
|
||||
// "FieldSequence requirements not met");
|
||||
for(auto const& field : fields)
|
||||
{
|
||||
auto const name = field.name();
|
||||
BOOST_ASSERT(! name.empty());
|
||||
if(name[0] == ':')
|
||||
continue;
|
||||
os << field.name() << ": " << field.value() << "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>::
|
||||
serializer(message<isRequest, Body, Fields> const& m,
|
||||
Decorator const& d, Allocator const& alloc)
|
||||
: m_(m)
|
||||
, d_(d)
|
||||
, b_(1024, alloc)
|
||||
, chunked_(token_list{
|
||||
m.fields["Transfer-Encoding"]}.exists("chunked"))
|
||||
, close_(token_list{
|
||||
m.fields["Connection"]}.exists("close") ||
|
||||
(m.version < 11 && ! m.fields.exists(
|
||||
"Content-Length")))
|
||||
{
|
||||
s_ = chunked_ ? do_init_c : do_init;
|
||||
// VFALCO Move this stuff to the switch?
|
||||
auto os = ostream(b_);
|
||||
detail::write_start_line(os, m_);
|
||||
detail::write_fields(os, m_.fields);
|
||||
os << "\r\n";
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
template<class Visit>
|
||||
void
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>::
|
||||
get(error_code& ec, Visit&& visit)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
switch(s_)
|
||||
{
|
||||
case do_init:
|
||||
{
|
||||
if(split_)
|
||||
goto go_header_only;
|
||||
rd_.emplace(m_);
|
||||
rd_->init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
auto result = rd_->get(ec);
|
||||
if(ec)
|
||||
{
|
||||
// Can't use need_more when ! is_deferred
|
||||
BOOST_ASSERT(ec != error::need_more);
|
||||
return;
|
||||
}
|
||||
if(! result)
|
||||
goto go_header_only;
|
||||
more_ = result->second;
|
||||
v_ = cb0_t{
|
||||
boost::in_place_init,
|
||||
b_.data(),
|
||||
result->first};
|
||||
s_ = do_header;
|
||||
// [[fallthrough]]
|
||||
}
|
||||
|
||||
case do_header:
|
||||
visit(ec, boost::get<cb0_t>(v_));
|
||||
break;
|
||||
|
||||
go_header_only:
|
||||
s_ = do_header_only;
|
||||
case do_header_only:
|
||||
visit(ec, b_.data());
|
||||
break;
|
||||
|
||||
case do_body:
|
||||
BOOST_ASSERT(! rd_);
|
||||
rd_.emplace(m_);
|
||||
rd_->init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
s_ = do_body + 1;
|
||||
// [[fallthrough]]
|
||||
|
||||
case do_body + 1:
|
||||
{
|
||||
auto result = rd_->get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_complete;
|
||||
more_ = result->second;
|
||||
v_ = cb1_t{result->first};
|
||||
s_ = do_body + 2;
|
||||
// [[fallthrough]]
|
||||
}
|
||||
|
||||
case do_body + 2:
|
||||
visit(ec, boost::get<cb1_t>(v_));
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_init_c:
|
||||
{
|
||||
if(split_)
|
||||
goto go_header_only_c;
|
||||
rd_.emplace(m_);
|
||||
rd_->init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
auto result = rd_->get(ec);
|
||||
if(ec)
|
||||
{
|
||||
// Can't use need_more when ! is_deferred
|
||||
BOOST_ASSERT(ec != error::need_more);
|
||||
return;
|
||||
}
|
||||
if(! result)
|
||||
goto go_header_only_c;
|
||||
more_ = result->second;
|
||||
v_ = ch0_t{
|
||||
boost::in_place_init,
|
||||
b_.data(),
|
||||
detail::chunk_header{
|
||||
buffer_size(result->first)},
|
||||
[&]()
|
||||
{
|
||||
auto sv = d_(result->first);
|
||||
return boost::asio::const_buffers_1{
|
||||
sv.data(), sv.size()};
|
||||
|
||||
}(),
|
||||
result->first,
|
||||
detail::chunk_crlf()};
|
||||
s_ = do_header_c;
|
||||
// [[fallthrough]]
|
||||
}
|
||||
|
||||
case do_header_c:
|
||||
visit(ec, boost::get<ch0_t>(v_));
|
||||
break;
|
||||
|
||||
go_header_only_c:
|
||||
s_ = do_header_only_c;
|
||||
case do_header_only_c:
|
||||
visit(ec, b_.data());
|
||||
break;
|
||||
|
||||
case do_body_c:
|
||||
BOOST_ASSERT(! rd_);
|
||||
rd_.emplace(m_);
|
||||
rd_->init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
s_ = do_body_c + 1;
|
||||
// [[fallthrough]]
|
||||
|
||||
case do_body_c + 1:
|
||||
{
|
||||
auto result = rd_->get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_final_c;
|
||||
more_ = result->second;
|
||||
v_ = ch1_t{
|
||||
boost::in_place_init,
|
||||
detail::chunk_header{
|
||||
buffer_size(result->first)},
|
||||
[&]()
|
||||
{
|
||||
auto sv = d_(result->first);
|
||||
return boost::asio::const_buffers_1{
|
||||
sv.data(), sv.size()};
|
||||
|
||||
}(),
|
||||
result->first,
|
||||
detail::chunk_crlf()};
|
||||
s_ = do_body_c + 2;
|
||||
// [[fallthrough]]
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
visit(ec, boost::get<ch1_t>(v_));
|
||||
break;
|
||||
|
||||
go_final_c:
|
||||
case do_final_c:
|
||||
v_ = ch2_t{
|
||||
boost::in_place_init,
|
||||
detail::chunk_final(),
|
||||
[&]()
|
||||
{
|
||||
auto sv = d_(
|
||||
boost::asio::null_buffers{});
|
||||
return boost::asio::const_buffers_1{
|
||||
sv.data(), sv.size()};
|
||||
|
||||
}(),
|
||||
detail::chunk_crlf()};
|
||||
s_ = do_final_c + 1;
|
||||
// [[fallthrough]]
|
||||
|
||||
case do_final_c + 1:
|
||||
visit(ec, boost::get<ch2_t>(v_));
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
case do_complete:
|
||||
BOOST_ASSERT(false);
|
||||
break;
|
||||
|
||||
go_complete:
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
switch(s_)
|
||||
{
|
||||
case do_header:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb0_t>(v_)));
|
||||
boost::get<cb0_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb0_t>(v_)) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_ = boost::blank{};
|
||||
b_.consume(b_.size()); // VFALCO delete b_?
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
|
||||
case do_header_only:
|
||||
BOOST_ASSERT(n <= b_.size());
|
||||
b_.consume(n);
|
||||
if(buffer_size(b_.data()) > 0)
|
||||
break;
|
||||
// VFALCO delete b_?
|
||||
header_done_ = true;
|
||||
if(! is_deferred::value)
|
||||
goto go_complete;
|
||||
s_ = do_body;
|
||||
break;
|
||||
|
||||
case do_body + 2:
|
||||
{
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb1_t>(v_)));
|
||||
boost::get<cb1_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb1_t>(v_)) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_header_c:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<ch0_t>(v_)));
|
||||
boost::get<ch0_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<ch0_t>(v_)) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_ = boost::blank{};
|
||||
b_.consume(b_.size()); // VFALCO delete b_?
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
case do_header_only_c:
|
||||
{
|
||||
BOOST_ASSERT(n <= buffer_size(b_.data()));
|
||||
b_.consume(n);
|
||||
if(buffer_size(b_.data()) > 0)
|
||||
break;
|
||||
// VFALCO delete b_?
|
||||
header_done_ = true;
|
||||
if(! is_deferred::value)
|
||||
{
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
}
|
||||
s_ = do_body_c;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<ch1_t>(v_)));
|
||||
boost::get<ch1_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<ch1_t>(v_)) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
case do_final_c + 1:
|
||||
BOOST_ASSERT(buffer_size(
|
||||
boost::get<ch2_t>(v_)));
|
||||
boost::get<ch2_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<ch2_t>(v_)) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
goto go_complete;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
BOOST_ASSERT(false);
|
||||
case do_complete:
|
||||
break;
|
||||
|
||||
go_complete:
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
350
include/beast/http/serializer.hpp
Normal file
350
include/beast/http/serializer.hpp
Normal file
@@ -0,0 +1,350 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_SERIALIZER_HPP
|
||||
#define BEAST_HTTP_SERIALIZER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/async_result.hpp>
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/core/string_view.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/detail/chunk_encode.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A chunk decorator which does nothing.
|
||||
|
||||
When selected as a chunk decorator, objects of this type
|
||||
affect the output of messages specifying chunked
|
||||
transfer encodings as follows:
|
||||
|
||||
@li chunk headers will have empty chunk extensions, and
|
||||
|
||||
@li final chunks will have an empty set of trailers.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
struct empty_decorator
|
||||
{
|
||||
template<class ConstBufferSequence>
|
||||
string_view
|
||||
operator()(ConstBufferSequence const&) const
|
||||
{
|
||||
return {"\r\n"};
|
||||
}
|
||||
|
||||
string_view
|
||||
operator()(boost::asio::null_buffers) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/** Provides buffer oriented HTTP message serialization functionality.
|
||||
|
||||
An object of this type is used to serialize a complete
|
||||
HTTP message into a seriest of octets. To use this class,
|
||||
construct an instance with the message to be serialized.
|
||||
To make it easier to declare the type, the helper function
|
||||
@ref make_serializer is provided.
|
||||
|
||||
The implementation will automatically perform chunk encoding
|
||||
if the contents of the message indicate that chunk encoding
|
||||
is required. If the semantics of the message indicate that
|
||||
the connection should be closed after the message is sent, the
|
||||
function @ref needs_close will return `true`.
|
||||
|
||||
Upon construction, an optional chunk decorator may be
|
||||
specified. This decorator is a function object called with
|
||||
each buffer sequence of the body when the chunked transfer
|
||||
encoding is indicate in the message header. The decorator
|
||||
will be called with an empty buffer sequence (actually
|
||||
the type `boost::asio::null_buffers`) to indicate the
|
||||
final chunk. The decorator may return a string which forms
|
||||
the chunk extension for chunks, and the field trailers
|
||||
for the final chunk.
|
||||
|
||||
In C++11 the decorator must be declared as a class or
|
||||
struct with a templated operator() thusly:
|
||||
|
||||
@code
|
||||
// The implementation guarantees that operator()
|
||||
// will be called only after the view returned by
|
||||
// any previous calls to operator() are no longer
|
||||
// needed. The decorator instance is intended to
|
||||
// manage the lifetime of the storage for all returned
|
||||
// views.
|
||||
//
|
||||
struct decorator
|
||||
{
|
||||
// Returns the chunk-extension for each chunk.
|
||||
// The buffer returned must include a trailing "\r\n",
|
||||
// and the leading semicolon (";") if one or more
|
||||
// chunk extensions are specified.
|
||||
//
|
||||
template<class ConstBufferSequence>
|
||||
string_view
|
||||
operator()(ConstBufferSequence const&) const;
|
||||
|
||||
// Returns a set of field trailers for the final chunk.
|
||||
// Each field should be formatted according to rfc7230
|
||||
// including the trailing "\r\n" for each field. If
|
||||
// no trailers are indicated, an empty string is returned.
|
||||
//
|
||||
string_view
|
||||
operator()(boost::asio::null_buffers) const;
|
||||
};
|
||||
@endcode
|
||||
|
||||
@tparam isRequest `true` if the message is a request.
|
||||
|
||||
@tparam Body The body type of the message.
|
||||
|
||||
@tparam Fields The type of fields in the message.
|
||||
|
||||
@tparam Decorator The type of chunk decorator to use.
|
||||
|
||||
@tparam Allocator The type of allocator to use.
|
||||
|
||||
@see @ref make_serializer
|
||||
*/
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator = empty_decorator,
|
||||
class Allocator = std::allocator<char>
|
||||
>
|
||||
class serializer
|
||||
{
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
|
||||
enum
|
||||
{
|
||||
do_init = 0,
|
||||
do_header_only = 10,
|
||||
do_header = 20,
|
||||
do_body = 40,
|
||||
|
||||
do_init_c = 50,
|
||||
do_header_only_c = 60,
|
||||
do_header_c = 70,
|
||||
do_body_c = 90,
|
||||
do_final_c = 100,
|
||||
|
||||
do_complete = 110
|
||||
};
|
||||
|
||||
void split(bool, std::true_type) {}
|
||||
void split(bool v, std::false_type) { split_ = v; }
|
||||
|
||||
using buffer_type =
|
||||
basic_multi_buffer<Allocator>;
|
||||
|
||||
using reader = typename Body::reader;
|
||||
|
||||
using is_deferred =
|
||||
typename reader::is_deferred;
|
||||
|
||||
using cb0_t = consuming_buffers<buffers_view<
|
||||
typename buffer_type::const_buffers_type, // header
|
||||
typename reader::const_buffers_type>>; // body
|
||||
|
||||
using cb1_t = consuming_buffers<
|
||||
typename reader::const_buffers_type>; // body
|
||||
|
||||
using ch0_t = consuming_buffers<buffers_view<
|
||||
typename buffer_type::const_buffers_type, // header
|
||||
detail::chunk_header, // chunk-header
|
||||
boost::asio::const_buffers_1, // chunk-ext+\r\n
|
||||
typename reader::const_buffers_type, // body
|
||||
boost::asio::const_buffers_1>>; // crlf
|
||||
|
||||
using ch1_t = consuming_buffers<buffers_view<
|
||||
detail::chunk_header, // chunk-header
|
||||
boost::asio::const_buffers_1, // chunk-ext+\r\n
|
||||
typename reader::const_buffers_type, // body
|
||||
boost::asio::const_buffers_1>>; // crlf
|
||||
|
||||
using ch2_t = consuming_buffers<buffers_view<
|
||||
boost::asio::const_buffers_1, // chunk-final
|
||||
boost::asio::const_buffers_1, // trailers
|
||||
boost::asio::const_buffers_1>>; // crlf
|
||||
|
||||
message<isRequest, Body, Fields> const& m_;
|
||||
Decorator d_;
|
||||
boost::optional<reader> rd_;
|
||||
buffer_type b_;
|
||||
boost::variant<boost::blank,
|
||||
cb0_t, cb1_t, ch0_t, ch1_t, ch2_t> v_;
|
||||
int s_;
|
||||
bool split_ = is_deferred::value;
|
||||
bool header_done_ = false;
|
||||
bool chunked_;
|
||||
bool close_;
|
||||
bool more_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
@param msg The message to serialize. The message object
|
||||
must remain valid for the lifetime of the write stream.
|
||||
|
||||
@param decorator An optional decorator to use.
|
||||
|
||||
@param alloc An optional allocator to use.
|
||||
*/
|
||||
explicit
|
||||
serializer(message<isRequest, Body, Fields> const& msg,
|
||||
Decorator const& decorator = Decorator{},
|
||||
Allocator const& alloc = Allocator{});
|
||||
|
||||
/** Returns `true` if we will pause after writing the header.
|
||||
*/
|
||||
bool
|
||||
split() const
|
||||
{
|
||||
return split_;
|
||||
}
|
||||
|
||||
/** Set whether the header and body are written separately.
|
||||
|
||||
When the split feature is enabled, the implementation will
|
||||
write only the octets corresponding to the serialized header
|
||||
first. If the header has already been written, this function
|
||||
will have no effect on output. This function should be called
|
||||
before any writes take place, otherwise the behavior is
|
||||
undefined.
|
||||
*/
|
||||
void
|
||||
split(bool v)
|
||||
{
|
||||
split(v, is_deferred{});
|
||||
}
|
||||
|
||||
/** Return `true` if serialization of the header is complete.
|
||||
|
||||
This function indicates whether or not all octets
|
||||
corresponding to the serialized representation of the
|
||||
header have been successfully delivered to the stream.
|
||||
*/
|
||||
bool
|
||||
is_header_done() const
|
||||
{
|
||||
return header_done_;
|
||||
}
|
||||
|
||||
/** Return `true` if serialization is complete
|
||||
|
||||
The operation is complete when all octets corresponding
|
||||
to the serialized representation of the message have been
|
||||
successfully delivered to the stream.
|
||||
*/
|
||||
bool
|
||||
is_done() const
|
||||
{
|
||||
return s_ == do_complete;
|
||||
}
|
||||
|
||||
/** Return `true` if Connection: close semantic is indicated.
|
||||
|
||||
After serialization is complete, if there is an
|
||||
underlying network connection then it should be closed if
|
||||
this function returns `true`.
|
||||
*/
|
||||
bool
|
||||
needs_close() const
|
||||
{
|
||||
return close_;
|
||||
}
|
||||
|
||||
/** Returns the next set of buffers in the serialization.
|
||||
|
||||
This function will attempt to call the `visit` function
|
||||
object with a @b ConstBufferSequence of unspecified type
|
||||
representing the next set of buffers in the serialization
|
||||
of the message represented by this object.
|
||||
|
||||
If there are no more buffers in the serialization, the
|
||||
visit function will not be called. In this case, no error
|
||||
will be indicated, and the function @ref is_done will
|
||||
return `true`.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@param visit The function to call. The equivalent function
|
||||
signature of this object must be:
|
||||
@code template<class ConstBufferSequence>
|
||||
void visit(error_code&, ConstBufferSequence const&);
|
||||
@endcode
|
||||
The function is not copied, if no error occurs it will be
|
||||
invoked before the call to @ref get returns.
|
||||
|
||||
*/
|
||||
template<class Visit>
|
||||
void
|
||||
get(error_code& ec, Visit&& visit);
|
||||
|
||||
/** Consume buffer octets in the serialization.
|
||||
|
||||
This function should be called after one or more octets
|
||||
contained in the buffers provided in the prior call
|
||||
to @ref get have been used.
|
||||
|
||||
After a call to @ref consume, callers should check the
|
||||
return value of @ref is_done to determine if the entire
|
||||
message has been serialized.
|
||||
|
||||
@param n The number of octets to consume. This number must
|
||||
be greater than zero and no greater than the number of
|
||||
octets in the buffers provided in the prior call to @ref get.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n);
|
||||
};
|
||||
|
||||
/** Return a stateful object to serialize an HTTP message.
|
||||
|
||||
This convenience function makes it easier to declare
|
||||
the variable for a given message.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator = empty_decorator,
|
||||
class Allocator = std::allocator<char>>
|
||||
inline
|
||||
serializer<isRequest, Body, Fields,
|
||||
typename std::decay<Decorator>::type,
|
||||
typename std::decay<Allocator>::type>
|
||||
make_serializer(message<isRequest, Body, Fields> const& m,
|
||||
Decorator const& decorator = Decorator{},
|
||||
Allocator const& allocator = Allocator{})
|
||||
{
|
||||
return serializer<isRequest, Body, Fields,
|
||||
typename std::decay<Decorator>::type,
|
||||
typename std::decay<Allocator>::type>{
|
||||
m, decorator, allocator};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/serializer.ipp>
|
||||
|
||||
#endif
|
@@ -13,6 +13,7 @@
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/serializer.hpp>
|
||||
#include <beast/http/detail/chunk_encode.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/async_result.hpp>
|
||||
@@ -27,410 +28,109 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A chunk decorator which does nothing.
|
||||
/** Write some serialized message data to a stream.
|
||||
|
||||
When selected as a chunk decorator, objects of this type
|
||||
affect the output of messages specifying chunked
|
||||
transfer encodings as follows:
|
||||
This function is used to write serialized message data to the
|
||||
stream. The function call will block until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li chunk headers will have empty chunk extensions, and
|
||||
@li An error occurs on the stream.
|
||||
|
||||
@li final chunks will have an empty set of trailers.
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
|
||||
@see @ref serializer
|
||||
@param sr The serializer to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see @ref async_write_some, @ref serializer
|
||||
*/
|
||||
struct empty_decorator
|
||||
{
|
||||
template<class ConstBufferSequence>
|
||||
string_view
|
||||
operator()(ConstBufferSequence const&) const
|
||||
{
|
||||
return {"\r\n"};
|
||||
}
|
||||
|
||||
string_view
|
||||
operator()(boost::asio::null_buffers) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/** Provides stream-oriented HTTP message serialization functionality.
|
||||
|
||||
Objects of this type may be used to perform synchronous or
|
||||
asynchronous serialization of an HTTP message on a stream.
|
||||
Unlike functions such as @ref write or @ref async_write,
|
||||
the stream operations provided here guarantee that bounded
|
||||
work will be performed. This is accomplished by making one
|
||||
or more calls to the underlying stream's `write_some` or
|
||||
`async_write_some` member functions. In order to fully
|
||||
serialize the message, multiple calls are required.
|
||||
|
||||
The ability to incrementally serialize a message, peforming
|
||||
bounded work at each iteration is useful in many scenarios,
|
||||
such as:
|
||||
|
||||
@li Setting consistent, per-call timeouts
|
||||
|
||||
@li Efficiently relaying body content from another stream
|
||||
|
||||
@li Performing application-layer flow control
|
||||
|
||||
To use this class, construct an instance with the message
|
||||
to be sent. To make it easier to declare the type, the
|
||||
helper function @ref make_serializer is provided:
|
||||
|
||||
The implementation will automatically perform chunk encoding
|
||||
if the contents of the message indicate that chunk encoding
|
||||
is required. If the semantics of the message indicate that
|
||||
the connection should be closed after the message is sent,
|
||||
the error delivered from stream operations will be
|
||||
`boost::asio::error::eof`.
|
||||
|
||||
@code
|
||||
template<class Stream>
|
||||
void send(Stream& stream, request<string_body> const& msg)
|
||||
{
|
||||
serializer<true, string_body, fields> w{msg};
|
||||
do
|
||||
{
|
||||
w.write_some();
|
||||
}
|
||||
while(! w.is_done());
|
||||
}
|
||||
@endcode
|
||||
|
||||
Upon construction, an optional chunk decorator may be
|
||||
specified. This decorator is a function object called with
|
||||
each buffer sequence of the body when the chunked transfer
|
||||
encoding is indicate in the message header. The decorator
|
||||
will be called with an empty buffer sequence (actually
|
||||
the type `boost::asio::null_buffers`) to indicate the
|
||||
final chunk. The decorator may return a string which forms
|
||||
the chunk extension for chunks, and the field trailers
|
||||
for the final chunk.
|
||||
|
||||
In C++11 the decorator must be declared as a class or
|
||||
struct with a templated operator() thusly:
|
||||
|
||||
@code
|
||||
// The implementation guarantees that operator()
|
||||
// will be called only after the view returned by
|
||||
// any previous calls to operator() are no longer
|
||||
// needed. The decorator instance is intended to
|
||||
// manage the lifetime of the storage for all returned
|
||||
// views.
|
||||
//
|
||||
struct decorator
|
||||
{
|
||||
// Returns the chunk-extension for each chunk.
|
||||
// The buffer returned must include a trailing "\r\n",
|
||||
// and the leading semicolon (";") if one or more
|
||||
// chunk extensions are specified.
|
||||
//
|
||||
template<class ConstBufferSequence>
|
||||
string_view
|
||||
operator()(ConstBufferSequence const&) const;
|
||||
|
||||
// Returns a set of field trailers for the final chunk.
|
||||
// Each field should be formatted according to rfc7230
|
||||
// including the trailing "\r\n" for each field. If
|
||||
// no trailers are indicated, an empty string is returned.
|
||||
//
|
||||
string_view
|
||||
operator()(boost::asio::null_buffers) const;
|
||||
};
|
||||
@endcode
|
||||
|
||||
@par Thread Safety
|
||||
@e Distinct @e objects: Safe.@n
|
||||
@e Shared @e objects: Unsafe.
|
||||
|
||||
@tparam isRequest `true` if the message is a request.
|
||||
|
||||
@tparam Body The body type of the message.
|
||||
|
||||
@tparam Fields The type of fields in the message.
|
||||
|
||||
@tparam Decorator The type of chunk decorator to use.
|
||||
|
||||
@tparam Allocator The type of allocator to use.
|
||||
|
||||
@see @ref make_serializer
|
||||
*/
|
||||
template<
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator = empty_decorator,
|
||||
class Allocator = std::allocator<char>
|
||||
>
|
||||
class serializer
|
||||
{
|
||||
template<class Stream, class Handler>
|
||||
class async_op;
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write_some(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr);
|
||||
|
||||
enum
|
||||
{
|
||||
do_init = 0,
|
||||
do_header_only = 10,
|
||||
do_header = 20,
|
||||
do_body = 40,
|
||||
|
||||
/** Write some serialized message data to a stream.
|
||||
|
||||
This function is used to write serialized message data to the
|
||||
stream. The function call will block until one of the following
|
||||
conditions is true:
|
||||
|
||||
do_init_c = 50,
|
||||
do_header_only_c = 60,
|
||||
do_header_c = 70,
|
||||
do_body_c = 90,
|
||||
do_final_c = 100,
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
do_complete = 110
|
||||
};
|
||||
@li An error occurs on the stream.
|
||||
|
||||
void split(bool, std::true_type) {}
|
||||
void split(bool v, std::false_type) { split_ = v; }
|
||||
|
||||
using buffer_type =
|
||||
basic_multi_buffer<Allocator>;
|
||||
|
||||
using reader = typename Body::reader;
|
||||
|
||||
using is_deferred =
|
||||
typename reader::is_deferred;
|
||||
|
||||
using cb0_t = consuming_buffers<buffers_view<
|
||||
typename buffer_type::const_buffers_type, // header
|
||||
typename reader::const_buffers_type>>; // body
|
||||
|
||||
using cb1_t = consuming_buffers<
|
||||
typename reader::const_buffers_type>; // body
|
||||
|
||||
using ch0_t = consuming_buffers<buffers_view<
|
||||
typename buffer_type::const_buffers_type, // header
|
||||
detail::chunk_header, // chunk-header
|
||||
boost::asio::const_buffers_1, // chunk-ext+\r\n
|
||||
typename reader::const_buffers_type, // body
|
||||
boost::asio::const_buffers_1>>; // crlf
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
|
||||
using ch1_t = consuming_buffers<buffers_view<
|
||||
detail::chunk_header, // chunk-header
|
||||
boost::asio::const_buffers_1, // chunk-ext+\r\n
|
||||
typename reader::const_buffers_type, // body
|
||||
boost::asio::const_buffers_1>>; // crlf
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
|
||||
using ch2_t = consuming_buffers<buffers_view<
|
||||
boost::asio::const_buffers_1, // chunk-final
|
||||
boost::asio::const_buffers_1, // trailers
|
||||
boost::asio::const_buffers_1>>; // crlf
|
||||
@param sr The serializer to use.
|
||||
|
||||
message<isRequest, Body, Fields> const& m_;
|
||||
Decorator d_;
|
||||
std::size_t limit_ =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
boost::optional<reader> rd_;
|
||||
buffer_type b_;
|
||||
boost::variant<boost::blank,
|
||||
cb0_t, cb1_t, ch0_t, ch1_t, ch2_t> v_;
|
||||
int s_;
|
||||
bool split_ = is_deferred::value;
|
||||
bool header_done_ = false;
|
||||
bool chunked_;
|
||||
bool close_;
|
||||
bool more_;
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
@see @ref async_write_some, @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write_some(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec);
|
||||
|
||||
@param msg The message to serialize. The message object
|
||||
must remain valid for the lifetime of the write stream.
|
||||
/** Start an asynchronous write of some serialized message data to a stream.
|
||||
|
||||
@param decorator An optional decorator to use.
|
||||
This function is used to asynchronously write serialized
|
||||
message data to the stream. The function call always returns
|
||||
immediately. The asynchronous operation will continue until
|
||||
one of the following conditions is true:
|
||||
|
||||
@param alloc An optional allocator to use.
|
||||
*/
|
||||
explicit
|
||||
serializer(message<isRequest, Body, Fields> const& msg,
|
||||
Decorator const& decorator = Decorator{},
|
||||
Allocator const& alloc = Allocator{});
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
/// Returns the maximum number of bytes that will be written in each operation
|
||||
std::size_t
|
||||
limit() const
|
||||
{
|
||||
return limit_;
|
||||
}
|
||||
@li An error occurs on the stream.
|
||||
|
||||
/** Returns `true` if we will pause after writing the header.
|
||||
*/
|
||||
bool
|
||||
split() const
|
||||
{
|
||||
return split_;
|
||||
}
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
|
||||
/** Set whether the header and body are written separately.
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
|
||||
When the split feature is enabled, the implementation will
|
||||
write only the octets corresponding to the serialized header
|
||||
first. If the header has already been written, this function
|
||||
will have no effect on output. This function should be called
|
||||
before any writes take place, otherwise the behavior is
|
||||
undefined.
|
||||
*/
|
||||
void
|
||||
split(bool v)
|
||||
{
|
||||
split(v, is_deferred{});
|
||||
}
|
||||
@param sr The serializer to use for writing.
|
||||
|
||||
/** Set the maximum number of bytes that will be written in each operation.
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required. The
|
||||
equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& ec // Result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
By default, there is no limit on the size of writes.
|
||||
|
||||
@param n The byte limit. This must be greater than zero.
|
||||
*/
|
||||
void
|
||||
limit(std::size_t n)
|
||||
{
|
||||
limit_ = n;
|
||||
}
|
||||
|
||||
/** Return `true` if serialization of the header is complete.
|
||||
|
||||
This function indicates whether or not all octets
|
||||
corresponding to the serialized representation of the
|
||||
header have been successfully delivered to the stream.
|
||||
*/
|
||||
bool
|
||||
is_header_done() const
|
||||
{
|
||||
return header_done_;
|
||||
}
|
||||
|
||||
/** Return `true` if serialization is complete
|
||||
|
||||
The operation is complete when all octets corresponding
|
||||
to the serialized representation of the message have been
|
||||
successfully delivered to the stream.
|
||||
*/
|
||||
bool
|
||||
is_done() const
|
||||
{
|
||||
return s_ == do_complete;
|
||||
}
|
||||
|
||||
/** Write some serialized message data to the stream.
|
||||
|
||||
This function is used to write serialized message data to the
|
||||
stream. The function call will block until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until @ref is_done returns `true`. If the
|
||||
semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error delivered from
|
||||
this call will be `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncWriteStream>
|
||||
void
|
||||
write_some(SyncWriteStream& stream);
|
||||
|
||||
/** Write some serialized message data to the stream.
|
||||
|
||||
This function is used to write serialized message data to the
|
||||
stream. The function call will block until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until @ref is_done returns `true`. If the
|
||||
semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error delivered from
|
||||
this call will be `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
template<class SyncWriteStream>
|
||||
void
|
||||
write_some(SyncWriteStream& stream, error_code &ec);
|
||||
|
||||
/** Start an asynchronous write of some serialized message data.
|
||||
|
||||
This function is used to asynchronously write serialized
|
||||
message data to the stream. The function call always returns
|
||||
immediately. The asynchronous operation will continue until
|
||||
one of the following conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until @ref is_done returns `true`. If the
|
||||
semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error delivered from
|
||||
this call will be `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required. The
|
||||
equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& ec // Result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class AsyncWriteStream, class WriteHandler>
|
||||
@see @ref write_some, @ref serializer
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
#endif
|
||||
async_write_some(AsyncWriteStream& stream,
|
||||
async_write_some(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler);
|
||||
};
|
||||
|
||||
/** Return a stateful object to serialize an HTTP message.
|
||||
|
||||
This convenience function makes it easier to declare
|
||||
the variable for a given message.
|
||||
*/
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator = empty_decorator,
|
||||
class Allocator = std::allocator<char>>
|
||||
inline
|
||||
serializer<isRequest, Body, Fields,
|
||||
typename std::decay<Decorator>::type,
|
||||
typename std::decay<Allocator>::type>
|
||||
make_serializer(message<isRequest, Body, Fields> const& m,
|
||||
Decorator const& decorator = Decorator{},
|
||||
Allocator const& allocator = Allocator{})
|
||||
{
|
||||
return serializer<isRequest, Body, Fields,
|
||||
typename std::decay<Decorator>::type,
|
||||
typename std::decay<Allocator>::type>{
|
||||
m, decorator, allocator};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Write a HTTP/1 message to a stream.
|
||||
|
||||
|
@@ -52,6 +52,7 @@ unit-test http-tests :
|
||||
http/message_parser.cpp
|
||||
http/read.cpp
|
||||
http/rfc7230.cpp
|
||||
http/serializer.cpp
|
||||
http/string_body.cpp
|
||||
http/type_traits.cpp
|
||||
http/write.cpp
|
||||
|
@@ -22,6 +22,7 @@ add_executable (http-tests
|
||||
message_parser.cpp
|
||||
read.cpp
|
||||
rfc7230.cpp
|
||||
serializer.cpp
|
||||
string_body.cpp
|
||||
type_traits.cpp
|
||||
write.cpp
|
||||
|
@@ -77,11 +77,11 @@ public:
|
||||
req.body = "Hello, world!";
|
||||
|
||||
// send header
|
||||
auto ws = make_serializer(req);
|
||||
auto sr = make_serializer(req);
|
||||
for(;;)
|
||||
{
|
||||
ws.async_write_some(stream, yield);
|
||||
if(ws.is_header_done())
|
||||
async_write_some(stream, sr, yield);
|
||||
if(sr.is_header_done())
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ public:
|
||||
}
|
||||
|
||||
// send body
|
||||
while(! ws.is_done())
|
||||
ws.async_write_some(stream, yield);
|
||||
while(! sr.is_done())
|
||||
async_write_some(stream, sr, yield);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
m.target("/");
|
||||
m.fields.insert("User-Agent", "test");
|
||||
m.fields.insert("Content-Length", s.size());
|
||||
auto ws = make_serializer(m);
|
||||
auto sr = make_serializer(m);
|
||||
error_code ec;
|
||||
for(;;)
|
||||
{
|
||||
@@ -171,7 +171,7 @@ public:
|
||||
m.body.second = s.size() > 3;
|
||||
for(;;)
|
||||
{
|
||||
ws.write_some(p.client, ec);
|
||||
write_some(p.client, sr, ec);
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
ec = {};
|
||||
@@ -179,12 +179,12 @@ public:
|
||||
}
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
return;
|
||||
if(ws.is_done())
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
s.erase(s.begin(), s.begin() +
|
||||
boost::asio::buffer_size(*m.body.first));
|
||||
if(ws.is_done())
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
BEAST_EXPECT(p.server.str() ==
|
||||
|
9
test/http/serializer.cpp
Normal file
9
test/http/serializer.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/serializer.hpp>
|
@@ -765,15 +765,15 @@ public:
|
||||
isRequest, Body, Fields> const& m, error_code& ec,
|
||||
Decorator const& decorator = Decorator{})
|
||||
{
|
||||
auto ws = make_serializer(m, decorator);
|
||||
auto sr = make_serializer(m, decorator);
|
||||
for(;;)
|
||||
{
|
||||
stream.nwrite = 0;
|
||||
ws.write_some(stream, ec);
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BEAST_EXPECT(stream.nwrite <= 1);
|
||||
if(ws.is_done())
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -787,15 +787,15 @@ public:
|
||||
error_code& ec, yield_context yield,
|
||||
Decorator const& decorator = Decorator{})
|
||||
{
|
||||
auto ws = make_serializer(m);
|
||||
auto sr = make_serializer(m);
|
||||
for(;;)
|
||||
{
|
||||
stream.nwrite = 0;
|
||||
ws.async_write_some(stream, yield[ec]);
|
||||
async_write_some(stream, sr, yield[ec]);
|
||||
if(ec)
|
||||
return;
|
||||
BEAST_EXPECT(stream.nwrite <= 1);
|
||||
if(ws.is_done())
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -857,12 +857,12 @@ public:
|
||||
{
|
||||
auto m = m0;
|
||||
error_code ec;
|
||||
serializer<false, Body, fields> w{m};
|
||||
w.split(true);
|
||||
serializer<false, Body, fields> sr{m};
|
||||
sr.split(true);
|
||||
for(;;)
|
||||
{
|
||||
w.write_some(p.client);
|
||||
if(w.is_header_done())
|
||||
write_some(p.client, sr);
|
||||
if(sr.is_header_done())
|
||||
break;
|
||||
}
|
||||
BEAST_EXPECT(! m.body.read);
|
||||
@@ -871,12 +871,12 @@ public:
|
||||
{
|
||||
auto m = m0;
|
||||
error_code ec;
|
||||
serializer<false, Body, fields> w{m};
|
||||
w.split(true);
|
||||
serializer<false, Body, fields> sr{m};
|
||||
sr.split(true);
|
||||
for(;;)
|
||||
{
|
||||
w.async_write_some(p.client, yield);
|
||||
if(w.is_header_done())
|
||||
async_write_some(p.client, sr, yield);
|
||||
if(sr.is_header_done())
|
||||
break;
|
||||
}
|
||||
BEAST_EXPECT(! m.body.read);
|
||||
@@ -921,12 +921,12 @@ public:
|
||||
auto m = m0;
|
||||
error_code ec;
|
||||
test::string_ostream so{get_io_service(), 3};
|
||||
serializer<false, Body, fields> w{m};
|
||||
w.split(true);
|
||||
serializer<false, Body, fields> sr{m};
|
||||
sr.split(true);
|
||||
for(;;)
|
||||
{
|
||||
w.write_some(p.client);
|
||||
if(w.is_header_done())
|
||||
write_some(p.client, sr);
|
||||
if(sr.is_header_done())
|
||||
break;
|
||||
}
|
||||
BEAST_EXPECT(! m.body.read);
|
||||
@@ -935,12 +935,12 @@ public:
|
||||
{
|
||||
auto m = m0;
|
||||
error_code ec;
|
||||
serializer<false, Body, fields> w{m};
|
||||
w.split(true);
|
||||
serializer<false, Body, fields> sr{m};
|
||||
sr.split(true);
|
||||
for(;;)
|
||||
{
|
||||
w.async_write_some(p.client, yield);
|
||||
if(w.is_header_done())
|
||||
async_write_some(p.client, sr, yield);
|
||||
if(sr.is_header_done())
|
||||
break;
|
||||
}
|
||||
BEAST_EXPECT(! m.body.read);
|
||||
@@ -969,13 +969,13 @@ public:
|
||||
m.body.first = boost::none;
|
||||
m.body.second = true;
|
||||
|
||||
auto w = make_serializer(m);
|
||||
auto sr = make_serializer(m);
|
||||
|
||||
// send the header first, so the
|
||||
// other end gets it right away
|
||||
for(;;)
|
||||
{
|
||||
w.write_some(output, ec);
|
||||
write_some(output, sr, ec);
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
ec = {};
|
||||
@@ -1008,7 +1008,7 @@ public:
|
||||
// write to output
|
||||
for(;;)
|
||||
{
|
||||
w.write_some(output, ec);
|
||||
write_some(output, sr, ec);
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
ec = {};
|
||||
@@ -1016,7 +1016,7 @@ public:
|
||||
}
|
||||
if(ec)
|
||||
return;
|
||||
if(w.is_done())
|
||||
if(sr.is_done())
|
||||
goto is_done;
|
||||
}
|
||||
b.consume(b.size());
|
||||
|
Reference in New Issue
Block a user