Update for Net-TS Asio (API Change):

fix #769

The following classes are removed:

* handler_type
* async_result
* async_completion
* is_dynamic_buffer
* is_const_buffer_sequence
* is_mutable_buffer_sequence
* handler_alloc

Actions Required:

* Use BOOST_ASIO_HANDLER_TYPE instead of handler_type
* Use BOOST_ASIO_INITFN_RESULT_TYPE instead of async_result
* Use boost::asio::async_completion
* Use boost::asio::is_dynamic_buffer
* Use boost::asio::is_const_buffer_sequence
* Use boost::asio::is_mutable_buffer_sequence
* boost::asio::associated_allocator_t replaces handler_alloc
This commit is contained in:
Vinnie Falco
2017-10-23 17:11:27 -07:00
parent 9dc9ca13b9
commit 3a28e999af
173 changed files with 3214 additions and 4138 deletions
+51 -44
View File
@@ -61,7 +61,8 @@ is_ssl_handshake(
ConstBufferSequence const& buffers)
{
// Make sure buffers meets the requirements
static_assert(boost::beast::is_const_buffer_sequence<ConstBufferSequence>::value,
static_assert(
boost::asio::is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
// We need at least one byte to really do anything
@@ -135,7 +136,8 @@ detect_ssl(
// Make sure arguments meet the requirements
static_assert(beast::is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(beast::is_dynamic_buffer<DynamicBuffer>::value,
static_assert(
boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
// Loop until an error occurs or we get a definitive answer
@@ -219,15 +221,15 @@ detect_ssl(
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`.
manner equivalent to using `boost::asio::io_context::post`.
*/
template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionToken>
boost::beast::async_return_type< /*< The [link beast.ref.boost__beast__async_return_type `async_return_type`] customizes the return value based on the completion token >*/
BOOST_ASIO_INITFN_RESULT_TYPE( /*< `BOOST_ASIO_INITFN_RESULT_TYPE` customizes the return value based on the completion token >*/
CompletionToken,
void(boost::beast::error_code, boost::tribool)> /*< This is the signature for the completion handler >*/
void(boost::beast::error_code, boost::tribool)) /*< This is the signature for the completion handler >*/
async_detect_ssl(
AsyncReadStream& stream,
DynamicBuffer& buffer,
@@ -249,9 +251,9 @@ template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionToken>
boost::beast::async_return_type<
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
void(boost::beast::error_code, boost::tribool)>
void(boost::beast::error_code, boost::tribool))
async_detect_ssl(
AsyncReadStream& stream,
DynamicBuffer& buffer,
@@ -262,26 +264,29 @@ async_detect_ssl(
// Make sure arguments meet the requirements
static_assert(beast::is_async_read_stream<AsyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(beast::is_dynamic_buffer<DynamicBuffer>::value,
static_assert(
boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
// This helper manages some of the handler's lifetime and
// uses the result and handler specializations associated with
// the completion token to help customize the return value.
//
beast::async_completion<
boost::asio::async_completion<
CompletionToken, void(beast::error_code, boost::tribool)> init{token};
// Create the composed operation and launch it. This is a constructor
// call followed by invocation of operator(). We use handler_type
// call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE
// to convert the completion token into the correct handler type,
// allowing user defined specializations of the async result template
// to take effect.
//
detect_ssl_op<AsyncReadStream, DynamicBuffer, beast::handler_type<
CompletionToken, void(beast::error_code, boost::tribool)>>{
stream, buffer, init.completion_handler}(
beast::error_code{}, 0);
detect_ssl_op<
AsyncReadStream,
DynamicBuffer,
BOOST_ASIO_HANDLER_TYPE(
CompletionToken, void(beast::error_code, boost::tribool))>{
stream, buffer, init.completion_handler}(beast::error_code{}, 0);
// This hook lets the caller see a return value when appropriate.
// For example this might return std::future<error_code, boost::tribool> if
@@ -336,6 +341,35 @@ public:
{
}
// Associated allocator support. This is Asio's system for
// allowing the final completion handler to customize the
// memory allocation strategy used for composed operation
// states. A composed operation needs to use the same allocator
// as the final handler. These declarations achieve that.
using allocator_type =
boost::asio::associated_allocator_t<Handler>;
allocator_type
get_allocator() const noexcept
{
return boost::asio::get_associated_allocator(handler_);
}
// Executor hook. This is Asio's system for customizing the
// manner in which asynchronous completion handlers are invoked.
// A composed operation needs to use the same executor to invoke
// intermediate completion handlers as that used to invoke the
// final handler.
using executor_type = boost::asio::associated_executor_t<
Handler, decltype(stream_.get_executor())>;
executor_type get_executor() const noexcept
{
return boost::asio::get_associated_executor(handler_, stream_.get_executor());
}
// Determines if the next asynchronous operation represents a
// continuation of the asynchronous flow of control associated
// with the final handler. If we are past step two, it means
@@ -358,34 +392,6 @@ public:
asio_handler_is_continuation(std::addressof(op->handler_));
}
// Handler hook forwarding. These free functions invoke the hooks
// associated with the final completion handler. In effect, they
// make the Asio implementation treat our composed operation the
// same way it would treat the final completion handler for the
// purpose of memory allocation and invocation.
//
// Our implementation just passes through the call to the hook
// associated with the final handler.
friend void* asio_handler_allocate(std::size_t size, detect_ssl_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size, std::addressof(op->handler_));
}
friend void asio_handler_deallocate(void* p, std::size_t size, detect_ssl_op* op)
{
using boost::asio::asio_handler_deallocate;
return asio_handler_deallocate(p, size, std::addressof(op->handler_));
}
template<class Function>
friend void asio_handler_invoke(Function&& f, detect_ssl_op* op)
{
using boost::asio::asio_handler_invoke;
return asio_handler_invoke(f, std::addressof(op->handler_));
}
// Our main entry point. This will get called as our
// intermediate operations complete. Definition below.
//
@@ -424,12 +430,13 @@ operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
// We need to invoke the handler, but the guarantee
// is that the handler will not be called before the
// call to async_detect_ssl returns, so we must post
// the operation to the io_service. The helper function
// the operation to the executor. The helper function
// `bind_handler` lets us bind arguments in a safe way
// that preserves the type customization hooks of the
// original handler.
step_ = 1;
return stream_.get_io_service().post(
return boost::asio::post(
stream_.get_executor(),
beast::bind_handler(std::move(*this), ec, 0));
}
+239 -270
View File
@@ -10,21 +10,21 @@
#ifndef BOOST_BEAST_EXAMPLE_COMMON_SESSION_ALLOC_HPP
#define BOOST_BEAST_EXAMPLE_COMMON_SESSION_ALLOC_HPP
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/assert.hpp>
#include <boost/intrusive/list.hpp>
#include <algorithm>
#include <cstddef>
#include <utility>
namespace detail {
template<class Context>
class session_alloc_base
{
template<class Handler>
class wrapped_handler;
protected:
class pool_t
{
using hook_type =
@@ -78,23 +78,18 @@ class session_alloc_base
boost::intrusive::constant_time_size<
true>>::type;
Context* ctx_;
std::size_t refs_ = 1; // shared count
std::size_t high_ = 0; // highest used
std::size_t size_ = 0; // size of buf_
char* buf_ = nullptr; // a large block
list_type list_; // list of allocations
explicit
pool_t(Context* ctx)
: ctx_(ctx)
{
}
pool_t() = default;
public:
static
pool_t&
construct(Context* ctx);
construct();
~pool_t();
@@ -110,73 +105,178 @@ class session_alloc_base
void
dealloc(void* pv, std::size_t n);
};
pool_t& pool_;
public:
session_alloc_base& operator=(session_alloc_base const&) = delete;
~session_alloc_base()
{
pool_.release();
}
session_alloc_base()
: pool_(pool_t::construct(nullptr))
{
static_assert(std::is_same<Context, std::nullptr_t>::value,
"Context requirements not met");
}
session_alloc_base(session_alloc_base const& other)
: pool_(other.pool_.addref())
{
}
template<class DeducedContext, class = typename
std::enable_if<! std::is_same<
session_alloc_base<Context>,
typename std::decay<DeducedContext>::type
>::value>::type>
explicit
session_alloc_base(DeducedContext& ctx)
: pool_(pool_t::construct(std::addressof(ctx)))
{
static_assert(! std::is_same<Context, std::nullptr_t>::value,
"Context requirements not met");
}
template<class Handler>
wrapped_handler<typename std::decay<Handler>::type>
wrap(Handler&& handler)
{
return wrapped_handler<
typename std::decay<Handler>::type>(
std::forward<Handler>(handler), *this);
}
protected:
void*
alloc(std::size_t n)
{
return pool_.alloc(n);
}
void
dealloc(void* p, std::size_t n)
{
pool_.dealloc(p, n);
}
};
template<class Context>
auto
session_alloc_base<Context>::
pool_t::
construct() ->
pool_t&
{
return *(new pool_t);
}
template<class Context>
session_alloc_base<Context>::
pool_t::
~pool_t()
{
BOOST_ASSERT(list_.size() == 0);
if(buf_)
delete[] buf_;
}
template<class Context>
auto
session_alloc_base<Context>::
pool_t::
addref() ->
pool_t&
{
++refs_;
return *this;
}
template<class Context>
void
session_alloc_base<Context>::
pool_t::
release()
{
if(--refs_)
return;
delete this;
}
template<class Context>
void*
session_alloc_base<Context>::
pool_t::
alloc(std::size_t n)
{
if(list_.empty() && size_ < high_)
{
if(buf_)
delete[] buf_;
buf_ = new char[high_];
size_ = high_;
}
if(buf_)
{
char* end;
std::size_t used;
if(list_.empty())
{
end = buf_;
used = sizeof(element) + n;
}
else
{
end = list_.back().end();
used = list_.back().used() +
sizeof(element) + n;
}
if(end >= buf_ && end +
sizeof(element) + n <= buf_ + size_)
{
auto& e = *new(end) element{n, used};
list_.push_back(e);
high_ = (std::max)(high_, used);
return e.data();
}
}
std::size_t const used =
sizeof(element) + n + (
buf_ && ! list_.empty() ?
list_.back().used() : 0);
auto& e = *new(new char[sizeof(element) + n]) element{n, used};
list_.push_back(e);
high_ = (std::max)(high_, used);
return e.data();
}
template<class Context>
void
session_alloc_base<Context>::
pool_t::
dealloc(void* pv, std::size_t n)
{
auto& e = *(reinterpret_cast<element*>(pv) - 1);
BOOST_ASSERT(e.size() == n);
if( (e.end() > buf_ + size_) ||
reinterpret_cast<char*>(&e) < buf_)
{
list_.erase(list_.iterator_to(e));
e.~element();
delete[] reinterpret_cast<char*>(&e);
return;
}
list_.erase(list_.iterator_to(e));
e.~element();
}
} // detail
//------------------------------------------------------------------------------
template<class T, class Context = std::nullptr_t>
class session_alloc : public session_alloc_base<Context>
template<class T>
class session_alloc
: private detail::session_alloc_base<void>
{
template<class U, class C>
template<class U>
friend class session_alloc;
template<class Handler>
class wrapped_handler
{
// Can't friend partial specializations,
// so we just friend the whole thing.
template<class T, class Executor>
friend struct boost::asio::associated_executor;
Handler h_;
session_alloc<char> alloc_;
public:
wrapped_handler(wrapped_handler&&) = default;
wrapped_handler(wrapped_handler const&) = default;
template<class DeducedHandler>
wrapped_handler(
DeducedHandler&& h,
session_alloc const& alloc)
: h_(std::forward<DeducedHandler>(h))
, alloc_(alloc)
{
}
using allocator_type = session_alloc<char>;
allocator_type
get_allocator() const noexcept;
template<class... Args>
void
operator()(Args&&... args) const
{
h_(std::forward<Args>(args)...);
}
friend
bool
asio_handler_is_continuation(wrapped_handler* w)
{
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(std::addressof(w->h_));
}
};
using pool_t = typename
detail::session_alloc_base<void>::pool_t;
pool_t& pool_;
public:
using value_type = T;
using is_always_equal = std::false_type;
@@ -193,21 +293,32 @@ public:
using other = session_alloc<U>;
};
session_alloc() = default;
session_alloc(session_alloc const&) = default;
session_alloc& operator=(session_alloc const&) = delete;
~session_alloc()
{
pool_.release();
}
session_alloc()
: pool_(pool_t::construct())
{
}
session_alloc(session_alloc const& other) noexcept
: pool_(other.pool_.addref())
{
}
template<class U>
session_alloc(session_alloc<U> const& other)
: session_alloc_base<Context>(static_cast<
session_alloc_base<Context> const&>(other))
session_alloc(session_alloc<U> const& other) noexcept
: pool_(other.pool_)
{
}
explicit
session_alloc(Context& ctx)
: session_alloc_base<Context>(ctx)
{
}
template<class Handler>
wrapped_handler<typename std::decay<Handler>::type>
wrap(Handler&& handler);
value_type*
allocate(size_type n)
@@ -257,209 +368,67 @@ public:
{
return ! (lhs == rhs);
}
protected:
void*
alloc(std::size_t n)
{
return pool_.alloc(n);
}
void
dealloc(void* p, std::size_t n)
{
pool_.dealloc(p, n);
}
};
//------------------------------------------------------------------------------
template<class Context>
template<class T>
template<class Handler>
class session_alloc_base<Context>::wrapped_handler
auto
session_alloc<T>::
wrapped_handler<Handler>::
get_allocator() const noexcept ->
allocator_type
{
Handler h_;
session_alloc_base alloc_;
void*
alloc(std::size_t size)
{
return alloc_.alloc(size);
}
void
dealloc(void* p, std::size_t size)
{
alloc_.dealloc(p, size);
}
public:
wrapped_handler(wrapped_handler&&) = default;
wrapped_handler(wrapped_handler const&) = default;
template<class DeducedHandler>
explicit
wrapped_handler(DeducedHandler&& h,
session_alloc_base const& alloc)
: h_(std::forward<DeducedHandler>(h))
, alloc_(alloc)
{
}
template<class... Args>
void
operator()(Args&&... args) const
{
h_(std::forward<Args>(args)...);
}
friend
void*
asio_handler_allocate(
std::size_t size, wrapped_handler* w)
{
return w->alloc(size);
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size, wrapped_handler* w)
{
w->dealloc(p, size);
}
friend
bool
asio_handler_is_continuation(wrapped_handler* w)
{
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(std::addressof(w->h_));
}
template<class F>
friend
void
asio_handler_invoke(F&& f, wrapped_handler* w)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(w->h_));
}
};
return alloc_;
}
//------------------------------------------------------------------------------
template<class Context>
template<class T>
template<class Handler>
auto
session_alloc_base<Context>::
pool_t::
construct(Context* ctx) ->
pool_t&
session_alloc<T>::
wrap(Handler&& handler) ->
wrapped_handler<typename std::decay<Handler>::type>
{
using boost::asio::asio_handler_allocate;
return *new(asio_handler_allocate(
sizeof(pool_t), ctx)) pool_t{ctx};
return wrapped_handler<
typename std::decay<Handler>::type>(
std::forward<Handler>(handler), *this);
}
template<class Context>
session_alloc_base<Context>::
pool_t::
~pool_t()
namespace boost {
namespace asio {
template<class T, class Handler, class Executor>
struct associated_executor<
session_alloc<T>::wrapped_handler<Handler>, Executor>
{
BOOST_ASSERT(list_.size() == 0);
if(buf_)
using type = typename
associated_executor<Handler, Executor>::type;
static
type
get(session_alloc<T>::wrapped_handler<Handler> const& h,
Executor const& ex = Executor()) noexcept
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(buf_, size_, ctx_);
return associated_executor<
Handler, Executor>::get(h.h_, ex);
}
}
template<class Context>
auto
session_alloc_base<Context>::
pool_t::
addref() ->
pool_t&
{
++refs_;
return *this;
}
template<class Context>
void
session_alloc_base<Context>::
pool_t::
release()
{
if(--refs_)
return;
this->~pool_t();
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(this, sizeof(*this), ctx_);
}
template<class Context>
void*
session_alloc_base<Context>::
pool_t::
alloc(std::size_t n)
{
if(list_.empty() && size_ < high_)
{
if(buf_)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(buf_, size_, ctx_);
}
using boost::asio::asio_handler_allocate;
buf_ = reinterpret_cast<char*>(
asio_handler_allocate(high_, ctx_));
size_ = high_;
}
if(buf_)
{
char* end;
std::size_t used;
if(list_.empty())
{
end = buf_;
used = sizeof(element) + n;
}
else
{
end = list_.back().end();
used = list_.back().used() +
sizeof(element) + n;
}
if(end >= buf_ && end +
sizeof(element) + n <= buf_ + size_)
{
auto& e = *new(end) element{n, used};
list_.push_back(e);
high_ = (std::max)(high_, used);
return e.data();
}
}
std::size_t const used =
sizeof(element) + n + (
buf_ && ! list_.empty() ?
list_.back().used() : 0);
using boost::asio::asio_handler_allocate;
auto& e = *new(asio_handler_allocate(
sizeof(element) + n, ctx_)) element{n, used};
list_.push_back(e);
high_ = (std::max)(high_, used);
return e.data();
}
template<class Context>
void
session_alloc_base<Context>::
pool_t::
dealloc(void* pv, std::size_t n)
{
auto& e = *(reinterpret_cast<element*>(pv) - 1);
BOOST_ASSERT(e.size() == n);
if( (e.end() > buf_ + size_) ||
reinterpret_cast<char*>(&e) < buf_)
{
list_.erase(list_.iterator_to(e));
e.~element();
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
&e, sizeof(e) + n, ctx_);
return;
}
list_.erase(list_.iterator_to(e));
e.~element();
}
};
} // asio
} // boost
#endif
+9 -16
View File
@@ -52,9 +52,6 @@ public:
/// Structure for use with deprecated impl_type.
using impl_struct = typename stream_type::impl_struct;
/// (Deprecated: Use native_handle_type.) The underlying implementation type.
using impl_type = typename stream_type::impl_type;
/// The type of the next layer.
using next_layer_type = typename stream_type::next_layer_type;
@@ -64,14 +61,16 @@ public:
ssl_stream(
boost::asio::ip::tcp::socket socket,
boost::asio::ssl::context& ctx)
: p_(new stream_type{socket.get_io_service(), ctx})
: p_(new stream_type{
socket.get_executor().context(), ctx})
, ctx_(&ctx)
{
p_->next_layer() = std::move(socket);
}
ssl_stream(ssl_stream&& other)
: p_(new stream_type(other.get_io_service(), *other.ctx_))
: p_(new stream_type(
other.get_executor().context(), *other.ctx_))
, ctx_(other.ctx_)
{
using std::swap;
@@ -80,8 +79,8 @@ public:
ssl_stream& operator=(ssl_stream&& other)
{
std::unique_ptr<stream_type> p(
new stream_type{other.get_io_service(), other.ctx_});
std::unique_ptr<stream_type> p(new stream_type{
other.get_executor().context(), other.ctx_});
using std::swap;
swap(p_, p);
swap(p_, other.p_);
@@ -89,10 +88,10 @@ public:
return *this;
}
boost::asio::io_service&
get_io_service()
decltype(p_->get_executor())
get_executor() noexcept
{
return p_->get_io_service();
return p_->get_executor();
}
native_handle_type
@@ -101,12 +100,6 @@ public:
return p_->native_handle();
}
impl_type
impl()
{
return p_->impl();
}
next_layer_type const&
next_layer() const
{
-230
View File
@@ -1,230 +0,0 @@
//
// Copyright (c) 2016-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)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_EXAMPLE_COMMON_WRITE_MSG_HPP
#define BOOST_BEAST_EXAMPLE_COMMON_WRITE_MSG_HPP
#include <boost/beast/core/async_result.hpp>
#include <boost/beast/core/handler_ptr.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/write.hpp>
#include <boost/beast/http/type_traits.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
namespace detail {
/** Composed operation to send an HTTP message
This implements the composed operation needed for the
@ref async_write_msg function.
*/
template<
class AsyncWriteStream,
class Handler,
bool isRequest, class Body, class Fields>
class write_msg_op
{
// This composed operation has a state which is not trivial
// to copy (msg) so we need to store the state in an allocated
// object.
//
struct data
{
// The stream we are writing to
AsyncWriteStream& stream;
// The message we are sending. Note that this composed
// operation takes ownership of the message and destroys
// it when it is done.
//
boost::beast::http::message<isRequest, Body, Fields> msg;
// Serializer for the message
boost::beast::http::serializer<isRequest, Body, Fields> sr;
data(
Handler& handler,
AsyncWriteStream& stream_,
boost::beast::http::message<isRequest, Body, Fields>&& msg_)
: stream(stream_)
, msg(std::move(msg_))
, sr(msg)
{
boost::ignore_unused(handler);
}
};
// `handler_ptr` is a utility which helps to manage a composed
// operation's state. It is similar to a shared pointer, but
// it uses handler allocation hooks to allocate and free memory,
// and it also helps to meet Asio's deallocate-before-invocation
// guarantee.
//
boost::beast::handler_ptr<data, Handler> d_;
public:
// Asio can move and copy the handler, we support both
write_msg_op(write_msg_op&&) = default;
write_msg_op(write_msg_op const&) = default;
// Constructor
//
// We take the handler as a template type to
// support both const and rvalue references.
//
template<
class DeducedHandler,
class... Args>
write_msg_op(
DeducedHandler&& h,
AsyncWriteStream& s,
Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
}
// Entry point
//
// The initiation function calls this to start the operation
//
void
operator()()
{
auto& d = *d_;
boost::beast::http::async_write(
d.stream, d.sr, std::move(*this));
}
// Completion handler
//
// This gets called when boost::beast::http::async_write completes
//
void
operator()(boost::beast::error_code ec)
{
d_.invoke(ec);
}
//
// These hooks are necessary for Asio
//
// The meaning is explained in the Beast documentation
//
friend
void* asio_handler_allocate(
std::size_t size, write_msg_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->d_.handler()));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, write_msg_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->d_.handler()));
}
friend
bool asio_handler_is_continuation(write_msg_op* op)
{
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(std::addressof(op->d_.handler()));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, write_msg_op* op)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(op->d_.handler()));
}
};
} // detail
/** Write an HTTP 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 write operations
until this operation completes. The algorithm will use a temporary
@ref serializer to produce buffers. If the semantics of the message
indicate that the connection should be closed after the message is sent,
the error delivered by this function
will be @ref error::end_of_stream
@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 function will take ownership
of the object as if by move constrction.
@param handler The handler to be called when the operation
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& error // 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,
bool isRequest, class Body, class Fields,
class WriteHandler>
boost::beast::async_return_type<WriteHandler, void(boost::beast::error_code)>
async_write_msg(
AsyncWriteStream& stream,
boost::beast::http::message<isRequest, Body, Fields>&& msg,
WriteHandler&& handler)
{
static_assert(
boost::beast::is_async_write_stream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
static_assert(boost::beast::http::is_body<Body>::value,
"Body requirements not met");
static_assert(boost::beast::http::is_body_reader<Body>::value,
"BodyReader requirements not met");
boost::beast::async_completion<WriteHandler, void(boost::beast::error_code)> init{handler};
::detail::write_msg_op<
AsyncWriteStream,
boost::beast::handler_type<WriteHandler, void(boost::beast::error_code)>,
isRequest, Body, Fields>{
init.completion_handler,
stream,
std::move(msg)}();
return init.result.get();
}
#endif