mirror of
https://github.com/boostorg/beast.git
synced 2025-07-29 20:37:31 +02:00
Add const and non-const overloads for message based http writes:
fix #1113 This solves a problem where the message-oriented synchronous HTTP write algorithms do not support messages whose body writer requires a non-const reference to the message in order to construct. In addition, the message-oriented async_write algorithm is modified to support messages passed by const reference when possible, based on the body writer attributes.
This commit is contained in:
@ -9,6 +9,7 @@ Version 170:
|
||||
* Remove deprecated serializer::reader_impl()
|
||||
* Remove deprecated Body reader and writer ctor signatures
|
||||
* Add is_mutable_body_writer metafunction
|
||||
* Add const and non-const overloads for message based http writes
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -333,6 +333,14 @@ class write_msg_op
|
||||
, sr(m_)
|
||||
{
|
||||
}
|
||||
|
||||
data(Handler const&, Stream& s_, message<
|
||||
isRequest, Body, Fields> const& m_)
|
||||
: s(s_)
|
||||
, wg(s.get_executor())
|
||||
, sr(m_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
@ -751,7 +759,33 @@ async_write(
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
@ -773,7 +807,30 @@ write(
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
return write(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
@ -793,8 +850,10 @@ template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||
WriteHandler, void(error_code, std::size_t))
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||
WriteHandler, void(error_code, std::size_t))>::type
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
@ -818,6 +877,37 @@ async_write(
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||
WriteHandler, void(error_code, std::size_t))>::type
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
BOOST_BEAST_HANDLER_INIT(
|
||||
WriteHandler, void(error_code, std::size_t));
|
||||
detail::write_msg_op<
|
||||
AsyncWriteStream,
|
||||
BOOST_ASIO_HANDLER_TYPE(WriteHandler,
|
||||
void(error_code, std::size_t)),
|
||||
isRequest, Body, Fields>{
|
||||
std::move(init.completion_handler), stream, msg}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/http/detail/chunk_encode.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
@ -427,6 +428,9 @@ async_write(
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for @b Body returns `true`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@ -441,7 +445,54 @@ async_write(
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg);
|
||||
|
||||
/** Write a complete message to a stream.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
HTTP/1. The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for @b Body returns `false`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg);
|
||||
@ -459,6 +510,9 @@ write(
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for @b Body returns `true`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@ -473,7 +527,55 @@ write(
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a complete message to a stream.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
HTTP/1. The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for @b Body returns `false`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
@ -495,6 +597,9 @@ write(
|
||||
until this operation completes. The algorithm will use a temporary
|
||||
@ref serializer with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for @b Body returns `true`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@ -520,13 +625,78 @@ template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||
WriteHandler, void(error_code, std::size_t))
|
||||
#else
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||
WriteHandler, void(error_code, std::size_t))>::type
|
||||
#endif
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
WriteHandler&& handler);
|
||||
|
||||
/** Write a complete message to a stream asynchronously.
|
||||
|
||||
This function is used to write a complete message to a stream asynchronously
|
||||
using HTTP/1. The function call always returns immediately. The asynchronous
|
||||
operation will continue until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other writes
|
||||
until this operation completes. The algorithm will use a temporary
|
||||
@ref serializer with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for @b Body returns `false`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler Invoked when the operation completes.
|
||||
The handler may be moved or copied as needed.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the number of bytes written to the stream
|
||||
); @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_context::post`.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||
WriteHandler, void(error_code, std::size_t))
|
||||
#else
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||
WriteHandler, void(error_code, std::size_t))>::type
|
||||
#endif
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
WriteHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Serialize an HTTP/1 header to a `std::ostream`.
|
||||
|
@ -50,8 +50,9 @@ public:
|
||||
boost::asio::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(header<isRequest, Fields> const&, value_type const& b)
|
||||
writer(
|
||||
header<isRequest, Fields> const&,
|
||||
value_type const& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
@ -94,8 +95,9 @@ public:
|
||||
boost::asio::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(header<isRequest, Fields> const&, value_type const& b)
|
||||
writer(
|
||||
header<isRequest, Fields> const&,
|
||||
value_type const& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
@ -890,6 +892,107 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
struct const_body_writer
|
||||
{
|
||||
struct value_type{};
|
||||
|
||||
struct writer
|
||||
{
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
writer(
|
||||
header<isRequest, Fields> const&,
|
||||
value_type const&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return {{const_buffers_type{"", 0}, false}};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct mutable_body_writer
|
||||
{
|
||||
struct value_type{};
|
||||
|
||||
struct writer
|
||||
{
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
writer(
|
||||
header<isRequest, Fields>&,
|
||||
value_type&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return {{const_buffers_type{"", 0}, false}};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void
|
||||
testBodyWriters()
|
||||
{
|
||||
{
|
||||
test::stream s{ioc_};
|
||||
message<true, const_body_writer> m;
|
||||
try
|
||||
{
|
||||
write(s, m);
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
}
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
test::stream s{ioc_};
|
||||
message<true, const_body_writer> m;
|
||||
write(s, m, ec);
|
||||
}
|
||||
{
|
||||
test::stream s{ioc_};
|
||||
message<true, mutable_body_writer> m;
|
||||
try
|
||||
{
|
||||
write(s, m);
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
}
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
test::stream s{ioc_};
|
||||
message<true, mutable_body_writer> m;
|
||||
write(s, m, ec);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@ -912,6 +1015,7 @@ public:
|
||||
testWriteStream<test_body< true, true>>(yield);
|
||||
});
|
||||
testAsioHandlerInvoke();
|
||||
testBodyWriters();
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user