forked from boostorg/beast
Boost prep
This commit is contained in:
1 parent
3777cb8b9c
commit
7139dd639a
375 files changed
+5253
-3884
No files matched your search
@@ -0,0 +1,816 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
||||
|
||||
#include <boost/beast/websocket/detail/type_traits.hpp>
|
||||
#include <boost/beast/http/empty_body.hpp>
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/http/string_body.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
#include <boost/beast/core/buffer_prefix.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// Respond to an upgrade HTTP request
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::response_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
response_type res;
|
||||
int state = 0;
|
||||
|
||||
template<class Body, class Allocator, class Decorator>
|
||||
data(Handler&, stream<NextLayer>& ws_, http::request<
|
||||
Body, http::basic_fields<Allocator>> const& req,
|
||||
Decorator const& decorator, bool cont_)
|
||||
: cont(cont_)
|
||||
, ws(ws_)
|
||||
, res(ws_.build_response(req, decorator))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Body, class Allocator,
|
||||
class Buffers, class Decorator>
|
||||
data(Handler&, stream<NextLayer>& ws_, http::request<
|
||||
Body, http::basic_fields<Allocator>> const& req,
|
||||
Buffers const& buffers, Decorator const& decorator,
|
||||
bool cont_)
|
||||
: cont(cont_)
|
||||
, ws(ws_)
|
||||
, res(ws_.build_response(req, decorator))
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
// VFALCO What about catch(std::length_error const&)?
|
||||
ws.stream_.buffer().commit(buffer_copy(
|
||||
ws.stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
response_op(response_op&&) = default;
|
||||
response_op(response_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
response_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()(
|
||||
error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, response_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, response_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(response_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, response_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::response_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
// send response
|
||||
d.state = 1;
|
||||
http::async_write(d.ws.next_layer(),
|
||||
d.res, std::move(*this));
|
||||
return;
|
||||
|
||||
// sent response
|
||||
case 1:
|
||||
d.state = 99;
|
||||
if(d.res.result() !=
|
||||
http::status::switching_protocols)
|
||||
ec = error::handshake_failed;
|
||||
if(! ec)
|
||||
{
|
||||
pmd_read(d.ws.pmd_config_, d.res);
|
||||
d.ws.open(role_type::server);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// read and respond to an upgrade request
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Decorator, class Handler>
|
||||
class stream<NextLayer>::accept_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
Decorator decorator;
|
||||
http::request_parser<http::empty_body> p;
|
||||
|
||||
data(Handler&, stream<NextLayer>& ws_,
|
||||
Decorator const& decorator_)
|
||||
: ws(ws_)
|
||||
, decorator(decorator_)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
data(Handler&, stream<NextLayer>& ws_,
|
||||
Buffers const& buffers,
|
||||
Decorator const& decorator_)
|
||||
: ws(ws_)
|
||||
, decorator(decorator_)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
// VFALCO What about catch(std::length_error const&)?
|
||||
ws.stream_.buffer().commit(buffer_copy(
|
||||
ws.stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
accept_op(accept_op&&) = default;
|
||||
accept_op(accept_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
accept_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()();
|
||||
|
||||
void operator()(error_code ec);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, accept_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, accept_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(accept_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, accept_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Decorator, class Handler>
|
||||
void
|
||||
stream<NextLayer>::accept_op<Decorator, Handler>::
|
||||
operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
http::async_read_header(d.ws.next_layer(),
|
||||
d.ws.stream_.buffer(), d.p,
|
||||
std::move(*this));
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Decorator, class Handler>
|
||||
void
|
||||
stream<NextLayer>::accept_op<Decorator, Handler>::
|
||||
operator()(error_code ec)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(! ec)
|
||||
{
|
||||
BOOST_ASSERT(d.p.is_header_done());
|
||||
// Arguments from our state must be
|
||||
// moved to the stack before releasing
|
||||
// the handler.
|
||||
auto& ws = d.ws;
|
||||
auto const req = d.p.release();
|
||||
auto const decorator = d.decorator;
|
||||
#if 1
|
||||
response_op<Handler>{
|
||||
d_.release_handler(),
|
||||
ws, req, decorator, true};
|
||||
#else
|
||||
// VFALCO This *should* work but breaks
|
||||
// coroutine invariants in the unit test.
|
||||
// Also it calls reset() when it shouldn't.
|
||||
ws.async_accept_ex(
|
||||
req, decorator, d_.release_handler());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept()
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
accept(ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ResponseDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept_ex(ResponseDecorator const& decorator)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
error_code ec;
|
||||
accept_ex(decorator, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
reset();
|
||||
do_accept(&default_decorate_res, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ResponseDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept_ex(ResponseDecorator const& decorator, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
reset();
|
||||
do_accept(decorator, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
typename std::enable_if<! http::detail::is_header<
|
||||
ConstBufferSequence>::value>::type
|
||||
stream<NextLayer>::
|
||||
accept(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
accept(buffers, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<
|
||||
class ConstBufferSequence, class ResponseDecorator>
|
||||
typename std::enable_if<! http::detail::is_header<
|
||||
ConstBufferSequence>::value>::type
|
||||
stream<NextLayer>::
|
||||
accept_ex(ConstBufferSequence const& buffers,
|
||||
ResponseDecorator const &decorator)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
error_code ec;
|
||||
accept_ex(buffers, decorator, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
typename std::enable_if<! http::detail::is_header<
|
||||
ConstBufferSequence>::value>::type
|
||||
stream<NextLayer>::
|
||||
accept(ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
reset();
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
stream_.buffer().commit(buffer_copy(
|
||||
stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
do_accept(&default_decorate_res, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<
|
||||
class ConstBufferSequence, class ResponseDecorator>
|
||||
typename std::enable_if<! http::detail::is_header<
|
||||
ConstBufferSequence>::value>::type
|
||||
stream<NextLayer>::
|
||||
accept_ex(ConstBufferSequence const& buffers,
|
||||
ResponseDecorator const& decorator, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
reset();
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
stream_.buffer().commit(buffer_copy(
|
||||
stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
do_accept(decorator, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
accept(req, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body,
|
||||
class Allocator, class ResponseDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept_ex(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ResponseDecorator const& decorator)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
error_code ec;
|
||||
accept_ex(req, decorator, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
reset();
|
||||
do_accept(req, &default_decorate_res, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class ResponseDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept_ex(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ResponseDecorator const& decorator, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
reset();
|
||||
do_accept(req, decorator, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
accept(req, buffers, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class ConstBufferSequence, class ResponseDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept_ex(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ConstBufferSequence const& buffers,
|
||||
ResponseDecorator const& decorator)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
error_code ec;
|
||||
accept_ex(req, buffers, decorator, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
reset();
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
stream_.buffer().commit(buffer_copy(
|
||||
stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
do_accept(req, &default_decorate_res, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class ConstBufferSequence, class ResponseDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept_ex(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ConstBufferSequence const& buffers,
|
||||
ResponseDecorator const& decorator,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
reset();
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
stream_.buffer().commit(buffer_copy(
|
||||
stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
do_accept(req, decorator, ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
template<class AcceptHandler>
|
||||
async_return_type<
|
||||
AcceptHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_accept(AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
async_completion<AcceptHandler,
|
||||
void(error_code)> init{handler};
|
||||
reset();
|
||||
accept_op<decltype(&default_decorate_res),
|
||||
handler_type<AcceptHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, &default_decorate_res}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ResponseDecorator, class AcceptHandler>
|
||||
async_return_type<
|
||||
AcceptHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_accept_ex(ResponseDecorator const& decorator,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
async_completion<AcceptHandler,
|
||||
void(error_code)> init{handler};
|
||||
reset();
|
||||
accept_op<ResponseDecorator, handler_type<
|
||||
AcceptHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, decorator}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class AcceptHandler>
|
||||
typename std::enable_if<
|
||||
! http::detail::is_header<ConstBufferSequence>::value,
|
||||
async_return_type<AcceptHandler, void(error_code)>>::type
|
||||
stream<NextLayer>::
|
||||
async_accept(ConstBufferSequence const& buffers,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
async_completion<AcceptHandler,
|
||||
void(error_code)> init{handler};
|
||||
reset();
|
||||
accept_op<decltype(&default_decorate_res),
|
||||
handler_type<AcceptHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, buffers,
|
||||
&default_decorate_res}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence,
|
||||
class ResponseDecorator, class AcceptHandler>
|
||||
typename std::enable_if<
|
||||
! http::detail::is_header<ConstBufferSequence>::value,
|
||||
async_return_type<AcceptHandler, void(error_code)>>::type
|
||||
stream<NextLayer>::
|
||||
async_accept_ex(ConstBufferSequence const& buffers,
|
||||
ResponseDecorator const& decorator,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
async_completion<AcceptHandler,
|
||||
void(error_code)> init{handler};
|
||||
reset();
|
||||
accept_op<ResponseDecorator, handler_type<
|
||||
AcceptHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, buffers,
|
||||
decorator}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class AcceptHandler>
|
||||
async_return_type<
|
||||
AcceptHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_accept(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
async_completion<AcceptHandler,
|
||||
void(error_code)> init{handler};
|
||||
reset();
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
response_op<handler_type<
|
||||
AcceptHandler, void(error_code)>>{init.completion_handler,
|
||||
*this, req, &default_decorate_res,
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(init.completion_handler))};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class ResponseDecorator, class AcceptHandler>
|
||||
async_return_type<
|
||||
AcceptHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_accept_ex(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ResponseDecorator const& decorator, AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
async_completion<AcceptHandler,
|
||||
void(error_code)> init{handler};
|
||||
reset();
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
response_op<handler_type<
|
||||
AcceptHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, req, decorator,
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(init.completion_handler))};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class ConstBufferSequence, class AcceptHandler>
|
||||
async_return_type<
|
||||
AcceptHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_accept(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ConstBufferSequence const& buffers,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
async_completion<AcceptHandler,
|
||||
void(error_code)> init{handler};
|
||||
reset();
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
response_op<handler_type<
|
||||
AcceptHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, req, buffers,
|
||||
&default_decorate_res, asio_handler_is_continuation(
|
||||
std::addressof(init.completion_handler))};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class ConstBufferSequence, class ResponseDecorator,
|
||||
class AcceptHandler>
|
||||
async_return_type<
|
||||
AcceptHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_accept_ex(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
ConstBufferSequence const& buffers,
|
||||
ResponseDecorator const& decorator,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(detail::is_ResponseDecorator<
|
||||
ResponseDecorator>::value,
|
||||
"ResponseDecorator requirements not met");
|
||||
async_completion<AcceptHandler,
|
||||
void(error_code)> init{handler};
|
||||
reset();
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
response_op<handler_type<
|
||||
AcceptHandler, void(error_code)>>{init.completion_handler,
|
||||
*this, req, buffers, decorator, asio_handler_is_continuation(
|
||||
std::addressof(init.completion_handler))};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,251 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_IPP
|
||||
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/flat_static_buffer.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// send the close message and wait for the response
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::close_op
|
||||
{
|
||||
struct data : op
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
close_reason cr;
|
||||
detail::frame_streambuf fb;
|
||||
int state = 0;
|
||||
token tok;
|
||||
|
||||
data(Handler&, stream<NextLayer>& ws_,
|
||||
close_reason const& cr_)
|
||||
: ws(ws_)
|
||||
, cr(cr_)
|
||||
, tok(ws.t_.unique())
|
||||
{
|
||||
ws.template write_close<
|
||||
flat_static_buffer_base>(fb, cr);
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
close_op(close_op&&) = default;
|
||||
close_op(close_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
close_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
(*this)({});
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, close_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, close_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(close_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, close_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::close_op<Handler>::
|
||||
operator()(error_code ec, std::size_t)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.ws.failed_ = true;
|
||||
goto upcall;
|
||||
}
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 1;
|
||||
d.ws.close_op_.emplace(std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.ws.wr_block_ = d.tok;
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted));
|
||||
return;
|
||||
}
|
||||
|
||||
do_write:
|
||||
// send close frame
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.state = 3;
|
||||
d.ws.wr_close_ = true;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = d.tok;
|
||||
d.state = 2;
|
||||
// The current context is safe but might not be
|
||||
// the same as the one for this operation (since
|
||||
// we are being called from a write operation).
|
||||
// Call post to make sure we are invoked the same
|
||||
// way as the final handler for this operation.
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
goto upcall;
|
||||
}
|
||||
goto do_write;
|
||||
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
upcall:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.ws.wr_block_.reset();
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke() ||
|
||||
d.ws.wr_op_.maybe_invoke();
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
close(close_reason const& cr)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
close(cr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
close(close_reason const& cr, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
// If rd_close_ is set then we already sent a close
|
||||
BOOST_ASSERT(! rd_close_);
|
||||
if(wr_close_)
|
||||
{
|
||||
// Can't call close twice, abort operation
|
||||
BOOST_ASSERT(! wr_close_);
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
return;
|
||||
}
|
||||
wr_close_ = true;
|
||||
detail::frame_streambuf fb;
|
||||
write_close<flat_static_buffer_base>(fb, cr);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class CloseHandler>
|
||||
async_return_type<
|
||||
CloseHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_close(close_reason const& cr, CloseHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
async_completion<CloseHandler,
|
||||
void(error_code)> init{handler};
|
||||
close_op<handler_type<
|
||||
CloseHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, cr}({});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::websocket::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
class websocket_error_category : public error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "beast.websocket";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
case error::closed: return "WebSocket connection closed normally";
|
||||
case error::failed: return "WebSocket connection failed due to a protocol violation";
|
||||
case error::handshake_failed: return "WebSocket Upgrade handshake failed";
|
||||
case error::buffer_overflow: return "buffer overflow";
|
||||
|
||||
default:
|
||||
return "beast.websocket error";
|
||||
}
|
||||
}
|
||||
|
||||
error_condition
|
||||
default_error_condition(int ev) const noexcept override
|
||||
{
|
||||
return error_condition(ev, *this);
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
error_condition const& condition
|
||||
) const noexcept override
|
||||
{
|
||||
return condition.value() == ev &&
|
||||
&condition.category() == this;
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(error_code const& error, int ev) const noexcept override
|
||||
{
|
||||
return error.value() == ev &&
|
||||
&error.category() == this;
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
error_category const&
|
||||
get_error_category()
|
||||
{
|
||||
static detail::websocket_error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_error_code(error e)
|
||||
{
|
||||
return error_code(
|
||||
static_cast<std::underlying_type<error>::type>(e),
|
||||
detail::get_error_category());
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_FAIL_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_FAIL_IPP
|
||||
|
||||
#include <boost/beast/websocket/teardown.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/flat_static_buffer.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// _Fail the WebSocket Connection_
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::fail_op
|
||||
{
|
||||
Handler h_;
|
||||
stream<NextLayer>& ws_;
|
||||
int step_ = 0;
|
||||
bool dispatched_ = false;
|
||||
fail_how how_;
|
||||
token tok_;
|
||||
|
||||
public:
|
||||
fail_op(fail_op&&) = default;
|
||||
fail_op(fail_op const&) = default;
|
||||
|
||||
// send close code, then teardown
|
||||
template<class DeducedHandler>
|
||||
fail_op(
|
||||
DeducedHandler&& h,
|
||||
stream<NextLayer>& ws,
|
||||
close_code code)
|
||||
: h_(std::forward<DeducedHandler>(h))
|
||||
, ws_(ws)
|
||||
, how_(fail_how::code)
|
||||
, tok_(ws_.t_.unique())
|
||||
{
|
||||
ws_.rd_.fb.consume(ws_.rd_.fb.size());
|
||||
ws_.template write_close<
|
||||
flat_static_buffer_base>(
|
||||
ws_.rd_.fb, code);
|
||||
}
|
||||
|
||||
// maybe send frame in fb, then teardown
|
||||
template<class DeducedHandler>
|
||||
fail_op(
|
||||
DeducedHandler&& h,
|
||||
stream<NextLayer>& ws,
|
||||
fail_how how)
|
||||
: h_(std::forward<DeducedHandler>(h))
|
||||
, ws_(ws)
|
||||
, how_(how)
|
||||
, tok_(ws_.t_.unique())
|
||||
{
|
||||
}
|
||||
|
||||
Handler&
|
||||
handler()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
void operator()(error_code ec = {},
|
||||
std::size_t bytes_transferred = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, fail_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, fail_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(fail_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->dispatched_ ||
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, fail_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(f,
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
fail_op<Handler>::
|
||||
operator()(error_code ec, std::size_t)
|
||||
{
|
||||
enum
|
||||
{
|
||||
do_start = 0,
|
||||
do_resume = 20,
|
||||
do_teardown = 40
|
||||
};
|
||||
switch(step_)
|
||||
{
|
||||
case do_start:
|
||||
// Acquire write block
|
||||
if(ws_.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
BOOST_ASSERT(ws_.wr_block_ != tok_);
|
||||
step_ = do_resume;
|
||||
ws_.rd_op_.save(std::move(*this));
|
||||
return;
|
||||
}
|
||||
ws_.wr_block_ = tok_;
|
||||
goto go_write;
|
||||
|
||||
case do_resume:
|
||||
BOOST_ASSERT(! ws_.wr_block_);
|
||||
ws_.wr_block_ = tok_;
|
||||
step_ = do_resume + 1;
|
||||
// We were invoked from a foreign context, so post
|
||||
return ws_.get_io_service().post(std::move(*this));
|
||||
|
||||
case do_resume + 1:
|
||||
BOOST_ASSERT(ws_.wr_block_ == tok_);
|
||||
dispatched_ = true;
|
||||
go_write:
|
||||
BOOST_ASSERT(ws_.wr_block_ == tok_);
|
||||
if(ws_.failed_)
|
||||
{
|
||||
ws_.wr_block_.reset();
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
}
|
||||
if(how_ == fail_how::teardown)
|
||||
goto go_teardown;
|
||||
if(ws_.wr_close_)
|
||||
goto go_teardown;
|
||||
// send close frame
|
||||
step_ = do_teardown;
|
||||
ws_.wr_close_ = true;
|
||||
return boost::asio::async_write(
|
||||
ws_.stream_, ws_.rd_.fb.data(),
|
||||
std::move(*this));
|
||||
|
||||
case do_teardown:
|
||||
BOOST_ASSERT(ws_.wr_block_ == tok_);
|
||||
dispatched_ = true;
|
||||
ws_.failed_ = !!ec;
|
||||
if(ws_.failed_)
|
||||
{
|
||||
ws_.wr_block_.reset();
|
||||
break;
|
||||
}
|
||||
go_teardown:
|
||||
BOOST_ASSERT(ws_.wr_block_ == tok_);
|
||||
step_ = do_teardown + 1;
|
||||
websocket_helpers::call_async_teardown(
|
||||
ws_.next_layer(), std::move(*this));
|
||||
return;
|
||||
|
||||
case do_teardown + 1:
|
||||
BOOST_ASSERT(ws_.wr_block_ == tok_);
|
||||
dispatched_ = true;
|
||||
ws_.failed_ = true;
|
||||
ws_.wr_block_.reset();
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
// Rationale:
|
||||
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
if(! ec)
|
||||
{
|
||||
switch(how_)
|
||||
{
|
||||
default:
|
||||
case fail_how::code:
|
||||
case fail_how::teardown: ec = error::failed; break;
|
||||
case fail_how::close: ec = error::closed; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
// upcall
|
||||
BOOST_ASSERT(ws_.wr_block_ != tok_);
|
||||
ws_.close_op_.maybe_invoke() ||
|
||||
ws_.ping_op_.maybe_invoke() ||
|
||||
ws_.wr_op_.maybe_invoke();
|
||||
if(! dispatched_)
|
||||
ws_.stream_.get_io_service().post(
|
||||
bind_handler(std::move(h_), ec));
|
||||
else
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,398 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
|
||||
|
||||
#include <boost/beast/websocket/detail/type_traits.hpp>
|
||||
#include <boost/beast/http/empty_body.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// send the upgrade request and process the response
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::handshake_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
response_type* res_p;
|
||||
detail::sec_ws_key_type key;
|
||||
http::request<http::empty_body> req;
|
||||
response_type res;
|
||||
int state = 0;
|
||||
|
||||
template<class Decorator>
|
||||
data(Handler& handler, stream<NextLayer>& ws_,
|
||||
response_type* res_p_,
|
||||
string_view host,
|
||||
string_view target,
|
||||
Decorator const& decorator)
|
||||
: ws(ws_)
|
||||
, res_p(res_p_)
|
||||
, req(ws.build_request(key,
|
||||
host, target, decorator))
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
cont = asio_handler_is_continuation(std::addressof(handler));
|
||||
ws.reset();
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
handshake_op(handshake_op&&) = default;
|
||||
handshake_op(handshake_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
handshake_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, handshake_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, handshake_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(handshake_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, handshake_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::handshake_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// send http upgrade
|
||||
d.state = 1;
|
||||
// VFALCO Do we need the ability to move
|
||||
// a message on the async_write?
|
||||
//
|
||||
pmd_read(d.ws.pmd_config_, d.req);
|
||||
http::async_write(d.ws.stream_,
|
||||
d.req, std::move(*this));
|
||||
// TODO We don't need d.req now. Figure
|
||||
// out a way to make it a parameter instead
|
||||
// of a state variable to reduce footprint.
|
||||
return;
|
||||
}
|
||||
|
||||
// sent upgrade
|
||||
case 1:
|
||||
// read http response
|
||||
d.state = 2;
|
||||
http::async_read(d.ws.next_layer(),
|
||||
d.ws.stream_.buffer(), d.res,
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got response
|
||||
case 2:
|
||||
{
|
||||
d.ws.do_response(d.res, d.key, ec);
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(d.res_p)
|
||||
swap(d.res, *d.res_p);
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class HandshakeHandler>
|
||||
async_return_type<
|
||||
HandshakeHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_handshake(string_view host,
|
||||
string_view target,
|
||||
HandshakeHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
async_completion<HandshakeHandler,
|
||||
void(error_code)> init{handler};
|
||||
handshake_op<handler_type<
|
||||
HandshakeHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, nullptr, host,
|
||||
target, &default_decorate_req};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class HandshakeHandler>
|
||||
async_return_type<
|
||||
HandshakeHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_handshake(response_type& res,
|
||||
string_view host,
|
||||
string_view target,
|
||||
HandshakeHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
async_completion<HandshakeHandler,
|
||||
void(error_code)> init{handler};
|
||||
handshake_op<handler_type<
|
||||
HandshakeHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, &res, host,
|
||||
target, &default_decorate_req};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class RequestDecorator, class HandshakeHandler>
|
||||
async_return_type<
|
||||
HandshakeHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_handshake_ex(string_view host,
|
||||
string_view target,
|
||||
RequestDecorator const& decorator,
|
||||
HandshakeHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
static_assert(detail::is_RequestDecorator<
|
||||
RequestDecorator>::value,
|
||||
"RequestDecorator requirements not met");
|
||||
async_completion<HandshakeHandler,
|
||||
void(error_code)> init{handler};
|
||||
handshake_op<handler_type<
|
||||
HandshakeHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, nullptr, host,
|
||||
target, decorator};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class RequestDecorator, class HandshakeHandler>
|
||||
async_return_type<
|
||||
HandshakeHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_handshake_ex(response_type& res,
|
||||
string_view host,
|
||||
string_view target,
|
||||
RequestDecorator const& decorator,
|
||||
HandshakeHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
static_assert(detail::is_RequestDecorator<
|
||||
RequestDecorator>::value,
|
||||
"RequestDecorator requirements not met");
|
||||
async_completion<HandshakeHandler,
|
||||
void(error_code)> init{handler};
|
||||
handshake_op<handler_type<
|
||||
HandshakeHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, &res, host,
|
||||
target, decorator};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake(string_view host,
|
||||
string_view target)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
handshake(
|
||||
host, target, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake(response_type& res,
|
||||
string_view host,
|
||||
string_view target)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
handshake(res, host, target, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class RequestDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake_ex(string_view host,
|
||||
string_view target,
|
||||
RequestDecorator const& decorator)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(detail::is_RequestDecorator<
|
||||
RequestDecorator>::value,
|
||||
"RequestDecorator requirements not met");
|
||||
error_code ec;
|
||||
handshake_ex(host, target, decorator, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class RequestDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake_ex(response_type& res,
|
||||
string_view host,
|
||||
string_view target,
|
||||
RequestDecorator const& decorator)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(detail::is_RequestDecorator<
|
||||
RequestDecorator>::value,
|
||||
"RequestDecorator requirements not met");
|
||||
error_code ec;
|
||||
handshake_ex(res, host, target, decorator, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake(string_view host,
|
||||
string_view target, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
do_handshake(nullptr,
|
||||
host, target, &default_decorate_req, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake(response_type& res,
|
||||
string_view host,
|
||||
string_view target,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
do_handshake(&res,
|
||||
host, target, &default_decorate_req, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class RequestDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake_ex(string_view host,
|
||||
string_view target,
|
||||
RequestDecorator const& decorator,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(detail::is_RequestDecorator<
|
||||
RequestDecorator>::value,
|
||||
"RequestDecorator requirements not met");
|
||||
do_handshake(nullptr,
|
||||
host, target, decorator, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class RequestDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake_ex(response_type& res,
|
||||
string_view host,
|
||||
string_view target,
|
||||
RequestDecorator const& decorator,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(detail::is_RequestDecorator<
|
||||
RequestDecorator>::value,
|
||||
"RequestDecorator requirements not met");
|
||||
do_handshake(&res,
|
||||
host, target, decorator, ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,275 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP
|
||||
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/websocket/detail/frame.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// write a ping frame
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::ping_op
|
||||
{
|
||||
struct data : op
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
detail::frame_streambuf fb;
|
||||
int state = 0;
|
||||
token tok;
|
||||
|
||||
data(Handler&, stream<NextLayer>& ws_,
|
||||
detail::opcode op_, ping_data const& payload)
|
||||
: ws(ws_)
|
||||
, tok(ws.t_.unique())
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
ws.template write_ping<
|
||||
flat_static_buffer_base>(fb, op_, payload);
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
ping_op(ping_op&&) = default;
|
||||
ping_op(ping_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
ping_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
(*this)({});
|
||||
}
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, ping_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, ping_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(ping_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, ping_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
ping_op<Handler>::
|
||||
operator()(error_code ec, std::size_t)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.ws.failed_ = true;
|
||||
goto upcall;
|
||||
}
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 1;
|
||||
d.ws.ping_op_.emplace(std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.ws.wr_block_ = d.tok;
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
return d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted));
|
||||
}
|
||||
|
||||
do_write:
|
||||
// send ping frame
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.state = 3;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = d.tok;
|
||||
d.state = 2;
|
||||
// The current context is safe but might not be
|
||||
// the same as the one for this operation (since
|
||||
// we are being called from a write operation).
|
||||
// Call post to make sure we are invoked the same
|
||||
// way as the final handler for this operation.
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
goto upcall;
|
||||
}
|
||||
goto do_write;
|
||||
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
upcall:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.ws.wr_block_.reset();
|
||||
d.ws.close_op_.maybe_invoke() ||
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.wr_op_.maybe_invoke();
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class WriteHandler>
|
||||
async_return_type<
|
||||
WriteHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_ping(ping_data const& payload, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
ping_op<handler_type<
|
||||
WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, *this,
|
||||
detail::opcode::ping, payload}({});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class WriteHandler>
|
||||
async_return_type<
|
||||
WriteHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_pong(ping_data const& payload, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
ping_op<handler_type<
|
||||
WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, *this,
|
||||
detail::opcode::pong, payload}({});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
ping(ping_data const& payload)
|
||||
{
|
||||
error_code ec;
|
||||
ping(payload, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
ping(ping_data const& payload, error_code& ec)
|
||||
{
|
||||
detail::frame_streambuf db;
|
||||
write_ping<flat_static_buffer_base>(
|
||||
db, detail::opcode::ping, payload);
|
||||
boost::asio::write(stream_, db.data(), ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
pong(ping_data const& payload)
|
||||
{
|
||||
error_code ec;
|
||||
pong(payload, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
pong(ping_data const& payload, error_code& ec)
|
||||
{
|
||||
detail::frame_streambuf db;
|
||||
write_ping<flat_static_buffer_base>(
|
||||
db, detail::opcode::pong, payload);
|
||||
boost::asio::write(stream_, db.data(), ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_IPP
|
||||
|
||||
#include <boost/beast/http/fields.hpp>
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
template<class Allocator>
|
||||
bool
|
||||
is_upgrade(http::header<true,
|
||||
http::basic_fields<Allocator>> const& req)
|
||||
{
|
||||
if(req.version < 11)
|
||||
return false;
|
||||
if(req.method() != http::verb::get)
|
||||
return false;
|
||||
if(! http::token_list{req["Connection"]}.exists("upgrade"))
|
||||
return false;
|
||||
if(! http::token_list{req["Upgrade"]}.exists("websocket"))
|
||||
return false;
|
||||
if(! req.count(http::field::sec_websocket_version))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/*
|
||||
|
||||
See
|
||||
http://stackoverflow.com/questions/32046034/what-is-the-proper-way-to-securely-disconnect-an-asio-ssl-socket/32054476#32054476
|
||||
|
||||
Behavior of ssl::stream regarding close_
|
||||
|
||||
If the remote host calls async_shutdown then the
|
||||
local host's async_read will complete with eof.
|
||||
|
||||
If both hosts call async_shutdown then the calls
|
||||
to async_shutdown will complete with eof.
|
||||
|
||||
*/
|
||||
|
||||
template<class AsyncStream>
|
||||
void
|
||||
teardown(teardown_tag,
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
error_code& ec)
|
||||
{
|
||||
stream.shutdown(ec);
|
||||
}
|
||||
|
||||
template<class AsyncStream, class TeardownHandler>
|
||||
void
|
||||
async_teardown(teardown_tag,
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
stream.async_shutdown(std::forward<TeardownHandler>(handler));
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,932 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IPP
|
||||
|
||||
#include <boost/beast/websocket/rfc6455.hpp>
|
||||
#include <boost/beast/websocket/teardown.hpp>
|
||||
#include <boost/beast/websocket/detail/hybi13.hpp>
|
||||
#include <boost/beast/websocket/detail/pmd_extension.hpp>
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
#include <boost/beast/core/buffer_cat.hpp>
|
||||
#include <boost/beast/core/buffer_prefix.hpp>
|
||||
#include <boost/beast/core/consuming_buffers.hpp>
|
||||
#include <boost/beast/core/flat_static_buffer.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
template<class NextLayer>
|
||||
template<class... Args>
|
||||
stream<NextLayer>::
|
||||
stream(Args&&... args)
|
||||
: stream_(std::forward<Args>(args)...)
|
||||
{
|
||||
BOOST_ASSERT(rd_.buf.max_size() >=
|
||||
max_control_frame_size);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
std::size_t
|
||||
stream<NextLayer>::
|
||||
read_size_hint(
|
||||
std::size_t initial_size) const
|
||||
{
|
||||
using beast::detail::clamp;
|
||||
// no permessage-deflate
|
||||
if(! pmd_ || (! rd_.done && ! pmd_->rd_set))
|
||||
{
|
||||
// fresh message
|
||||
if(rd_.done)
|
||||
return initial_size;
|
||||
|
||||
if(rd_.fh.fin)
|
||||
return clamp(rd_.remain);
|
||||
}
|
||||
return (std::max)(
|
||||
initial_size, clamp(rd_.remain));
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class DynamicBuffer, class>
|
||||
std::size_t
|
||||
stream<NextLayer>::
|
||||
read_size_hint(
|
||||
DynamicBuffer& buffer) const
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
using beast::detail::clamp;
|
||||
// no permessage-deflate
|
||||
if(! pmd_ || (! rd_.done && ! pmd_->rd_set))
|
||||
{
|
||||
// fresh message
|
||||
if(rd_.done)
|
||||
return (std::min)(
|
||||
buffer.max_size(),
|
||||
(std::max)(+tcp_frame_size,
|
||||
buffer.capacity() - buffer.size()));
|
||||
|
||||
if(rd_.fh.fin)
|
||||
{
|
||||
BOOST_ASSERT(rd_.remain != 0);
|
||||
return (std::min)(
|
||||
buffer.max_size(), clamp(rd_.remain));
|
||||
}
|
||||
}
|
||||
return (std::min)(buffer.max_size(), (std::max)(
|
||||
(std::max)(+tcp_frame_size, clamp(rd_.remain)),
|
||||
buffer.capacity() - buffer.size()));
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
set_option(permessage_deflate const& o)
|
||||
{
|
||||
if( o.server_max_window_bits > 15 ||
|
||||
o.server_max_window_bits < 9)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid server_max_window_bits"});
|
||||
if( o.client_max_window_bits > 15 ||
|
||||
o.client_max_window_bits < 9)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid client_max_window_bits"});
|
||||
if( o.compLevel < 0 ||
|
||||
o.compLevel > 9)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid compLevel"});
|
||||
if( o.memLevel < 1 ||
|
||||
o.memLevel > 9)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid memLevel"});
|
||||
pmd_opts_ = o;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
open(role_type role)
|
||||
{
|
||||
// VFALCO TODO analyze and remove dupe code in reset()
|
||||
role_ = role;
|
||||
failed_ = false;
|
||||
rd_.remain = 0;
|
||||
rd_.cont = false;
|
||||
rd_.done = true;
|
||||
rd_.buf.consume(rd_.buf.size());
|
||||
rd_close_ = false;
|
||||
wr_close_ = false;
|
||||
wr_block_.reset();
|
||||
ping_data_ = nullptr; // should be nullptr on close anyway
|
||||
|
||||
wr_.cont = false;
|
||||
wr_.buf_size = 0;
|
||||
|
||||
if(((role_ == role_type::client && pmd_opts_.client_enable) ||
|
||||
(role_ == role_type::server && pmd_opts_.server_enable)) &&
|
||||
pmd_config_.accept)
|
||||
{
|
||||
pmd_normalize(pmd_config_);
|
||||
pmd_.reset(new pmd_t);
|
||||
if(role_ == role_type::client)
|
||||
{
|
||||
pmd_->zi.reset(
|
||||
pmd_config_.server_max_window_bits);
|
||||
pmd_->zo.reset(
|
||||
pmd_opts_.compLevel,
|
||||
pmd_config_.client_max_window_bits,
|
||||
pmd_opts_.memLevel,
|
||||
zlib::Strategy::normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
pmd_->zi.reset(
|
||||
pmd_config_.client_max_window_bits);
|
||||
pmd_->zo.reset(
|
||||
pmd_opts_.compLevel,
|
||||
pmd_config_.server_max_window_bits,
|
||||
pmd_opts_.memLevel,
|
||||
zlib::Strategy::normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
close()
|
||||
{
|
||||
wr_.buf.reset();
|
||||
pmd_.reset();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
reset()
|
||||
{
|
||||
failed_ = false;
|
||||
rd_.remain = 0;
|
||||
rd_.cont = false;
|
||||
rd_.done = true;
|
||||
rd_.buf.consume(rd_.buf.size());
|
||||
rd_close_ = false;
|
||||
wr_close_ = false;
|
||||
wr_.cont = false;
|
||||
wr_block_.reset();
|
||||
ping_data_ = nullptr; // should be nullptr on close anyway
|
||||
|
||||
stream_.buffer().consume(
|
||||
stream_.buffer().size());
|
||||
}
|
||||
|
||||
// Called before each write frame
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
wr_begin()
|
||||
{
|
||||
wr_.autofrag = wr_autofrag_;
|
||||
wr_.compress = static_cast<bool>(pmd_);
|
||||
|
||||
// Maintain the write buffer
|
||||
if( wr_.compress ||
|
||||
role_ == role_type::client)
|
||||
{
|
||||
if(! wr_.buf || wr_.buf_size != wr_buf_size_)
|
||||
{
|
||||
wr_.buf_size = wr_buf_size_;
|
||||
wr_.buf = boost::make_unique_noinit<
|
||||
std::uint8_t[]>(wr_.buf_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wr_.buf_size = wr_buf_size_;
|
||||
wr_.buf.reset();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Attempt to read a complete frame header.
|
||||
// Returns `false` if more bytes are needed
|
||||
template<class NextLayer>
|
||||
template<class DynamicBuffer>
|
||||
bool
|
||||
stream<NextLayer>::
|
||||
parse_fh(
|
||||
detail::frame_header& fh,
|
||||
DynamicBuffer& b,
|
||||
close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const err =
|
||||
[&](close_code cv)
|
||||
{
|
||||
code = cv;
|
||||
return false;
|
||||
};
|
||||
if(buffer_size(b.data()) < 2)
|
||||
{
|
||||
code = close_code::none;
|
||||
return false;
|
||||
}
|
||||
consuming_buffers<typename
|
||||
DynamicBuffer::const_buffers_type> cb{
|
||||
b.data()};
|
||||
{
|
||||
std::uint8_t tmp[2];
|
||||
cb.consume(buffer_copy(buffer(tmp), cb));
|
||||
std::size_t need;
|
||||
fh.len = tmp[1] & 0x7f;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126: need = 2; break;
|
||||
case 127: need = 8; break;
|
||||
default:
|
||||
need = 0;
|
||||
}
|
||||
fh.mask = (tmp[1] & 0x80) != 0;
|
||||
if(fh.mask)
|
||||
need += 4;
|
||||
if(buffer_size(cb) < need)
|
||||
{
|
||||
code = close_code::none;
|
||||
return false;
|
||||
}
|
||||
fh.op = static_cast<
|
||||
detail::opcode>(tmp[0] & 0x0f);
|
||||
fh.fin = (tmp[0] & 0x80) != 0;
|
||||
fh.rsv1 = (tmp[0] & 0x40) != 0;
|
||||
fh.rsv2 = (tmp[0] & 0x20) != 0;
|
||||
fh.rsv3 = (tmp[0] & 0x10) != 0;
|
||||
}
|
||||
switch(fh.op)
|
||||
{
|
||||
case detail::opcode::binary:
|
||||
case detail::opcode::text:
|
||||
if(rd_.cont)
|
||||
{
|
||||
// new data frame when continuation expected
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if((fh.rsv1 && ! pmd_) ||
|
||||
fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(pmd_)
|
||||
pmd_->rd_set = fh.rsv1;
|
||||
break;
|
||||
|
||||
case detail::opcode::cont:
|
||||
if(! rd_.cont)
|
||||
{
|
||||
// continuation without an active message
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if(detail::is_reserved(fh.op))
|
||||
{
|
||||
// reserved opcode
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(! fh.fin)
|
||||
{
|
||||
// fragmented control message
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(fh.len > 125)
|
||||
{
|
||||
// invalid length for control message
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// unmasked frame from client
|
||||
if(role_ == role_type::server && ! fh.mask)
|
||||
return err(close_code::protocol_error);
|
||||
// masked frame from server
|
||||
if(role_ == role_type::client && fh.mask)
|
||||
return err(close_code::protocol_error);
|
||||
if(detail::is_control(fh.op) &&
|
||||
buffer_size(cb) < fh.len)
|
||||
{
|
||||
// Make the entire control frame payload
|
||||
// get read in before we return `true`
|
||||
return false;
|
||||
}
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126:
|
||||
{
|
||||
std::uint8_t tmp[2];
|
||||
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
||||
cb.consume(buffer_copy(buffer(tmp), cb));
|
||||
fh.len = detail::big_uint16_to_native(&tmp[0]);
|
||||
// length not canonical
|
||||
if(fh.len < 126)
|
||||
return err(close_code::protocol_error);
|
||||
break;
|
||||
}
|
||||
case 127:
|
||||
{
|
||||
std::uint8_t tmp[8];
|
||||
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
||||
cb.consume(buffer_copy(buffer(tmp), cb));
|
||||
fh.len = detail::big_uint64_to_native(&tmp[0]);
|
||||
// length not canonical
|
||||
if(fh.len < 65536)
|
||||
return err(close_code::protocol_error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
std::uint8_t tmp[4];
|
||||
BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
|
||||
cb.consume(buffer_copy(buffer(tmp), cb));
|
||||
fh.key = detail::little_uint32_to_native(&tmp[0]);
|
||||
detail::prepare_key(rd_.key, fh.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize this otherwise operator== breaks
|
||||
fh.key = 0;
|
||||
}
|
||||
if(! detail::is_control(fh.op))
|
||||
{
|
||||
if(fh.op != detail::opcode::cont)
|
||||
{
|
||||
rd_.size = 0;
|
||||
rd_.op = fh.op;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rd_.size > (std::numeric_limits<
|
||||
std::uint64_t>::max)() - fh.len)
|
||||
return err(close_code::too_big);
|
||||
}
|
||||
if(! pmd_ || ! pmd_->rd_set)
|
||||
{
|
||||
if(rd_msg_max_ && beast::detail::sum_exceeds(
|
||||
rd_.size, fh.len, rd_msg_max_))
|
||||
return err(close_code::too_big);
|
||||
}
|
||||
rd_.cont = ! fh.fin;
|
||||
rd_.remain = fh.len;
|
||||
}
|
||||
b.consume(b.size() - buffer_size(cb));
|
||||
code = close_code::none;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read fixed frame header from buffer
|
||||
// Requires at least 2 bytes
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
stream<NextLayer>::
|
||||
read_fh1(detail::frame_header& fh,
|
||||
DynamicBuffer& db, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const err =
|
||||
[&](close_code cv)
|
||||
{
|
||||
code = cv;
|
||||
return 0;
|
||||
};
|
||||
std::uint8_t b[2];
|
||||
BOOST_ASSERT(buffer_size(db.data()) >= sizeof(b));
|
||||
db.consume(buffer_copy(buffer(b), db.data()));
|
||||
std::size_t need;
|
||||
fh.len = b[1] & 0x7f;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126: need = 2; break;
|
||||
case 127: need = 8; break;
|
||||
default:
|
||||
need = 0;
|
||||
}
|
||||
fh.mask = (b[1] & 0x80) != 0;
|
||||
if(fh.mask)
|
||||
need += 4;
|
||||
fh.op = static_cast<
|
||||
detail::opcode>(b[0] & 0x0f);
|
||||
fh.fin = (b[0] & 0x80) != 0;
|
||||
fh.rsv1 = (b[0] & 0x40) != 0;
|
||||
fh.rsv2 = (b[0] & 0x20) != 0;
|
||||
fh.rsv3 = (b[0] & 0x10) != 0;
|
||||
switch(fh.op)
|
||||
{
|
||||
case detail::opcode::binary:
|
||||
case detail::opcode::text:
|
||||
if(rd_.cont)
|
||||
{
|
||||
// new data frame when continuation expected
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if((fh.rsv1 && ! pmd_) ||
|
||||
fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(pmd_)
|
||||
pmd_->rd_set = fh.rsv1;
|
||||
break;
|
||||
|
||||
case detail::opcode::cont:
|
||||
if(! rd_.cont)
|
||||
{
|
||||
// continuation without an active message
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if(is_reserved(fh.op))
|
||||
{
|
||||
// reserved opcode
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(! fh.fin)
|
||||
{
|
||||
// fragmented control message
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(fh.len > 125)
|
||||
{
|
||||
// invalid length for control message
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
// reserved bits not cleared
|
||||
return err(close_code::protocol_error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// unmasked frame from client
|
||||
if(role_ == role_type::server && ! fh.mask)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// masked frame from server
|
||||
if(role_ == role_type::client && fh.mask)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
code = close_code::none;
|
||||
return need;
|
||||
}
|
||||
|
||||
// Decode variable frame header from buffer
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
read_fh2(detail::frame_header& fh,
|
||||
DynamicBuffer& db, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using namespace boost::endian;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126:
|
||||
{
|
||||
std::uint8_t b[2];
|
||||
BOOST_ASSERT(buffer_size(db.data()) >= sizeof(b));
|
||||
db.consume(buffer_copy(buffer(b), db.data()));
|
||||
fh.len = detail::big_uint16_to_native(&b[0]);
|
||||
// length not canonical
|
||||
if(fh.len < 126)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 127:
|
||||
{
|
||||
std::uint8_t b[8];
|
||||
BOOST_ASSERT(buffer_size(db.data()) >= sizeof(b));
|
||||
db.consume(buffer_copy(buffer(b), db.data()));
|
||||
fh.len = detail::big_uint64_to_native(&b[0]);
|
||||
// length not canonical
|
||||
if(fh.len < 65536)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
std::uint8_t b[4];
|
||||
BOOST_ASSERT(buffer_size(db.data()) >= sizeof(b));
|
||||
db.consume(buffer_copy(buffer(b), db.data()));
|
||||
fh.key = detail::little_uint32_to_native(&b[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize this otherwise operator== breaks
|
||||
fh.key = 0;
|
||||
}
|
||||
if(! detail::is_control(fh.op))
|
||||
{
|
||||
if(fh.op != detail::opcode::cont)
|
||||
{
|
||||
rd_.size = 0;
|
||||
rd_.op = fh.op;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rd_.size > (std::numeric_limits<
|
||||
std::uint64_t>::max)() - fh.len)
|
||||
{
|
||||
code = close_code::too_big;
|
||||
return;
|
||||
}
|
||||
}
|
||||
rd_.cont = ! fh.fin;
|
||||
}
|
||||
code = close_code::none;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_close(DynamicBuffer& db, close_reason const& cr)
|
||||
{
|
||||
using namespace boost::endian;
|
||||
detail::frame_header fh;
|
||||
fh.op = detail::opcode::close;
|
||||
fh.fin = true;
|
||||
fh.rsv1 = false;
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
fh.len = cr.code == close_code::none ?
|
||||
0 : 2 + cr.reason.size();
|
||||
fh.mask = role_ == role_type::client;
|
||||
if(fh.mask)
|
||||
fh.key = maskgen_();
|
||||
detail::write(db, fh);
|
||||
if(cr.code != close_code::none)
|
||||
{
|
||||
detail::prepared_key key;
|
||||
if(fh.mask)
|
||||
detail::prepare_key(key, fh.key);
|
||||
{
|
||||
std::uint8_t b[2];
|
||||
::new(&b[0]) big_uint16_buf_t{
|
||||
(std::uint16_t)cr.code};
|
||||
auto d = db.prepare(2);
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::buffer(b));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
db.commit(2);
|
||||
}
|
||||
if(! cr.reason.empty())
|
||||
{
|
||||
auto d = db.prepare(cr.reason.size());
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::const_buffer(
|
||||
cr.reason.data(), cr.reason.size()));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
db.commit(cr.reason.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_ping(DynamicBuffer& db,
|
||||
detail::opcode code, ping_data const& data)
|
||||
{
|
||||
detail::frame_header fh;
|
||||
fh.op = code;
|
||||
fh.fin = true;
|
||||
fh.rsv1 = false;
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
fh.len = data.size();
|
||||
fh.mask = role_ == role_type::client;
|
||||
if(fh.mask)
|
||||
fh.key = maskgen_();
|
||||
detail::write(db, fh);
|
||||
if(data.empty())
|
||||
return;
|
||||
detail::prepared_key key;
|
||||
if(fh.mask)
|
||||
detail::prepare_key(key, fh.key);
|
||||
auto d = db.prepare(data.size());
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::const_buffers_1(
|
||||
data.data(), data.size()));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
db.commit(data.size());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Decorator>
|
||||
request_type
|
||||
stream<NextLayer>::
|
||||
build_request(detail::sec_ws_key_type& key,
|
||||
string_view host,
|
||||
string_view target,
|
||||
Decorator const& decorator)
|
||||
{
|
||||
request_type req;
|
||||
req.target(target);
|
||||
req.version = 11;
|
||||
req.method(http::verb::get);
|
||||
req.set(http::field::host, host);
|
||||
req.set(http::field::upgrade, "websocket");
|
||||
req.set(http::field::connection, "upgrade");
|
||||
detail::make_sec_ws_key(key, maskgen_);
|
||||
req.set(http::field::sec_websocket_key, key);
|
||||
req.set(http::field::sec_websocket_version, "13");
|
||||
if(pmd_opts_.client_enable)
|
||||
{
|
||||
detail::pmd_offer config;
|
||||
config.accept = true;
|
||||
config.server_max_window_bits =
|
||||
pmd_opts_.server_max_window_bits;
|
||||
config.client_max_window_bits =
|
||||
pmd_opts_.client_max_window_bits;
|
||||
config.server_no_context_takeover =
|
||||
pmd_opts_.server_no_context_takeover;
|
||||
config.client_no_context_takeover =
|
||||
pmd_opts_.client_no_context_takeover;
|
||||
detail::pmd_write(req, config);
|
||||
}
|
||||
decorator(req);
|
||||
if(! req.count(http::field::user_agent))
|
||||
req.set(http::field::user_agent,
|
||||
BOOST_BEAST_VERSION_STRING);
|
||||
return req;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator, class Decorator>
|
||||
response_type
|
||||
stream<NextLayer>::
|
||||
build_response(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
Decorator const& decorator)
|
||||
{
|
||||
auto const decorate =
|
||||
[&decorator](response_type& res)
|
||||
{
|
||||
decorator(res);
|
||||
if(! res.count(http::field::server))
|
||||
{
|
||||
BOOST_STATIC_ASSERT(sizeof(BOOST_BEAST_VERSION_STRING) < 20);
|
||||
static_string<20> s(BOOST_BEAST_VERSION_STRING);
|
||||
res.set(http::field::server, s);
|
||||
}
|
||||
};
|
||||
auto err =
|
||||
[&](std::string const& text)
|
||||
{
|
||||
response_type res;
|
||||
res.version = req.version;
|
||||
res.result(http::status::bad_request);
|
||||
res.body = text;
|
||||
res.prepare_payload();
|
||||
decorate(res);
|
||||
return res;
|
||||
};
|
||||
if(req.version < 11)
|
||||
return err("HTTP version 1.1 required");
|
||||
if(req.method() != http::verb::get)
|
||||
return err("Wrong method");
|
||||
if(! is_upgrade(req))
|
||||
return err("Expected Upgrade request");
|
||||
if(! req.count(http::field::host))
|
||||
return err("Missing Host");
|
||||
if(! req.count(http::field::sec_websocket_key))
|
||||
return err("Missing Sec-WebSocket-Key");
|
||||
if(! http::token_list{req[http::field::upgrade]}.exists("websocket"))
|
||||
return err("Missing websocket Upgrade token");
|
||||
auto const key = req[http::field::sec_websocket_key];
|
||||
if(key.size() > detail::sec_ws_key_type::max_size_n)
|
||||
return err("Invalid Sec-WebSocket-Key");
|
||||
{
|
||||
auto const version =
|
||||
req[http::field::sec_websocket_version];
|
||||
if(version.empty())
|
||||
return err("Missing Sec-WebSocket-Version");
|
||||
if(version != "13")
|
||||
{
|
||||
response_type res;
|
||||
res.result(http::status::upgrade_required);
|
||||
res.version = req.version;
|
||||
res.set(http::field::sec_websocket_version, "13");
|
||||
res.prepare_payload();
|
||||
decorate(res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
response_type res;
|
||||
{
|
||||
detail::pmd_offer offer;
|
||||
detail::pmd_offer unused;
|
||||
pmd_read(offer, req);
|
||||
pmd_negotiate(res, unused, offer, pmd_opts_);
|
||||
}
|
||||
res.result(http::status::switching_protocols);
|
||||
res.version = req.version;
|
||||
res.set(http::field::upgrade, "websocket");
|
||||
res.set(http::field::connection, "upgrade");
|
||||
{
|
||||
detail::sec_ws_accept_type acc;
|
||||
detail::make_sec_ws_accept(acc, key);
|
||||
res.set(http::field::sec_websocket_accept, acc);
|
||||
}
|
||||
decorate(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Decorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_accept(
|
||||
Decorator const& decorator, error_code& ec)
|
||||
{
|
||||
http::request_parser<http::empty_body> p;
|
||||
http::read_header(next_layer(),
|
||||
stream_.buffer(), p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
do_accept(p.get(), decorator, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Allocator,
|
||||
class Decorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_accept(http::request<Body,
|
||||
http::basic_fields<Allocator>> const& req,
|
||||
Decorator const& decorator, error_code& ec)
|
||||
{
|
||||
auto const res = build_response(req, decorator);
|
||||
http::write(stream_, res, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(res.result() != http::status::switching_protocols)
|
||||
{
|
||||
ec = error::handshake_failed;
|
||||
// VFALCO TODO Respect keep alive setting, perform
|
||||
// teardown if Connection: close.
|
||||
return;
|
||||
}
|
||||
pmd_read(pmd_config_, req);
|
||||
open(role_type::server);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class RequestDecorator>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_handshake(response_type* res_p,
|
||||
string_view host,
|
||||
string_view target,
|
||||
RequestDecorator const& decorator,
|
||||
error_code& ec)
|
||||
{
|
||||
response_type res;
|
||||
reset();
|
||||
detail::sec_ws_key_type key;
|
||||
{
|
||||
auto const req = build_request(
|
||||
key, host, target, decorator);
|
||||
pmd_read(pmd_config_, req);
|
||||
http::write(stream_, req, ec);
|
||||
}
|
||||
if(ec)
|
||||
return;
|
||||
http::read(next_layer(), stream_.buffer(), res, ec);
|
||||
if(ec)
|
||||
return;
|
||||
do_response(res, key, ec);
|
||||
if(res_p)
|
||||
*res_p = std::move(res);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_response(response_type const& res,
|
||||
detail::sec_ws_key_type const& key, error_code& ec)
|
||||
{
|
||||
bool const success = [&]()
|
||||
{
|
||||
if(res.version < 11)
|
||||
return false;
|
||||
if(res.result() != http::status::switching_protocols)
|
||||
return false;
|
||||
if(! http::token_list{res[http::field::connection]}.exists("upgrade"))
|
||||
return false;
|
||||
if(! http::token_list{res[http::field::upgrade]}.exists("websocket"))
|
||||
return false;
|
||||
if(res.count(http::field::sec_websocket_accept) != 1)
|
||||
return false;
|
||||
detail::sec_ws_accept_type acc;
|
||||
detail::make_sec_ws_accept(acc, key);
|
||||
if(acc.compare(
|
||||
res[http::field::sec_websocket_accept]) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}();
|
||||
if(! success)
|
||||
{
|
||||
ec = error::handshake_failed;
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
detail::pmd_offer offer;
|
||||
pmd_read(offer, res);
|
||||
// VFALCO see if offer satisfies pmd_config_,
|
||||
// return an error if not.
|
||||
pmd_config_ = offer; // overwrite for now
|
||||
open(role_type::client);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
|
||||
|
||||
#include <boost/beast/core/async_result.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Handler>
|
||||
class teardown_tcp_op
|
||||
{
|
||||
using socket_type =
|
||||
boost::asio::ip::tcp::socket;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
socket_type& socket;
|
||||
char buf[2048];
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, socket_type& socket_)
|
||||
: socket(socket_)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
cont = asio_handler_is_continuation(std::addressof(handler));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
template<class DeducedHandler>
|
||||
teardown_tcp_op(
|
||||
DeducedHandler&& h,
|
||||
socket_type& socket)
|
||||
: d_(std::forward<DeducedHandler>(h), socket)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(std::size_t size,
|
||||
teardown_tcp_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, teardown_tcp_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(teardown_tcp_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f,
|
||||
teardown_tcp_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Handler>
|
||||
void
|
||||
teardown_tcp_op<Handler>::
|
||||
operator()(error_code ec, std::size_t, bool again)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
d.socket.shutdown(
|
||||
boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
d.socket.async_read_some(
|
||||
buffer(d.buf), std::move(*this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
d.socket.close(ec);
|
||||
ec = error_code{};
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
void
|
||||
teardown(teardown_tag,
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
socket.shutdown(
|
||||
boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||
while(! ec)
|
||||
{
|
||||
char buf[8192];
|
||||
auto const n = socket.read_some(
|
||||
buffer(buf), ec);
|
||||
if(! n)
|
||||
break;
|
||||
}
|
||||
if(ec == boost::asio::error::eof)
|
||||
ec = error_code{};
|
||||
socket.close(ec);
|
||||
}
|
||||
|
||||
template<class TeardownHandler>
|
||||
inline
|
||||
void
|
||||
async_teardown(teardown_tag,
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_completion_handler<
|
||||
TeardownHandler, void(error_code)>::value,
|
||||
"TeardownHandler requirements not met");
|
||||
detail::teardown_tcp_op<typename std::decay<
|
||||
TeardownHandler>::type>{std::forward<
|
||||
TeardownHandler>(handler), socket};
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,930 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP
|
||||
#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP
|
||||
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/buffer_cat.hpp>
|
||||
#include <boost/beast/core/buffer_prefix.hpp>
|
||||
#include <boost/beast/core/consuming_buffers.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/flat_static_buffer.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/websocket/detail/frame.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
class stream<NextLayer>::write_some_op
|
||||
{
|
||||
struct data : op
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
consuming_buffers<Buffers> cb;
|
||||
bool fin;
|
||||
detail::frame_header fh;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::prepared_key key;
|
||||
std::uint64_t remain;
|
||||
int step = 0;
|
||||
int entry_state;
|
||||
token tok;
|
||||
|
||||
data(Handler& handler, stream<NextLayer>& ws_,
|
||||
bool fin_, Buffers const& bs)
|
||||
: ws(ws_)
|
||||
, cb(bs)
|
||||
, fin(fin_)
|
||||
, tok(ws.t_.unique())
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
cont = asio_handler_is_continuation(std::addressof(handler));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_some_op(write_some_op&&) = default;
|
||||
write_some_op(write_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_some_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
(*this)({}, 0, true);
|
||||
}
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred,
|
||||
bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_some_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_some_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_some_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_some_op<Buffers, Handler>::
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
using beast::detail::clamp;
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
enum
|
||||
{
|
||||
do_init = 0,
|
||||
do_nomask_nofrag = 20,
|
||||
do_nomask_frag = 30,
|
||||
do_mask_nofrag = 40,
|
||||
do_mask_frag = 50,
|
||||
do_deflate = 60,
|
||||
do_maybe_suspend = 80,
|
||||
do_upcall = 99
|
||||
};
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.ws.failed_ = true;
|
||||
goto upcall;
|
||||
}
|
||||
loop:
|
||||
switch(d.step)
|
||||
{
|
||||
case do_init:
|
||||
if(! d.ws.wr_.cont)
|
||||
{
|
||||
d.ws.wr_begin();
|
||||
d.fh.rsv1 = d.ws.wr_.compress;
|
||||
}
|
||||
else
|
||||
{
|
||||
d.fh.rsv1 = false;
|
||||
}
|
||||
d.fh.rsv2 = false;
|
||||
d.fh.rsv3 = false;
|
||||
d.fh.op = d.ws.wr_.cont ?
|
||||
detail::opcode::cont : d.ws.wr_opcode_;
|
||||
d.fh.mask =
|
||||
d.ws.role_ == role_type::client;
|
||||
|
||||
// entry_state determines which algorithm
|
||||
// we will use to send. If we suspend, we
|
||||
// will transition to entry_state + 1 on
|
||||
// the resume.
|
||||
if(d.ws.wr_.compress)
|
||||
{
|
||||
d.entry_state = do_deflate;
|
||||
}
|
||||
else if(! d.fh.mask)
|
||||
{
|
||||
if(! d.ws.wr_.autofrag)
|
||||
{
|
||||
d.entry_state = do_nomask_nofrag;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_.buf_size != 0);
|
||||
d.remain = buffer_size(d.cb);
|
||||
if(d.remain > d.ws.wr_.buf_size)
|
||||
d.entry_state = do_nomask_frag;
|
||||
else
|
||||
d.entry_state = do_nomask_nofrag;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(! d.ws.wr_.autofrag)
|
||||
{
|
||||
d.entry_state = do_mask_nofrag;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_.buf_size != 0);
|
||||
d.remain = buffer_size(d.cb);
|
||||
if(d.remain > d.ws.wr_.buf_size)
|
||||
d.entry_state = do_mask_frag;
|
||||
else
|
||||
d.entry_state = do_mask_nofrag;
|
||||
}
|
||||
}
|
||||
d.step = do_maybe_suspend;
|
||||
goto loop;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_nomask_nofrag:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.fh.fin = d.fin;
|
||||
d.fh.len = buffer_size(d.cb);
|
||||
detail::write<flat_static_buffer_base>(
|
||||
d.fh_buf, d.fh);
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame
|
||||
d.step = do_upcall;
|
||||
return boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(d.fh_buf.data(), d.cb),
|
||||
std::move(*this));
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_nomask_frag:
|
||||
case do_nomask_frag:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
auto const n = clamp(
|
||||
d.remain, d.ws.wr_.buf_size);
|
||||
d.remain -= n;
|
||||
d.fh.len = n;
|
||||
d.fh.fin = d.fin ? d.remain == 0 : false;
|
||||
detail::write<flat_static_buffer_base>(
|
||||
d.fh_buf, d.fh);
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame
|
||||
d.step = d.remain == 0 ?
|
||||
do_upcall : do_nomask_frag + 1;
|
||||
return boost::asio::async_write(
|
||||
d.ws.stream_, buffer_cat(
|
||||
d.fh_buf.data(), buffer_prefix(
|
||||
n, d.cb)), std::move(*this));
|
||||
}
|
||||
|
||||
case do_nomask_frag + 1:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.ws.wr_block_.reset();
|
||||
d.cb.consume(
|
||||
bytes_transferred - d.fh_buf.size());
|
||||
d.fh_buf.consume(d.fh_buf.size());
|
||||
d.fh.op = detail::opcode::cont;
|
||||
// Allow outgoing control frames to
|
||||
// be sent in between message frames
|
||||
if( d.ws.close_op_.maybe_invoke() ||
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke())
|
||||
{
|
||||
d.step = do_maybe_suspend;
|
||||
return d.ws.get_io_service().post(
|
||||
std::move(*this));
|
||||
}
|
||||
d.ws.wr_block_ = d.tok;
|
||||
goto go_nomask_frag;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_mask_nofrag:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.remain = buffer_size(d.cb);
|
||||
d.fh.fin = d.fin;
|
||||
d.fh.len = d.remain;
|
||||
d.fh.key = d.ws.maskgen_();
|
||||
detail::prepare_key(d.key, d.fh.key);
|
||||
detail::write<flat_static_buffer_base>(
|
||||
d.fh_buf, d.fh);
|
||||
auto const n =
|
||||
clamp(d.remain, d.ws.wr_.buf_size);
|
||||
auto const b =
|
||||
buffer(d.ws.wr_.buf.get(), n);
|
||||
buffer_copy(b, d.cb);
|
||||
detail::mask_inplace(b, d.key);
|
||||
d.remain -= n;
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame header and partial payload
|
||||
d.step = d.remain == 0 ?
|
||||
do_upcall : do_mask_nofrag + 1;
|
||||
return boost::asio::async_write(
|
||||
d.ws.stream_, buffer_cat(d.fh_buf.data(),
|
||||
b), std::move(*this));
|
||||
}
|
||||
|
||||
case do_mask_nofrag + 1:
|
||||
{
|
||||
d.cb.consume(d.ws.wr_.buf_size);
|
||||
auto const n =
|
||||
clamp(d.remain, d.ws.wr_.buf_size);
|
||||
auto const b =
|
||||
buffer(d.ws.wr_.buf.get(), n);
|
||||
buffer_copy(b, d.cb);
|
||||
detail::mask_inplace(b, d.key);
|
||||
d.remain -= n;
|
||||
// Send partial payload
|
||||
if(d.remain == 0)
|
||||
d.step = do_upcall;
|
||||
return boost::asio::async_write(
|
||||
d.ws.stream_, b, std::move(*this));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_mask_frag:
|
||||
case do_mask_frag:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
auto const n = clamp(
|
||||
d.remain, d.ws.wr_.buf_size);
|
||||
d.remain -= n;
|
||||
d.fh.len = n;
|
||||
d.fh.key = d.ws.maskgen_();
|
||||
d.fh.fin = d.fin ? d.remain == 0 : false;
|
||||
detail::prepare_key(d.key, d.fh.key);
|
||||
auto const b = buffer(
|
||||
d.ws.wr_.buf.get(), n);
|
||||
buffer_copy(b, d.cb);
|
||||
detail::mask_inplace(b, d.key);
|
||||
detail::write<flat_static_buffer_base>(
|
||||
d.fh_buf, d.fh);
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame
|
||||
d.step = d.remain == 0 ?
|
||||
do_upcall : do_mask_frag + 1;
|
||||
return boost::asio::async_write(
|
||||
d.ws.stream_, buffer_cat(
|
||||
d.fh_buf.data(), b),
|
||||
std::move(*this));
|
||||
}
|
||||
|
||||
case do_mask_frag + 1:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.ws.wr_block_.reset();
|
||||
d.cb.consume(
|
||||
bytes_transferred - d.fh_buf.size());
|
||||
d.fh_buf.consume(d.fh_buf.size());
|
||||
d.fh.op = detail::opcode::cont;
|
||||
// Allow outgoing control frames to
|
||||
// be sent in between message frames:
|
||||
if( d.ws.close_op_.maybe_invoke() ||
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke())
|
||||
{
|
||||
d.step = do_maybe_suspend;
|
||||
d.ws.get_io_service().post(
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.ws.wr_block_ = d.tok;
|
||||
goto go_mask_frag;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_deflate:
|
||||
case do_deflate:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
auto b = buffer(d.ws.wr_.buf.get(),
|
||||
d.ws.wr_.buf_size);
|
||||
auto const more = detail::deflate(
|
||||
d.ws.pmd_->zo, b, d.cb, d.fin, ec);
|
||||
d.ws.failed_ = !!ec;
|
||||
if(d.ws.failed_)
|
||||
goto upcall;
|
||||
auto const n = buffer_size(b);
|
||||
if(n == 0)
|
||||
{
|
||||
// The input was consumed, but there
|
||||
// is no output due to compression
|
||||
// latency.
|
||||
BOOST_ASSERT(! d.fin);
|
||||
BOOST_ASSERT(buffer_size(d.cb) == 0);
|
||||
|
||||
// We can skip the dispatch if the
|
||||
// asynchronous initiation function is
|
||||
// not on call stack but its hard to
|
||||
// figure out so be safe and dispatch.
|
||||
d.step = do_upcall;
|
||||
d.ws.get_io_service().post(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.fh.mask)
|
||||
{
|
||||
d.fh.key = d.ws.maskgen_();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, d.fh.key);
|
||||
detail::mask_inplace(b, key);
|
||||
}
|
||||
d.fh.fin = ! more;
|
||||
d.fh.len = n;
|
||||
detail::write<
|
||||
flat_static_buffer_base>(d.fh_buf, d.fh);
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame
|
||||
d.step = more ?
|
||||
do_deflate + 1 : do_deflate + 2;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(d.fh_buf.data(), b),
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
case do_deflate + 1:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
d.fh_buf.consume(d.fh_buf.size());
|
||||
d.ws.wr_block_.reset();
|
||||
d.fh.op = detail::opcode::cont;
|
||||
d.fh.rsv1 = false;
|
||||
// Allow outgoing control frames to
|
||||
// be sent in between message frames:
|
||||
if( d.ws.close_op_.maybe_invoke() ||
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke())
|
||||
{
|
||||
d.step = do_maybe_suspend;
|
||||
d.ws.get_io_service().post(
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.ws.wr_block_ = d.tok;
|
||||
goto go_deflate;
|
||||
|
||||
case do_deflate + 2:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
if(d.fh.fin && (
|
||||
(d.ws.role_ == role_type::client &&
|
||||
d.ws.pmd_config_.client_no_context_takeover) ||
|
||||
(d.ws.role_ == role_type::server &&
|
||||
d.ws.pmd_config_.server_no_context_takeover)))
|
||||
d.ws.pmd_->zo.reset();
|
||||
goto upcall;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_maybe_suspend:
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
BOOST_ASSERT(d.ws.wr_block_ != d.tok);
|
||||
d.step = do_maybe_suspend + 1;
|
||||
d.ws.wr_op_.emplace(std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.ws.wr_block_ = d.tok;
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
return d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted, 0));
|
||||
}
|
||||
d.step = d.entry_state;
|
||||
goto loop;
|
||||
|
||||
case do_maybe_suspend + 1:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = d.tok;
|
||||
d.step = do_maybe_suspend + 2;
|
||||
// The current context is safe but might not be
|
||||
// the same as the one for this operation (since
|
||||
// we are being called from a write operation).
|
||||
// Call post to make sure we are invoked the same
|
||||
// way as the final handler for this operation.
|
||||
d.ws.get_io_service().post(bind_handler(
|
||||
std::move(*this), ec, 0));
|
||||
return;
|
||||
|
||||
case do_maybe_suspend + 2:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == d.tok);
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
goto upcall;
|
||||
}
|
||||
d.step = d.entry_state;
|
||||
goto loop;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_upcall:
|
||||
goto upcall;
|
||||
}
|
||||
upcall:
|
||||
if(d.ws.wr_block_ == d.tok)
|
||||
d.ws.wr_block_.reset();
|
||||
d.ws.close_op_.maybe_invoke() ||
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke();
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
class stream<NextLayer>::write_op
|
||||
{
|
||||
struct data : op
|
||||
{
|
||||
int step = 0;
|
||||
stream<NextLayer>& ws;
|
||||
consuming_buffers<Buffers> cb;
|
||||
std::size_t remain;
|
||||
token tok;
|
||||
|
||||
data(Handler&, stream<NextLayer>& ws_,
|
||||
Buffers const& bs)
|
||||
: ws(ws_)
|
||||
, cb(bs)
|
||||
, remain(boost::asio::buffer_size(cb))
|
||||
, tok(ws.t_.unique())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
explicit
|
||||
write_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(error_code ec);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_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_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_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->d_->step > 2 ||
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_op<Buffers, Handler>::
|
||||
operator()(error_code ec)
|
||||
{
|
||||
auto& d = *d_;
|
||||
switch(d.step)
|
||||
{
|
||||
case 2:
|
||||
d.step = 3;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
case 3:
|
||||
case 0:
|
||||
{
|
||||
auto const n = d.remain;
|
||||
d.remain -= n;
|
||||
auto const fin = d.remain <= 0;
|
||||
if(fin)
|
||||
d.step = d.step ? 4 : 1;
|
||||
else
|
||||
d.step = d.step ? 3 : 2;
|
||||
auto const pb = buffer_prefix(n, d.cb);
|
||||
d.cb.consume(n);
|
||||
return d.ws.async_write_some(
|
||||
fin, pb, std::move(*this));
|
||||
}
|
||||
|
||||
case 1:
|
||||
case 4:
|
||||
break;
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_some(bool fin, ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
write_some(fin, buffers, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_some(bool fin,
|
||||
ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using beast::detail::clamp;
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
detail::frame_header fh;
|
||||
if(! wr_.cont)
|
||||
{
|
||||
wr_begin();
|
||||
fh.rsv1 = wr_.compress;
|
||||
}
|
||||
else
|
||||
{
|
||||
fh.rsv1 = false;
|
||||
}
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
fh.op = wr_.cont ?
|
||||
detail::opcode::cont : wr_opcode_;
|
||||
fh.mask = role_ == role_type::client;
|
||||
auto remain = buffer_size(buffers);
|
||||
if(wr_.compress)
|
||||
{
|
||||
consuming_buffers<
|
||||
ConstBufferSequence> cb{buffers};
|
||||
for(;;)
|
||||
{
|
||||
auto b = buffer(
|
||||
wr_.buf.get(), wr_.buf_size);
|
||||
auto const more = detail::deflate(
|
||||
pmd_->zo, b, cb, fin, ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
auto const n = buffer_size(b);
|
||||
if(n == 0)
|
||||
{
|
||||
// The input was consumed, but there
|
||||
// is no output due to compression
|
||||
// latency.
|
||||
BOOST_ASSERT(! fin);
|
||||
BOOST_ASSERT(buffer_size(cb) == 0);
|
||||
fh.fin = false;
|
||||
break;
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
fh.key = maskgen_();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
detail::mask_inplace(b, key);
|
||||
}
|
||||
fh.fin = ! more;
|
||||
fh.len = n;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<flat_static_buffer_base>(fh_buf, fh);
|
||||
wr_.cont = ! fin;
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), b), ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
if(! more)
|
||||
break;
|
||||
fh.op = detail::opcode::cont;
|
||||
fh.rsv1 = false;
|
||||
}
|
||||
if(fh.fin && (
|
||||
(role_ == role_type::client &&
|
||||
pmd_config_.client_no_context_takeover) ||
|
||||
(role_ == role_type::server &&
|
||||
pmd_config_.server_no_context_takeover)))
|
||||
pmd_->zo.reset();
|
||||
return;
|
||||
}
|
||||
if(! fh.mask)
|
||||
{
|
||||
if(! wr_.autofrag)
|
||||
{
|
||||
// no mask, no autofrag
|
||||
fh.fin = fin;
|
||||
fh.len = remain;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<flat_static_buffer_base>(fh_buf, fh);
|
||||
wr_.cont = ! fin;
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), buffers), ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no mask, autofrag
|
||||
BOOST_ASSERT(wr_.buf_size != 0);
|
||||
consuming_buffers<
|
||||
ConstBufferSequence> cb{buffers};
|
||||
for(;;)
|
||||
{
|
||||
auto const n = clamp(remain, wr_.buf_size);
|
||||
remain -= n;
|
||||
fh.len = n;
|
||||
fh.fin = fin ? remain == 0 : false;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<flat_static_buffer_base>(fh_buf, fh);
|
||||
wr_.cont = ! fin;
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(),
|
||||
buffer_prefix(n, cb)), ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
if(remain == 0)
|
||||
break;
|
||||
fh.op = detail::opcode::cont;
|
||||
cb.consume(n);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(! wr_.autofrag)
|
||||
{
|
||||
// mask, no autofrag
|
||||
fh.fin = fin;
|
||||
fh.len = remain;
|
||||
fh.key = maskgen_();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<flat_static_buffer_base>(fh_buf, fh);
|
||||
consuming_buffers<
|
||||
ConstBufferSequence> cb{buffers};
|
||||
{
|
||||
auto const n = clamp(remain, wr_.buf_size);
|
||||
auto const b = buffer(wr_.buf.get(), n);
|
||||
buffer_copy(b, cb);
|
||||
cb.consume(n);
|
||||
remain -= n;
|
||||
detail::mask_inplace(b, key);
|
||||
wr_.cont = ! fin;
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), b), ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
while(remain > 0)
|
||||
{
|
||||
auto const n = clamp(remain, wr_.buf_size);
|
||||
auto const b = buffer(wr_.buf.get(), n);
|
||||
buffer_copy(b, cb);
|
||||
cb.consume(n);
|
||||
remain -= n;
|
||||
detail::mask_inplace(b, key);
|
||||
boost::asio::write(stream_, b, ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
{
|
||||
// mask, autofrag
|
||||
BOOST_ASSERT(wr_.buf_size != 0);
|
||||
consuming_buffers<
|
||||
ConstBufferSequence> cb{buffers};
|
||||
for(;;)
|
||||
{
|
||||
fh.key = maskgen_();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
auto const n = clamp(remain, wr_.buf_size);
|
||||
auto const b = buffer(wr_.buf.get(), n);
|
||||
buffer_copy(b, cb);
|
||||
detail::mask_inplace(b, key);
|
||||
fh.len = n;
|
||||
remain -= n;
|
||||
fh.fin = fin ? remain == 0 : false;
|
||||
wr_.cont = ! fh.fin;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<flat_static_buffer_base>(fh_buf, fh);
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), b), ec);
|
||||
failed_ = !!ec;
|
||||
if(failed_)
|
||||
return;
|
||||
if(remain == 0)
|
||||
break;
|
||||
fh.op = detail::opcode::cont;
|
||||
cb.consume(n);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
async_return_type<
|
||||
WriteHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_write_some(bool fin,
|
||||
ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
static_assert(beast::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
write_some_op<ConstBufferSequence, handler_type<
|
||||
WriteHandler, void(error_code)>>{init.completion_handler,
|
||||
*this, fin, bs}({}, 0, false);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
write(buffers, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write(ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_stream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
write_some(true, buffers, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
async_return_type<
|
||||
WriteHandler, void(error_code)>
|
||||
stream<NextLayer>::
|
||||
async_write(
|
||||
ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_stream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
static_assert(beast::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
write_op<ConstBufferSequence, handler_type<
|
||||
WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, *this, bs}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user