boost/beast/http/serializer.hpp
//
// Copyright (c) 2016-2019 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)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_HTTP_SERIALIZER_HPP
#define BOOST_BEAST_HTTP_SERIALIZER_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/buffers_cat.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/detail/variant.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/chunk_encode.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/optional.hpp>
namespace boost {
namespace beast {
namespace http {
/** Provides buffer oriented HTTP message serialization functionality.
An object of this type is used to serialize a complete
HTTP message into a sequence of octets. To use this class,
construct an instance with the message to be serialized.
The implementation will automatically perform chunk encoding
if the contents of the message indicate that chunk encoding
is required.
Chunked output produced by the serializer never contains chunk
extensions or trailers, and the location of chunk boundaries
is not specified. If callers require chunk extensions, trailers,
or control over the exact contents of each chunk they should
use the serializer to write just the message header, and then
assume control over serializing the chunked payload by using
the chunk buffer sequence types @ref chunk_body, @ref chunk_crlf,
@ref chunk_header, and @ref chunk_last.
@note
Moving or copying the serializer after the first call to
@ref serializer::next results in undefined behavior. Try to heap-allocate
the serializer object if you need to move the serializer between multiple
async operations (for example, between a call to `async_write_header` and
`async_write`).
@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.
*/
template<
bool isRequest,
class Body,
class Fields = fields>
class serializer
{
public:
static_assert(is_body<Body>::value,
"Body type requirements not met");
static_assert(is_body_writer<Body>::value,
"BodyWriter type requirements not met");
/** The type of message this serializer uses
This may be const or non-const depending on the
implementation of the corresponding <em>BodyWriter</em>.
*/
#if BOOST_BEAST_DOXYGEN
using value_type = __implementation_defined__;
#else
using value_type = typename std::conditional<
std::is_constructible<typename Body::writer,
header<isRequest, Fields>&,
typename Body::value_type&>::value &&
! std::is_constructible<typename Body::writer,
header<isRequest, Fields> const&,
typename Body::value_type const&>::value,
message<isRequest, Body, Fields>,
message<isRequest, Body, Fields> const>::type;
#endif
private:
enum
{
do_construct = 0,
do_init = 10,
do_header_only = 20,
do_header = 30,
do_body = 40,
do_init_c = 50,
do_header_only_c = 60,
do_header_c = 70,
do_body_c = 80,
do_final_c = 90,
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
do_body_final_c = 100,
do_all_c = 110,
#endif
do_complete = 120
};
void fwrinit(std::true_type);
void fwrinit(std::false_type);
template<std::size_t, class Visit>
void
do_visit(error_code& ec, Visit& visit);
using writer = typename Body::writer;
using cb1_t = buffers_suffix<typename
Fields::writer::const_buffers_type>; // header
using pcb1_t = buffers_prefix_view<cb1_t const&>;
using cb2_t = buffers_suffix<buffers_cat_view<
typename Fields::writer::const_buffers_type,// header
typename writer::const_buffers_type>>; // body
using pcb2_t = buffers_prefix_view<cb2_t const&>;
using cb3_t = buffers_suffix<
typename writer::const_buffers_type>; // body
using pcb3_t = buffers_prefix_view<cb3_t const&>;
using cb4_t = buffers_suffix<buffers_cat_view<
typename Fields::writer::const_buffers_type,// header
detail::chunk_size, // chunk-size
net::const_buffer, // chunk-ext
chunk_crlf, // crlf
typename writer::const_buffers_type, // body
chunk_crlf>>; // crlf
using pcb4_t = buffers_prefix_view<cb4_t const&>;
using cb5_t = buffers_suffix<buffers_cat_view<
detail::chunk_size, // chunk-header
net::const_buffer, // chunk-ext
chunk_crlf, // crlf
typename writer::const_buffers_type, // body
chunk_crlf>>; // crlf
using pcb5_t = buffers_prefix_view<cb5_t const&>;
using cb6_t = buffers_suffix<buffers_cat_view<
detail::chunk_size, // chunk-header
net::const_buffer, // chunk-size
chunk_crlf, // crlf
typename writer::const_buffers_type, // body
chunk_crlf, // crlf
net::const_buffer, // chunk-final
net::const_buffer, // trailers
chunk_crlf>>; // crlf
using pcb6_t = buffers_prefix_view<cb6_t const&>;
using cb7_t = buffers_suffix<buffers_cat_view<
typename Fields::writer::const_buffers_type,// header
detail::chunk_size, // chunk-size
net::const_buffer, // chunk-ext
chunk_crlf, // crlf
typename writer::const_buffers_type, // body
chunk_crlf, // crlf
net::const_buffer, // chunk-final
net::const_buffer, // trailers
chunk_crlf>>; // crlf
using pcb7_t = buffers_prefix_view<cb7_t const&>;
using cb8_t = buffers_suffix<buffers_cat_view<
net::const_buffer, // chunk-final
net::const_buffer, // trailers
chunk_crlf>>; // crlf
using pcb8_t = buffers_prefix_view<cb8_t const&>;
value_type& m_;
writer wr_;
boost::optional<typename Fields::writer> fwr_;
beast::detail::variant<
cb1_t, cb2_t, cb3_t, cb4_t,
cb5_t ,cb6_t, cb7_t, cb8_t> v_;
beast::detail::variant<
pcb1_t, pcb2_t, pcb3_t, pcb4_t,
pcb5_t ,pcb6_t, pcb7_t, pcb8_t> pv_;
std::size_t limit_ =
(std::numeric_limits<std::size_t>::max)();
int s_ = do_construct;
bool split_ = false;
bool header_done_ = false;
bool more_ = false;
public:
/** Move Constructor
@note
Moving or copying the serializer after the first call to
@ref serializer::next results in undefined behavior. Try to heap-allocate
the serializer object if you need to move the serializer between multiple
async operations (for example, between a call to `async_write_header` and
`async_write`).
*/
serializer(serializer&&) = default;
/** Copy Constructor
@note
Moving or copying the serializer after the first call to
@ref serializer::next results in undefined behavior. Try to heap-allocate
the serializer object if you need to move the serializer between multiple
async operations (for example, between a call to `async_write_header` and
`async_write`).
*/
serializer(serializer const&) = default;
/// Assignment
serializer& operator=(serializer const&) = delete;
/** Constructor
The implementation guarantees that the message passed on
construction will not be accessed until the first call to
@ref next. This allows the message to be lazily created.
For example, if the header is filled in before serialization.
@param msg A reference to the message to serialize, which must
remain valid for the lifetime of the serializer. Depending on
the type of Body used, this may or may not be a `const` reference.
@note This function participates in overload resolution only if
Body::writer is constructible from a `const` message reference.
*/
explicit
serializer(value_type& msg);
/// Returns the message being serialized
value_type&
get()
{
return m_;
}
/// Returns the serialized buffer size limit
std::size_t
limit()
{
return limit_;
}
/** Set the serialized buffer size limit
This function adjusts the limit on the maximum size of the
buffers passed to the visitor. The new size limit takes effect
in the following call to @ref next.
The default is no buffer size limit.
@param limit The new buffer size limit. If this number
is zero, the size limit is removed.
*/
void
limit(std::size_t limit)
{
limit_ = limit > 0 ? limit :
(std::numeric_limits<std::size_t>::max)();
}
/** Returns `true` if we will pause after writing the complete header.
*/
bool
split()
{
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.
*/
void
split(bool v)
{
split_ = v;
}
/** Return `true` if serialization of the header is complete.
This function indicates whether or not all buffers containing
serialized header octets have been retrieved.
*/
bool
is_header_done()
{
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 retrieved.
*/
bool
is_done() const
{
return s_ == do_complete;
}
/** Returns the next set of buffers in the serialization.
This function will attempt to call the `visit` function
object with a <em>ConstBufferSequence</em> 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 next returns.
*/
template<class Visit>
void
next(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 next 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 next.
*/
void
consume(std::size_t n);
/** Provides low-level access to the associated <em>BodyWriter</em>
This function provides access to the instance of the writer
associated with the body and created by the serializer
upon construction. The behavior of accessing this object
is defined by the specification of the particular writer
and its associated body.
@return A reference to the writer.
*/
writer&
writer_impl()
{
return wr_;
}
};
/// A serializer for HTTP/1 requests
template<class Body, class Fields = fields>
using request_serializer = serializer<true, Body, Fields>;
/// A serializer for HTTP/1 responses
template<class Body, class Fields = fields>
using response_serializer = serializer<false, Body, Fields>;
} // http
} // beast
} // boost
#include <boost/beast/http/impl/serializer.hpp>
#endif