mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 14:24:31 +02:00
Reorganize source files and definitions
This commit is contained in:
435
include/beast/websocket/impl/accept.ipp
Normal file
435
include/beast/websocket/impl/accept.ipp
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
||||||
|
#define BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
||||||
|
|
||||||
|
#include <beast/http/message.hpp>
|
||||||
|
#include <beast/http/parser_v1.hpp>
|
||||||
|
#include <beast/http/read.hpp>
|
||||||
|
#include <beast/http/string_body.hpp>
|
||||||
|
#include <beast/http/write.hpp>
|
||||||
|
#include <beast/core/handler_alloc.hpp>
|
||||||
|
#include <beast/core/prepare_buffers.hpp>
|
||||||
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Respond to an upgrade HTTP request
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Handler>
|
||||||
|
class stream<NextLayer>::response_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
stream<NextLayer>& ws;
|
||||||
|
http::response<http::string_body> resp;
|
||||||
|
Handler h;
|
||||||
|
error_code final_ec;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler,
|
||||||
|
class Body, class Headers>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||||
|
http::request<Body, Headers> const& req,
|
||||||
|
bool cont_)
|
||||||
|
: ws(ws_)
|
||||||
|
, resp(ws_.build_response(req))
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(cont_)
|
||||||
|
{
|
||||||
|
// can't call stream::reset() here
|
||||||
|
// otherwise accept_op will malfunction
|
||||||
|
//
|
||||||
|
if(resp.status != 101)
|
||||||
|
final_ec = error::handshake_failed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> 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::allocate_shared<data>(alloc_type{h},
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, response_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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.resp, std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// sent response
|
||||||
|
case 1:
|
||||||
|
d.state = 99;
|
||||||
|
ec = d.final_ec;
|
||||||
|
if(! ec)
|
||||||
|
d.ws.open(detail::role_type::server);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// read and respond to an upgrade request
|
||||||
|
//
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Handler>
|
||||||
|
class stream<NextLayer>::accept_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
stream<NextLayer>& ws;
|
||||||
|
http::request<http::string_body> req;
|
||||||
|
Handler h;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class Buffers>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||||
|
Buffers const& buffers)
|
||||||
|
: ws(ws_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
ws.reset();
|
||||||
|
ws.stream_.buffer().commit(buffer_copy(
|
||||||
|
ws.stream_.buffer().prepare(
|
||||||
|
buffer_size(buffers)), buffers));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> 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::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec)
|
||||||
|
{
|
||||||
|
(*this)(ec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
void* asio_handler_allocate(
|
||||||
|
std::size_t size, accept_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, accept_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(accept_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, accept_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::accept_op<Handler>::
|
||||||
|
operator()(error_code const& ec,
|
||||||
|
std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
beast::detail::ignore_unused(bytes_transferred);
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// read message
|
||||||
|
d.state = 1;
|
||||||
|
http::async_read(d.ws.next_layer(),
|
||||||
|
d.ws.stream_.buffer(), d.req,
|
||||||
|
std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got message
|
||||||
|
case 1:
|
||||||
|
// respond to request
|
||||||
|
#if 1
|
||||||
|
// VFALCO I have no idea why passing std::move(*this) crashes
|
||||||
|
d.state = 99;
|
||||||
|
d.ws.async_accept(d.req, *this);
|
||||||
|
#else
|
||||||
|
response_op<Handler>{
|
||||||
|
std::move(d.h), d.ws, d.req, true};
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class AcceptHandler>
|
||||||
|
typename async_completion<
|
||||||
|
AcceptHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_accept(AcceptHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements requirements not met");
|
||||||
|
return async_accept(boost::asio::null_buffers{},
|
||||||
|
std::forward<AcceptHandler>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence, class AcceptHandler>
|
||||||
|
typename async_completion<
|
||||||
|
AcceptHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements requirements not met");
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
AcceptHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
accept_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, bs};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Body, class Headers, class AcceptHandler>
|
||||||
|
typename async_completion<
|
||||||
|
AcceptHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_accept(http::request<Body, Headers> const& req,
|
||||||
|
AcceptHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
AcceptHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
reset();
|
||||||
|
response_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, req,
|
||||||
|
boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(completion.handler)};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
accept()
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
accept(boost::asio::null_buffers{}, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
accept(error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
accept(boost::asio::null_buffers{}, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
accept(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
accept(buffers, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
accept(ConstBufferSequence const& buffers, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
reset();
|
||||||
|
stream_.buffer().commit(buffer_copy(
|
||||||
|
stream_.buffer().prepare(
|
||||||
|
buffer_size(buffers)), buffers));
|
||||||
|
http::request<http::string_body> m;
|
||||||
|
http::read(next_layer(), stream_.buffer(), m, ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
accept(m, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
accept(http::request<Body, Headers> const& request)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
accept(request, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Body, class Headers>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
accept(http::request<Body, Headers> const& req,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
reset();
|
||||||
|
auto const res = build_response(req);
|
||||||
|
http::write(stream_, res, ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
if(res.status != 101)
|
||||||
|
{
|
||||||
|
ec = error::handshake_failed;
|
||||||
|
// VFALCO TODO Respect keep alive setting, perform
|
||||||
|
// teardown if Connection: close.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
open(detail::role_type::server);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
@@ -1,156 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP
|
|
||||||
#define BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP
|
|
||||||
|
|
||||||
#include <beast/websocket/impl/response_op.ipp>
|
|
||||||
#include <beast/http/message.hpp>
|
|
||||||
#include <beast/http/parser_v1.hpp>
|
|
||||||
#include <beast/http/read.hpp>
|
|
||||||
#include <beast/core/handler_alloc.hpp>
|
|
||||||
#include <beast/core/prepare_buffers.hpp>
|
|
||||||
#include <beast/core/detail/type_traits.hpp>
|
|
||||||
#include <boost/assert.hpp>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace websocket {
|
|
||||||
|
|
||||||
// read and respond to an upgrade request
|
|
||||||
//
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Handler>
|
|
||||||
class stream<NextLayer>::accept_op
|
|
||||||
{
|
|
||||||
using alloc_type =
|
|
||||||
handler_alloc<char, Handler>;
|
|
||||||
|
|
||||||
struct data
|
|
||||||
{
|
|
||||||
stream<NextLayer>& ws;
|
|
||||||
http::request<http::string_body> req;
|
|
||||||
Handler h;
|
|
||||||
bool cont;
|
|
||||||
int state = 0;
|
|
||||||
|
|
||||||
template<class DeducedHandler, class Buffers>
|
|
||||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
|
||||||
Buffers const& buffers)
|
|
||||||
: ws(ws_)
|
|
||||||
, h(std::forward<DeducedHandler>(h_))
|
|
||||||
, cont(boost_asio_handler_cont_helpers::
|
|
||||||
is_continuation(h))
|
|
||||||
{
|
|
||||||
using boost::asio::buffer_copy;
|
|
||||||
using boost::asio::buffer_size;
|
|
||||||
ws.reset();
|
|
||||||
ws.stream_.buffer().commit(buffer_copy(
|
|
||||||
ws.stream_.buffer().prepare(
|
|
||||||
buffer_size(buffers)), buffers));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<data> 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::allocate_shared<data>(alloc_type{h},
|
|
||||||
std::forward<DeducedHandler>(h), ws,
|
|
||||||
std::forward<Args>(args)...))
|
|
||||||
{
|
|
||||||
(*this)(error_code{}, 0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(error_code const& ec)
|
|
||||||
{
|
|
||||||
(*this)(ec, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(error_code const& ec,
|
|
||||||
std::size_t bytes_transferred, bool again = true);
|
|
||||||
|
|
||||||
friend
|
|
||||||
void* asio_handler_allocate(
|
|
||||||
std::size_t size, accept_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
allocate(size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void asio_handler_deallocate(
|
|
||||||
void* p, std::size_t size, accept_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
deallocate(p, size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
bool asio_handler_is_continuation(accept_op* op)
|
|
||||||
{
|
|
||||||
return op->d_->cont;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Function>
|
|
||||||
friend
|
|
||||||
void asio_handler_invoke(Function&& f, accept_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_invoke_helpers::
|
|
||||||
invoke(f, op->d_->h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Handler>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::accept_op<Handler>::
|
|
||||||
operator()(error_code const& ec,
|
|
||||||
std::size_t bytes_transferred, bool again)
|
|
||||||
{
|
|
||||||
beast::detail::ignore_unused(bytes_transferred);
|
|
||||||
auto& d = *d_;
|
|
||||||
d.cont = d.cont || again;
|
|
||||||
while(! ec && d.state != 99)
|
|
||||||
{
|
|
||||||
switch(d.state)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
// read message
|
|
||||||
d.state = 1;
|
|
||||||
http::async_read(d.ws.next_layer(),
|
|
||||||
d.ws.stream_.buffer(), d.req,
|
|
||||||
std::move(*this));
|
|
||||||
return;
|
|
||||||
|
|
||||||
// got message
|
|
||||||
case 1:
|
|
||||||
// respond to request
|
|
||||||
#if 1
|
|
||||||
// VFALCO I have no idea why passing std::move(*this) crashes
|
|
||||||
d.state = 99;
|
|
||||||
d.ws.async_accept(d.req, *this);
|
|
||||||
#else
|
|
||||||
response_op<Handler>{
|
|
||||||
std::move(d.h), d.ws, d.req, true};
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.h(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // websocket
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
@@ -5,16 +5,19 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP
|
#ifndef BEAST_WEBSOCKET_IMPL_CLOSE_IPP
|
||||||
#define BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP
|
#define BEAST_WEBSOCKET_IMPL_CLOSE_IPP
|
||||||
|
|
||||||
#include <beast/core/handler_alloc.hpp>
|
#include <beast/core/handler_alloc.hpp>
|
||||||
#include <beast/core/static_streambuf.hpp>
|
#include <beast/core/static_streambuf.hpp>
|
||||||
|
#include <beast/core/stream_concepts.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace websocket {
|
namespace websocket {
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// send the close message and wait for the response
|
// send the close message and wait for the response
|
||||||
//
|
//
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -188,6 +191,53 @@ upcall:
|
|||||||
d.h(ec);
|
d.h(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class CloseHandler>
|
||||||
|
typename async_completion<
|
||||||
|
CloseHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_close(close_reason const& cr, CloseHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
CloseHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
close_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, cr};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
close(close_reason const& cr)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
close(cr, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
close(close_reason const& cr, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
BOOST_ASSERT(! wr_close_);
|
||||||
|
wr_close_ = true;
|
||||||
|
detail::frame_streambuf fb;
|
||||||
|
write_close<static_streambuf>(fb, cr);
|
||||||
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
|
|
@@ -5,20 +5,23 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP
|
#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
|
||||||
#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP
|
#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
|
||||||
|
|
||||||
#include <beast/http/empty_body.hpp>
|
#include <beast/http/empty_body.hpp>
|
||||||
#include <beast/http/message.hpp>
|
#include <beast/http/message.hpp>
|
||||||
#include <beast/http/read.hpp>
|
#include <beast/http/read.hpp>
|
||||||
#include <beast/http/write.hpp>
|
#include <beast/http/write.hpp>
|
||||||
#include <beast/core/handler_alloc.hpp>
|
#include <beast/core/handler_alloc.hpp>
|
||||||
|
#include <beast/core/stream_concepts.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace websocket {
|
namespace websocket {
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// send the upgrade request and process the response
|
// send the upgrade request and process the response
|
||||||
//
|
//
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -147,6 +150,61 @@ operator()(error_code ec, bool again)
|
|||||||
d.h(ec);
|
d.h(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class HandshakeHandler>
|
||||||
|
typename async_completion<
|
||||||
|
HandshakeHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_handshake(boost::string_ref const& host,
|
||||||
|
boost::string_ref const& resource, HandshakeHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
HandshakeHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
handshake_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, host, resource};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
handshake(boost::string_ref const& host,
|
||||||
|
boost::string_ref const& resource)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
handshake(host, resource, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
handshake(boost::string_ref const& host,
|
||||||
|
boost::string_ref const& resource, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
reset();
|
||||||
|
std::string key;
|
||||||
|
http::write(stream_,
|
||||||
|
build_request(host, resource, key), ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
http::response<http::string_body> res;
|
||||||
|
http::read(next_layer(), stream_.buffer(), res, ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
do_response(res, key, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
|
|
@@ -5,17 +5,20 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_PING_OP_HPP
|
#ifndef BEAST_WEBSOCKET_IMPL_PING_IPP
|
||||||
#define BEAST_WEBSOCKET_IMPL_PING_OP_HPP
|
#define BEAST_WEBSOCKET_IMPL_PING_IPP
|
||||||
|
|
||||||
#include <beast/core/bind_handler.hpp>
|
#include <beast/core/bind_handler.hpp>
|
||||||
#include <beast/core/handler_alloc.hpp>
|
#include <beast/core/handler_alloc.hpp>
|
||||||
|
#include <beast/core/stream_concepts.hpp>
|
||||||
#include <beast/websocket/detail/frame.hpp>
|
#include <beast/websocket/detail/frame.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace websocket {
|
namespace websocket {
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// write a ping frame
|
// write a ping frame
|
||||||
//
|
//
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
@@ -187,6 +190,47 @@ upcall:
|
|||||||
d.h(ec);
|
d.h(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class PingHandler>
|
||||||
|
typename async_completion<
|
||||||
|
PingHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_ping(ping_data const& payload, PingHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
PingHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
ping_op<decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, payload};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
ping(ping_data const& payload)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ping(payload, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
ping(ping_data const& payload, error_code& ec)
|
||||||
|
{
|
||||||
|
detail::frame_streambuf db;
|
||||||
|
write_ping<static_streambuf>(
|
||||||
|
db, opcode::ping, payload);
|
||||||
|
boost::asio::write(stream_, db.data(), ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
|
|
@@ -5,13 +5,15 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP
|
#ifndef BEAST_WEBSOCKET_IMPL_READ_IPP
|
||||||
#define BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP
|
#define BEAST_WEBSOCKET_IMPL_READ_IPP
|
||||||
|
|
||||||
#include <beast/websocket/teardown.hpp>
|
#include <beast/websocket/teardown.hpp>
|
||||||
|
#include <beast/core/buffer_concepts.hpp>
|
||||||
#include <beast/core/handler_alloc.hpp>
|
#include <beast/core/handler_alloc.hpp>
|
||||||
#include <beast/core/prepare_buffers.hpp>
|
#include <beast/core/prepare_buffers.hpp>
|
||||||
#include <beast/core/static_streambuf.hpp>
|
#include <beast/core/static_streambuf.hpp>
|
||||||
|
#include <beast/core/stream_concepts.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -19,6 +21,8 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace websocket {
|
namespace websocket {
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Reads a single message frame,
|
// Reads a single message frame,
|
||||||
// processes any received control frames.
|
// processes any received control frames.
|
||||||
//
|
//
|
||||||
@@ -541,6 +545,368 @@ upcall:
|
|||||||
d.h(ec);
|
d.h(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class DynamicBuffer, class ReadHandler>
|
||||||
|
typename async_completion<
|
||||||
|
ReadHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_read_frame(frame_info& fi,
|
||||||
|
DynamicBuffer& dynabuf, ReadHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements requirements not met");
|
||||||
|
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
||||||
|
"DynamicBuffer requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
ReadHandler, void(error_code)> completion(handler);
|
||||||
|
read_frame_op<DynamicBuffer, decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, fi, dynabuf};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
read_frame(frame_info& fi, DynamicBuffer& dynabuf)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
||||||
|
"DynamicBuffer requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
read_frame(fi, dynabuf, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
||||||
|
"DynamicBuffer requirements not met");
|
||||||
|
close_code::value code{};
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(rd_need_ == 0)
|
||||||
|
{
|
||||||
|
// read header
|
||||||
|
detail::frame_streambuf fb;
|
||||||
|
do_read_fh(fb, code, ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
if(code != close_code::none)
|
||||||
|
break;
|
||||||
|
if(detail::is_control(rd_fh_.op))
|
||||||
|
{
|
||||||
|
// read control payload
|
||||||
|
if(rd_fh_.len > 0)
|
||||||
|
{
|
||||||
|
auto const mb = fb.prepare(
|
||||||
|
static_cast<std::size_t>(rd_fh_.len));
|
||||||
|
fb.commit(boost::asio::read(stream_, mb, ec));
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
if(rd_fh_.mask)
|
||||||
|
detail::mask_inplace(mb, rd_key_);
|
||||||
|
fb.commit(static_cast<std::size_t>(rd_fh_.len));
|
||||||
|
}
|
||||||
|
if(rd_fh_.op == opcode::ping)
|
||||||
|
{
|
||||||
|
ping_data data;
|
||||||
|
detail::read(data, fb.data());
|
||||||
|
fb.reset();
|
||||||
|
write_ping<static_streambuf>(
|
||||||
|
fb, opcode::pong, data);
|
||||||
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(rd_fh_.op == opcode::pong)
|
||||||
|
{
|
||||||
|
ping_data payload;
|
||||||
|
detail::read(payload, fb.data());
|
||||||
|
if(pong_cb_)
|
||||||
|
pong_cb_(payload);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
BOOST_ASSERT(rd_fh_.op == opcode::close);
|
||||||
|
{
|
||||||
|
detail::read(cr_, fb.data(), code);
|
||||||
|
if(code != close_code::none)
|
||||||
|
break;
|
||||||
|
if(! wr_close_)
|
||||||
|
{
|
||||||
|
auto cr = cr_;
|
||||||
|
if(cr.code == close_code::none)
|
||||||
|
cr.code = close_code::normal;
|
||||||
|
cr.reason = "";
|
||||||
|
fb.reset();
|
||||||
|
wr_close_ = true;
|
||||||
|
write_close<static_streambuf>(fb, cr);
|
||||||
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rd_need_ == 0 && ! rd_fh_.fin)
|
||||||
|
{
|
||||||
|
// empty frame
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// read payload
|
||||||
|
auto smb = dynabuf.prepare(
|
||||||
|
detail::clamp(rd_need_));
|
||||||
|
auto const bytes_transferred =
|
||||||
|
stream_.read_some(smb, ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
rd_need_ -= bytes_transferred;
|
||||||
|
auto const pb = prepare_buffers(
|
||||||
|
bytes_transferred, smb);
|
||||||
|
if(rd_fh_.mask)
|
||||||
|
detail::mask_inplace(pb, rd_key_);
|
||||||
|
if(rd_opcode_ == opcode::text)
|
||||||
|
{
|
||||||
|
if(! rd_utf8_check_.write(pb) ||
|
||||||
|
(rd_need_ == 0 && rd_fh_.fin &&
|
||||||
|
! rd_utf8_check_.finish()))
|
||||||
|
{
|
||||||
|
code = close_code::bad_payload;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dynabuf.commit(bytes_transferred);
|
||||||
|
fi.op = rd_opcode_;
|
||||||
|
fi.fin = rd_fh_.fin && rd_need_ == 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(code != close_code::none)
|
||||||
|
{
|
||||||
|
// Fail the connection (per rfc6455)
|
||||||
|
if(! wr_close_)
|
||||||
|
{
|
||||||
|
wr_close_ = true;
|
||||||
|
detail::frame_streambuf fb;
|
||||||
|
write_close<static_streambuf>(fb, code);
|
||||||
|
boost::asio::write(stream_, fb.data(), ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
websocket_helpers::call_teardown(next_layer(), ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
ec = error::failed;
|
||||||
|
failed_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(! ec)
|
||||||
|
websocket_helpers::call_teardown(next_layer(), ec);
|
||||||
|
if(! ec)
|
||||||
|
ec = error::closed;
|
||||||
|
failed_ = ec != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// read an entire message
|
||||||
|
//
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class DynamicBuffer, class Handler>
|
||||||
|
class stream<NextLayer>::read_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
stream<NextLayer>& ws;
|
||||||
|
opcode& op;
|
||||||
|
DynamicBuffer& db;
|
||||||
|
Handler h;
|
||||||
|
frame_info fi;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_,
|
||||||
|
stream<NextLayer>& ws_, opcode& op_,
|
||||||
|
DynamicBuffer& sb_)
|
||||||
|
: ws(ws_)
|
||||||
|
, op(op_)
|
||||||
|
, db(sb_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
read_op(read_op&&) = default;
|
||||||
|
read_op(read_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
read_op(DeducedHandler&& h,
|
||||||
|
stream<NextLayer>& ws, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(
|
||||||
|
error_code const& ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
void* asio_handler_allocate(
|
||||||
|
std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(read_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class DynamicBuffer, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::read_op<DynamicBuffer, Handler>::
|
||||||
|
operator()(error_code const& ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// read payload
|
||||||
|
d.state = 1;
|
||||||
|
#if 0
|
||||||
|
// VFALCO This causes dereference of null, because
|
||||||
|
// the handler is moved from the data block
|
||||||
|
// before asio_handler_deallocate is called.
|
||||||
|
d.ws.async_read_frame(
|
||||||
|
d.fi, d.db, std::move(*this));
|
||||||
|
#else
|
||||||
|
d.ws.async_read_frame(d.fi, d.db, *this);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got payload
|
||||||
|
case 1:
|
||||||
|
d.op = d.fi.op;
|
||||||
|
if(d.fi.fin)
|
||||||
|
goto upcall;
|
||||||
|
d.state = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upcall:
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class DynamicBuffer, class ReadHandler>
|
||||||
|
typename async_completion<
|
||||||
|
ReadHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_read(opcode& op,
|
||||||
|
DynamicBuffer& dynabuf, ReadHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements requirements not met");
|
||||||
|
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
||||||
|
"DynamicBuffer requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
ReadHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
read_op<DynamicBuffer, decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, op, dynabuf};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
read(opcode& op, DynamicBuffer& dynabuf)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
||||||
|
"DynamicBuffer requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
read(op, dynabuf, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class DynamicBuffer>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
read(opcode& op, DynamicBuffer& dynabuf, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
||||||
|
"DynamicBuffer requirements not met");
|
||||||
|
frame_info fi;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
read_frame(fi, dynabuf, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
op = fi.op;
|
||||||
|
if(fi.fin)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
|
|
@@ -1,142 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_READ_OP_HPP
|
|
||||||
#define BEAST_WEBSOCKET_IMPL_READ_OP_HPP
|
|
||||||
|
|
||||||
#include <beast/core/handler_alloc.hpp>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace websocket {
|
|
||||||
|
|
||||||
// read an entire message
|
|
||||||
//
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class DynamicBuffer, class Handler>
|
|
||||||
class stream<NextLayer>::read_op
|
|
||||||
{
|
|
||||||
using alloc_type =
|
|
||||||
handler_alloc<char, Handler>;
|
|
||||||
|
|
||||||
struct data
|
|
||||||
{
|
|
||||||
stream<NextLayer>& ws;
|
|
||||||
opcode& op;
|
|
||||||
DynamicBuffer& db;
|
|
||||||
Handler h;
|
|
||||||
frame_info fi;
|
|
||||||
bool cont;
|
|
||||||
int state = 0;
|
|
||||||
|
|
||||||
template<class DeducedHandler>
|
|
||||||
data(DeducedHandler&& h_,
|
|
||||||
stream<NextLayer>& ws_, opcode& op_,
|
|
||||||
DynamicBuffer& sb_)
|
|
||||||
: ws(ws_)
|
|
||||||
, op(op_)
|
|
||||||
, db(sb_)
|
|
||||||
, h(std::forward<DeducedHandler>(h_))
|
|
||||||
, cont(boost_asio_handler_cont_helpers::
|
|
||||||
is_continuation(h))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<data> d_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
read_op(read_op&&) = default;
|
|
||||||
read_op(read_op const&) = default;
|
|
||||||
|
|
||||||
template<class DeducedHandler, class... Args>
|
|
||||||
read_op(DeducedHandler&& h,
|
|
||||||
stream<NextLayer>& ws, Args&&... args)
|
|
||||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
|
||||||
std::forward<DeducedHandler>(h), ws,
|
|
||||||
std::forward<Args>(args)...))
|
|
||||||
{
|
|
||||||
(*this)(error_code{}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(
|
|
||||||
error_code const& ec, bool again = true);
|
|
||||||
|
|
||||||
friend
|
|
||||||
void* asio_handler_allocate(
|
|
||||||
std::size_t size, read_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
allocate(size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void asio_handler_deallocate(
|
|
||||||
void* p, std::size_t size, read_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
deallocate(p, size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
bool asio_handler_is_continuation(read_op* op)
|
|
||||||
{
|
|
||||||
return op->d_->cont;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Function>
|
|
||||||
friend
|
|
||||||
void asio_handler_invoke(Function&& f, read_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_invoke_helpers::
|
|
||||||
invoke(f, op->d_->h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class DynamicBuffer, class Handler>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::read_op<DynamicBuffer, Handler>::
|
|
||||||
operator()(error_code const& ec, bool again)
|
|
||||||
{
|
|
||||||
auto& d = *d_;
|
|
||||||
d.cont = d.cont || again;
|
|
||||||
while(! ec)
|
|
||||||
{
|
|
||||||
switch(d.state)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
// read payload
|
|
||||||
d.state = 1;
|
|
||||||
#if 0
|
|
||||||
// VFALCO This causes dereference of null, because
|
|
||||||
// the handler is moved from the data block
|
|
||||||
// before asio_handler_deallocate is called.
|
|
||||||
d.ws.async_read_frame(
|
|
||||||
d.fi, d.db, std::move(*this));
|
|
||||||
#else
|
|
||||||
d.ws.async_read_frame(d.fi, d.db, *this);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
|
|
||||||
// got payload
|
|
||||||
case 1:
|
|
||||||
d.op = d.fi.op;
|
|
||||||
if(d.fi.fin)
|
|
||||||
goto upcall;
|
|
||||||
d.state = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
upcall:
|
|
||||||
d.h(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // websocket
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,139 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP
|
|
||||||
#define BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP
|
|
||||||
|
|
||||||
#include <beast/http/message.hpp>
|
|
||||||
#include <beast/http/string_body.hpp>
|
|
||||||
#include <beast/http/write.hpp>
|
|
||||||
#include <beast/core/handler_alloc.hpp>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace websocket {
|
|
||||||
|
|
||||||
// Respond to an upgrade HTTP request
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Handler>
|
|
||||||
class stream<NextLayer>::response_op
|
|
||||||
{
|
|
||||||
using alloc_type =
|
|
||||||
handler_alloc<char, Handler>;
|
|
||||||
|
|
||||||
struct data
|
|
||||||
{
|
|
||||||
stream<NextLayer>& ws;
|
|
||||||
http::response<http::string_body> resp;
|
|
||||||
Handler h;
|
|
||||||
error_code final_ec;
|
|
||||||
bool cont;
|
|
||||||
int state = 0;
|
|
||||||
|
|
||||||
template<class DeducedHandler,
|
|
||||||
class Body, class Headers>
|
|
||||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
|
||||||
http::request<Body, Headers> const& req,
|
|
||||||
bool cont_)
|
|
||||||
: ws(ws_)
|
|
||||||
, resp(ws_.build_response(req))
|
|
||||||
, h(std::forward<DeducedHandler>(h_))
|
|
||||||
, cont(cont_)
|
|
||||||
{
|
|
||||||
// can't call stream::reset() here
|
|
||||||
// otherwise accept_op will malfunction
|
|
||||||
//
|
|
||||||
if(resp.status != 101)
|
|
||||||
final_ec = error::handshake_failed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<data> 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::allocate_shared<data>(alloc_type{h},
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
allocate(size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void asio_handler_deallocate(
|
|
||||||
void* p, std::size_t size, response_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
deallocate(p, size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_invoke_helpers::
|
|
||||||
invoke(f, op->d_->h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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.resp, std::move(*this));
|
|
||||||
return;
|
|
||||||
|
|
||||||
// sent response
|
|
||||||
case 1:
|
|
||||||
d.state = 99;
|
|
||||||
ec = d.final_ec;
|
|
||||||
if(! ec)
|
|
||||||
d.ws.open(detail::role_type::server);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.h(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // websocket
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
@@ -10,15 +10,6 @@
|
|||||||
|
|
||||||
#include <beast/websocket/teardown.hpp>
|
#include <beast/websocket/teardown.hpp>
|
||||||
#include <beast/websocket/detail/hybi13.hpp>
|
#include <beast/websocket/detail/hybi13.hpp>
|
||||||
#include <beast/websocket/impl/accept_op.ipp>
|
|
||||||
#include <beast/websocket/impl/close_op.ipp>
|
|
||||||
#include <beast/websocket/impl/handshake_op.ipp>
|
|
||||||
#include <beast/websocket/impl/ping_op.ipp>
|
|
||||||
#include <beast/websocket/impl/read_op.ipp>
|
|
||||||
#include <beast/websocket/impl/read_frame_op.ipp>
|
|
||||||
#include <beast/websocket/impl/response_op.ipp>
|
|
||||||
#include <beast/websocket/impl/write_op.ipp>
|
|
||||||
#include <beast/websocket/impl/write_frame_op.ipp>
|
|
||||||
#include <beast/http/read.hpp>
|
#include <beast/http/read.hpp>
|
||||||
#include <beast/http/write.hpp>
|
#include <beast/http/write.hpp>
|
||||||
#include <beast/http/reason.hpp>
|
#include <beast/http/reason.hpp>
|
||||||
@@ -46,779 +37,6 @@ stream(Args&&... args)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
accept()
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
accept(boost::asio::null_buffers{}, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
accept(error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
accept(boost::asio::null_buffers{}, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class AcceptHandler>
|
|
||||||
typename async_completion<
|
|
||||||
AcceptHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_accept(AcceptHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements requirements not met");
|
|
||||||
return async_accept(boost::asio::null_buffers{},
|
|
||||||
std::forward<AcceptHandler>(handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
accept(ConstBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
accept(buffers, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
accept(ConstBufferSequence const& buffers, error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
using boost::asio::buffer_copy;
|
|
||||||
using boost::asio::buffer_size;
|
|
||||||
reset();
|
|
||||||
stream_.buffer().commit(buffer_copy(
|
|
||||||
stream_.buffer().prepare(
|
|
||||||
buffer_size(buffers)), buffers));
|
|
||||||
http::request<http::string_body> m;
|
|
||||||
http::read(next_layer(), stream_.buffer(), m, ec);
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
accept(m, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence, class AcceptHandler>
|
|
||||||
typename async_completion<
|
|
||||||
AcceptHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements requirements not met");
|
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
AcceptHandler, void(error_code)
|
|
||||||
> completion(handler);
|
|
||||||
accept_op<decltype(completion.handler)>{
|
|
||||||
completion.handler, *this, bs};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Body, class Headers>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
accept(http::request<Body, Headers> const& request)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
accept(request, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Body, class Headers>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
accept(http::request<Body, Headers> const& req,
|
|
||||||
error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
reset();
|
|
||||||
auto const res = build_response(req);
|
|
||||||
http::write(stream_, res, ec);
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
if(res.status != 101)
|
|
||||||
{
|
|
||||||
ec = error::handshake_failed;
|
|
||||||
// VFALCO TODO Respect keep alive setting, perform
|
|
||||||
// teardown if Connection: close.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
open(detail::role_type::server);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Body, class Headers, class AcceptHandler>
|
|
||||||
typename async_completion<
|
|
||||||
AcceptHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_accept(http::request<Body, Headers> const& req,
|
|
||||||
AcceptHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
AcceptHandler, void(error_code)
|
|
||||||
> completion(handler);
|
|
||||||
reset();
|
|
||||||
response_op<decltype(completion.handler)>{
|
|
||||||
completion.handler, *this, req,
|
|
||||||
boost_asio_handler_cont_helpers::
|
|
||||||
is_continuation(completion.handler)};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
handshake(boost::string_ref const& host,
|
|
||||||
boost::string_ref const& resource)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
handshake(host, resource, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
handshake(boost::string_ref const& host,
|
|
||||||
boost::string_ref const& resource, error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
reset();
|
|
||||||
std::string key;
|
|
||||||
http::write(stream_,
|
|
||||||
build_request(host, resource, key), ec);
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
http::response<http::string_body> res;
|
|
||||||
http::read(next_layer(), stream_.buffer(), res, ec);
|
|
||||||
if(ec)
|
|
||||||
return;
|
|
||||||
do_response(res, key, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class HandshakeHandler>
|
|
||||||
typename async_completion<
|
|
||||||
HandshakeHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_handshake(boost::string_ref const& host,
|
|
||||||
boost::string_ref const& resource, HandshakeHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
HandshakeHandler, void(error_code)
|
|
||||||
> completion(handler);
|
|
||||||
handshake_op<decltype(completion.handler)>{
|
|
||||||
completion.handler, *this, host, resource};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
close(close_reason const& cr)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
close(cr, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
close(close_reason const& cr, error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
BOOST_ASSERT(! wr_close_);
|
|
||||||
wr_close_ = true;
|
|
||||||
detail::frame_streambuf fb;
|
|
||||||
write_close<static_streambuf>(fb, cr);
|
|
||||||
boost::asio::write(stream_, fb.data(), ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class CloseHandler>
|
|
||||||
typename async_completion<
|
|
||||||
CloseHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_close(close_reason const& cr, CloseHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
CloseHandler, void(error_code)
|
|
||||||
> completion(handler);
|
|
||||||
close_op<decltype(completion.handler)>{
|
|
||||||
completion.handler, *this, cr};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
ping(ping_data const& payload)
|
|
||||||
{
|
|
||||||
error_code ec;
|
|
||||||
ping(payload, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
ping(ping_data const& payload, error_code& ec)
|
|
||||||
{
|
|
||||||
detail::frame_streambuf db;
|
|
||||||
write_ping<static_streambuf>(
|
|
||||||
db, opcode::ping, payload);
|
|
||||||
boost::asio::write(stream_, db.data(), ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class PingHandler>
|
|
||||||
typename async_completion<
|
|
||||||
PingHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_ping(ping_data const& payload, PingHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
PingHandler, void(error_code)
|
|
||||||
> completion(handler);
|
|
||||||
ping_op<decltype(completion.handler)>{
|
|
||||||
completion.handler, *this, payload};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
read(opcode& op, DynamicBuffer& dynabuf)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
|
||||||
"DynamicBuffer requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
read(op, dynabuf, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
read(opcode& op, DynamicBuffer& dynabuf, error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
|
||||||
"DynamicBuffer requirements not met");
|
|
||||||
frame_info fi;
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
read_frame(fi, dynabuf, ec);
|
|
||||||
if(ec)
|
|
||||||
break;
|
|
||||||
op = fi.op;
|
|
||||||
if(fi.fin)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class DynamicBuffer, class ReadHandler>
|
|
||||||
typename async_completion<
|
|
||||||
ReadHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_read(opcode& op,
|
|
||||||
DynamicBuffer& dynabuf, ReadHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements requirements not met");
|
|
||||||
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
|
||||||
"DynamicBuffer requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
ReadHandler, void(error_code)
|
|
||||||
> completion(handler);
|
|
||||||
read_op<DynamicBuffer, decltype(completion.handler)>{
|
|
||||||
completion.handler, *this, op, dynabuf};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
read_frame(frame_info& fi, DynamicBuffer& dynabuf)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
|
||||||
"DynamicBuffer requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
read_frame(fi, dynabuf, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class DynamicBuffer>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
read_frame(frame_info& fi, DynamicBuffer& dynabuf, error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
|
||||||
"DynamicBuffer requirements not met");
|
|
||||||
close_code::value code{};
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
if(rd_need_ == 0)
|
|
||||||
{
|
|
||||||
// read header
|
|
||||||
detail::frame_streambuf fb;
|
|
||||||
do_read_fh(fb, code, ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
if(code != close_code::none)
|
|
||||||
break;
|
|
||||||
if(detail::is_control(rd_fh_.op))
|
|
||||||
{
|
|
||||||
// read control payload
|
|
||||||
if(rd_fh_.len > 0)
|
|
||||||
{
|
|
||||||
auto const mb = fb.prepare(
|
|
||||||
static_cast<std::size_t>(rd_fh_.len));
|
|
||||||
fb.commit(boost::asio::read(stream_, mb, ec));
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
if(rd_fh_.mask)
|
|
||||||
detail::mask_inplace(mb, rd_key_);
|
|
||||||
fb.commit(static_cast<std::size_t>(rd_fh_.len));
|
|
||||||
}
|
|
||||||
if(rd_fh_.op == opcode::ping)
|
|
||||||
{
|
|
||||||
ping_data data;
|
|
||||||
detail::read(data, fb.data());
|
|
||||||
fb.reset();
|
|
||||||
write_ping<static_streambuf>(
|
|
||||||
fb, opcode::pong, data);
|
|
||||||
boost::asio::write(stream_, fb.data(), ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if(rd_fh_.op == opcode::pong)
|
|
||||||
{
|
|
||||||
ping_data payload;
|
|
||||||
detail::read(payload, fb.data());
|
|
||||||
if(pong_cb_)
|
|
||||||
pong_cb_(payload);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
BOOST_ASSERT(rd_fh_.op == opcode::close);
|
|
||||||
{
|
|
||||||
detail::read(cr_, fb.data(), code);
|
|
||||||
if(code != close_code::none)
|
|
||||||
break;
|
|
||||||
if(! wr_close_)
|
|
||||||
{
|
|
||||||
auto cr = cr_;
|
|
||||||
if(cr.code == close_code::none)
|
|
||||||
cr.code = close_code::normal;
|
|
||||||
cr.reason = "";
|
|
||||||
fb.reset();
|
|
||||||
wr_close_ = true;
|
|
||||||
write_close<static_streambuf>(fb, cr);
|
|
||||||
boost::asio::write(stream_, fb.data(), ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rd_need_ == 0 && ! rd_fh_.fin)
|
|
||||||
{
|
|
||||||
// empty frame
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// read payload
|
|
||||||
auto smb = dynabuf.prepare(
|
|
||||||
detail::clamp(rd_need_));
|
|
||||||
auto const bytes_transferred =
|
|
||||||
stream_.read_some(smb, ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
rd_need_ -= bytes_transferred;
|
|
||||||
auto const pb = prepare_buffers(
|
|
||||||
bytes_transferred, smb);
|
|
||||||
if(rd_fh_.mask)
|
|
||||||
detail::mask_inplace(pb, rd_key_);
|
|
||||||
if(rd_opcode_ == opcode::text)
|
|
||||||
{
|
|
||||||
if(! rd_utf8_check_.write(pb) ||
|
|
||||||
(rd_need_ == 0 && rd_fh_.fin &&
|
|
||||||
! rd_utf8_check_.finish()))
|
|
||||||
{
|
|
||||||
code = close_code::bad_payload;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dynabuf.commit(bytes_transferred);
|
|
||||||
fi.op = rd_opcode_;
|
|
||||||
fi.fin = rd_fh_.fin && rd_need_ == 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(code != close_code::none)
|
|
||||||
{
|
|
||||||
// Fail the connection (per rfc6455)
|
|
||||||
if(! wr_close_)
|
|
||||||
{
|
|
||||||
wr_close_ = true;
|
|
||||||
detail::frame_streambuf fb;
|
|
||||||
write_close<static_streambuf>(fb, code);
|
|
||||||
boost::asio::write(stream_, fb.data(), ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
websocket_helpers::call_teardown(next_layer(), ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
ec = error::failed;
|
|
||||||
failed_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(! ec)
|
|
||||||
websocket_helpers::call_teardown(next_layer(), ec);
|
|
||||||
if(! ec)
|
|
||||||
ec = error::closed;
|
|
||||||
failed_ = ec != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class DynamicBuffer, class ReadHandler>
|
|
||||||
typename async_completion<
|
|
||||||
ReadHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_read_frame(frame_info& fi,
|
|
||||||
DynamicBuffer& dynabuf, ReadHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements requirements not met");
|
|
||||||
static_assert(beast::is_DynamicBuffer<DynamicBuffer>::value,
|
|
||||||
"DynamicBuffer requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
ReadHandler, void(error_code)> completion(handler);
|
|
||||||
read_frame_op<DynamicBuffer, decltype(completion.handler)>{
|
|
||||||
completion.handler, *this, fi, dynabuf};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
write(ConstBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
write(buffers, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
write(ConstBufferSequence const& buffers, error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
write_frame(true, buffers, ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence, class WriteHandler>
|
|
||||||
typename async_completion<
|
|
||||||
WriteHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_write(ConstBufferSequence const& bs, WriteHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements not met");
|
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
WriteHandler, void(error_code)> completion(handler);
|
|
||||||
write_op<ConstBufferSequence, decltype(completion.handler)>{
|
|
||||||
completion.handler, *this, bs};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
write_frame(bool fin, ConstBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
error_code ec;
|
|
||||||
write_frame(fin, buffers, ec);
|
|
||||||
if(ec)
|
|
||||||
throw system_error{ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
if(compress)
|
|
||||||
compress buffers into write_buffer
|
|
||||||
if(write_buffer_avail == write_buffer_size || fin`)
|
|
||||||
if(mask)
|
|
||||||
apply mask to write buffer
|
|
||||||
write frame header, write_buffer as one frame
|
|
||||||
else if(auto-fragment)
|
|
||||||
if(fin || write_buffer_avail + buffers size == write_buffer_size)
|
|
||||||
if(mask)
|
|
||||||
append buffers to write buffer
|
|
||||||
apply mask to write buffer
|
|
||||||
write frame header, write buffer as one frame
|
|
||||||
|
|
||||||
else:
|
|
||||||
write frame header, write buffer, and buffers as one frame
|
|
||||||
else:
|
|
||||||
append buffers to write buffer
|
|
||||||
else if(mask)
|
|
||||||
copy buffers to write_buffer
|
|
||||||
apply mask to write_buffer
|
|
||||||
write frame header and possibly full write_buffer in a single call
|
|
||||||
loop:
|
|
||||||
copy buffers to write_buffer
|
|
||||||
apply mask to write_buffer
|
|
||||||
write write_buffer in a single call
|
|
||||||
else
|
|
||||||
write frame header, buffers as one frame
|
|
||||||
*/
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
write_frame(bool fin,
|
|
||||||
ConstBufferSequence const& buffers, error_code& ec)
|
|
||||||
{
|
|
||||||
static_assert(is_SyncStream<next_layer_type>::value,
|
|
||||||
"SyncStream requirements not met");
|
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
using boost::asio::buffer;
|
|
||||||
using boost::asio::buffer_copy;
|
|
||||||
using boost::asio::buffer_size;
|
|
||||||
bool const compress = false;
|
|
||||||
if(! wr_.cont)
|
|
||||||
wr_prepare(compress);
|
|
||||||
detail::frame_header fh;
|
|
||||||
fh.op = wr_.cont ? opcode::cont : wr_opcode_;
|
|
||||||
fh.rsv1 = false;
|
|
||||||
fh.rsv2 = false;
|
|
||||||
fh.rsv3 = false;
|
|
||||||
fh.mask = role_ == detail::role_type::client;
|
|
||||||
auto remain = buffer_size(buffers);
|
|
||||||
if(compress)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
else if(wr_.autofrag)
|
|
||||||
{
|
|
||||||
consuming_buffers<ConstBufferSequence> cb(buffers);
|
|
||||||
do
|
|
||||||
{
|
|
||||||
auto const room = wr_.max - wr_.size;
|
|
||||||
if(! fin && remain < room)
|
|
||||||
{
|
|
||||||
buffer_copy(
|
|
||||||
buffer(wr_.buf.get() + wr_.size, remain), cb);
|
|
||||||
wr_.size += remain;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto const n = detail::clamp(remain, room);
|
|
||||||
buffer_copy(
|
|
||||||
buffer(wr_.buf.get() + wr_.size, n), cb);
|
|
||||||
auto const mb = buffer(wr_.buf.get(), wr_.size + n);
|
|
||||||
if(fh.mask)
|
|
||||||
{
|
|
||||||
fh.key = maskgen_();
|
|
||||||
detail::prepared_key_type key;
|
|
||||||
detail::prepare_key(key, fh.key);
|
|
||||||
detail::mask_inplace(mb, key);
|
|
||||||
}
|
|
||||||
fh.fin = fin && n == remain;
|
|
||||||
fh.len = buffer_size(mb);
|
|
||||||
detail::fh_streambuf fh_buf;
|
|
||||||
detail::write<static_streambuf>(fh_buf, fh);
|
|
||||||
// send header and payload
|
|
||||||
boost::asio::write(stream_,
|
|
||||||
buffer_cat(fh_buf.data(), mb), ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
remain -= n;
|
|
||||||
cb.consume(n);
|
|
||||||
wr_.size = 0;
|
|
||||||
fh.op = opcode::cont;
|
|
||||||
}
|
|
||||||
while(remain > 0);
|
|
||||||
wr_.cont = ! fh.fin;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(fh.mask)
|
|
||||||
{
|
|
||||||
consuming_buffers<ConstBufferSequence> cb(buffers);
|
|
||||||
fh.fin = fin;
|
|
||||||
fh.len = remain;
|
|
||||||
fh.key = maskgen_();
|
|
||||||
wr_.cont = ! fh.fin;
|
|
||||||
detail::fh_streambuf fh_buf;
|
|
||||||
detail::write<static_streambuf>(fh_buf, fh);
|
|
||||||
detail::prepared_key_type key;
|
|
||||||
detail::prepare_key(key, fh.key);
|
|
||||||
{
|
|
||||||
auto const n = detail::clamp(remain, wr_.max);
|
|
||||||
auto const mb = buffer(wr_.buf.get(), n);
|
|
||||||
buffer_copy(mb, cb);
|
|
||||||
cb.consume(n);
|
|
||||||
remain -= n;
|
|
||||||
detail::mask_inplace(mb, key);
|
|
||||||
// send header and payload
|
|
||||||
boost::asio::write(stream_,
|
|
||||||
buffer_cat(fh_buf.data(), mb), ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while(remain > 0)
|
|
||||||
{
|
|
||||||
auto const n = detail::clamp(remain, wr_.max);
|
|
||||||
auto const mb = buffer(wr_.buf.get(), n);
|
|
||||||
buffer_copy(mb, cb);
|
|
||||||
cb.consume(n);
|
|
||||||
remain -= n;
|
|
||||||
detail::mask_inplace(mb, key);
|
|
||||||
// send payload
|
|
||||||
boost::asio::write(stream_, mb, ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
if(failed_)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// send header and payload
|
|
||||||
fh.fin = fin;
|
|
||||||
fh.len = remain;
|
|
||||||
wr_.cont = ! fh.fin;
|
|
||||||
detail::fh_streambuf fh_buf;
|
|
||||||
detail::write<static_streambuf>(fh_buf, fh);
|
|
||||||
boost::asio::write(stream_,
|
|
||||||
buffer_cat(fh_buf.data(), buffers), ec);
|
|
||||||
failed_ = ec != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class ConstBufferSequence, class WriteHandler>
|
|
||||||
typename async_completion<
|
|
||||||
WriteHandler, void(error_code)>::result_type
|
|
||||||
stream<NextLayer>::
|
|
||||||
async_write_frame(bool fin,
|
|
||||||
ConstBufferSequence const& bs, WriteHandler&& handler)
|
|
||||||
{
|
|
||||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
|
||||||
"AsyncStream requirements not met");
|
|
||||||
static_assert(beast::is_ConstBufferSequence<
|
|
||||||
ConstBufferSequence>::value,
|
|
||||||
"ConstBufferSequence requirements not met");
|
|
||||||
beast::async_completion<
|
|
||||||
WriteHandler, void(error_code)
|
|
||||||
> completion(handler);
|
|
||||||
write_frame_op<ConstBufferSequence, decltype(
|
|
||||||
completion.handler)>{completion.handler,
|
|
||||||
*this, fin, bs};
|
|
||||||
return completion.result.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class NextLayer>
|
template<class NextLayer>
|
||||||
|
682
include/beast/websocket/impl/write.ipp
Normal file
682
include/beast/websocket/impl/write.ipp
Normal file
@@ -0,0 +1,682 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_WEBSOCKET_IMPL_WRITE_IPP
|
||||||
|
#define BEAST_WEBSOCKET_IMPL_WRITE_IPP
|
||||||
|
|
||||||
|
#include <beast/core/bind_handler.hpp>
|
||||||
|
#include <beast/core/buffer_cat.hpp>
|
||||||
|
#include <beast/core/buffer_concepts.hpp>
|
||||||
|
#include <beast/core/consuming_buffers.hpp>
|
||||||
|
#include <beast/core/handler_alloc.hpp>
|
||||||
|
#include <beast/core/prepare_buffers.hpp>
|
||||||
|
#include <beast/core/static_streambuf.hpp>
|
||||||
|
#include <beast/core/stream_concepts.hpp>
|
||||||
|
#include <beast/websocket/detail/frame.hpp>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace websocket {
|
||||||
|
|
||||||
|
/*
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
write_frame(bool fin, ConstBufferSequence const& buffer)
|
||||||
|
|
||||||
|
Depending on the settings of autofragment role, and compression,
|
||||||
|
different algorithms are used.
|
||||||
|
|
||||||
|
1. autofragment: false
|
||||||
|
compression: false
|
||||||
|
|
||||||
|
In the server role, this will send a single frame in one
|
||||||
|
system call, by concatenating the frame header and the payload.
|
||||||
|
|
||||||
|
In the client role, this will send a single frame in one system
|
||||||
|
calls, using the write buffer to calculate masked data.
|
||||||
|
|
||||||
|
2. autofragment: true
|
||||||
|
compression: false
|
||||||
|
|
||||||
|
In the server role, this will send one or more frames in one
|
||||||
|
system call per sent frame. Each frame is sent by concatenating
|
||||||
|
the frame header and payload. The size of each sent frame will
|
||||||
|
not exceed the write buffer size option.
|
||||||
|
|
||||||
|
In the client role, this will send one or more frames in one
|
||||||
|
system call per sent frame, using the write buffer to calculate
|
||||||
|
masked data. The size of each sent frame will not exceed the
|
||||||
|
write buffer size option.
|
||||||
|
|
||||||
|
3. autofragment: false
|
||||||
|
compression: true
|
||||||
|
|
||||||
|
In the server role, this will...
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
if(compress)
|
||||||
|
compress buffers into write_buffer
|
||||||
|
if(write_buffer_avail == write_buffer_size || fin`)
|
||||||
|
if(mask)
|
||||||
|
apply mask to write buffer
|
||||||
|
write frame header, write_buffer as one frame
|
||||||
|
else if(auto-fragment)
|
||||||
|
if(fin || write_buffer_avail + buffers size == write_buffer_size)
|
||||||
|
if(mask)
|
||||||
|
append buffers to write buffer
|
||||||
|
apply mask to write buffer
|
||||||
|
write frame header, write buffer as one frame
|
||||||
|
|
||||||
|
else:
|
||||||
|
write frame header, write buffer, and buffers as one frame
|
||||||
|
else:
|
||||||
|
append buffers to write buffer
|
||||||
|
else if(mask)
|
||||||
|
copy buffers to write_buffer
|
||||||
|
apply mask to write_buffer
|
||||||
|
write frame header and possibly full write_buffer in a single call
|
||||||
|
loop:
|
||||||
|
copy buffers to write_buffer
|
||||||
|
apply mask to write_buffer
|
||||||
|
write write_buffer in a single call
|
||||||
|
else
|
||||||
|
write frame header, buffers as one frame
|
||||||
|
*/
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
class stream<NextLayer>::write_frame_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data : op
|
||||||
|
{
|
||||||
|
stream<NextLayer>& ws;
|
||||||
|
consuming_buffers<Buffers> cb;
|
||||||
|
Handler h;
|
||||||
|
detail::frame_header fh;
|
||||||
|
detail::fh_streambuf fh_buf;
|
||||||
|
detail::prepared_key_type key;
|
||||||
|
void* tmp;
|
||||||
|
std::size_t tmp_size;
|
||||||
|
std::uint64_t remain;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||||
|
bool fin, Buffers const& bs)
|
||||||
|
: ws(ws_)
|
||||||
|
, cb(bs)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
fh.op = ws.wr_.cont ?
|
||||||
|
opcode::cont : ws.wr_opcode_;
|
||||||
|
ws.wr_.cont = ! fin;
|
||||||
|
fh.fin = fin;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.len = boost::asio::buffer_size(cb);
|
||||||
|
fh.mask = ws.role_ == detail::role_type::client;
|
||||||
|
if(fh.mask)
|
||||||
|
{
|
||||||
|
fh.key = ws.maskgen_();
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
tmp_size = detail::clamp(
|
||||||
|
fh.len, ws.wr_buf_size_);
|
||||||
|
tmp = boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(tmp_size, h);
|
||||||
|
remain = fh.len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmp = nullptr;
|
||||||
|
}
|
||||||
|
detail::write<static_streambuf>(fh_buf, fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
~data()
|
||||||
|
{
|
||||||
|
if(tmp)
|
||||||
|
boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(tmp, tmp_size, h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
write_frame_op(write_frame_op&&) = default;
|
||||||
|
write_frame_op(write_frame_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
write_frame_op(DeducedHandler&& h,
|
||||||
|
stream<NextLayer>& ws, Args&&... args)
|
||||||
|
: d_(std::make_shared<data>(
|
||||||
|
std::forward<DeducedHandler>(h), ws,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()()
|
||||||
|
{
|
||||||
|
(*this)(error_code{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code ec, std::size_t);
|
||||||
|
|
||||||
|
void operator()(error_code ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
void* asio_handler_allocate(
|
||||||
|
std::size_t size, write_frame_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, write_frame_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(write_frame_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, write_frame_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
write_frame_op<Buffers, Handler>::
|
||||||
|
operator()(error_code ec, std::size_t)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
if(ec)
|
||||||
|
d.ws.failed_ = true;
|
||||||
|
(*this)(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
write_frame_op<Buffers, Handler>::
|
||||||
|
operator()(error_code ec, bool again)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::mutable_buffers_1;
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
if(ec)
|
||||||
|
goto upcall;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if(d.ws.wr_block_)
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.state = 3;
|
||||||
|
d.ws.wr_op_.template emplace<
|
||||||
|
write_frame_op>(std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(d.ws.failed_ || d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.ws.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this),
|
||||||
|
boost::asio::error::operation_aborted));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
if(! d.fh.mask)
|
||||||
|
{
|
||||||
|
// send header and entire payload
|
||||||
|
d.state = 99;
|
||||||
|
BOOST_ASSERT(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
buffer_cat(d.fh_buf.data(), d.cb),
|
||||||
|
std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto const n =
|
||||||
|
detail::clamp(d.remain, d.tmp_size);
|
||||||
|
mutable_buffers_1 mb{d.tmp, n};
|
||||||
|
buffer_copy(mb, d.cb);
|
||||||
|
d.cb.consume(n);
|
||||||
|
d.remain -= n;
|
||||||
|
detail::mask_inplace(mb, d.key);
|
||||||
|
// send header and payload
|
||||||
|
d.state = d.remain > 0 ? 2 : 99;
|
||||||
|
BOOST_ASSERT(! d.ws.wr_block_);
|
||||||
|
d.ws.wr_block_ = &d;
|
||||||
|
boost::asio::async_write(d.ws.stream_,
|
||||||
|
buffer_cat(d.fh_buf.data(),
|
||||||
|
mb), std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent masked payload
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
auto const n =
|
||||||
|
detail::clamp(d.remain, d.tmp_size);
|
||||||
|
mutable_buffers_1 mb{d.tmp,
|
||||||
|
static_cast<std::size_t>(n)};
|
||||||
|
buffer_copy(mb, d.cb);
|
||||||
|
d.cb.consume(n);
|
||||||
|
d.remain -= n;
|
||||||
|
detail::mask_inplace(mb, d.key);
|
||||||
|
// send payload
|
||||||
|
if(d.remain == 0)
|
||||||
|
d.state = 99;
|
||||||
|
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||||
|
boost::asio::async_write(
|
||||||
|
d.ws.stream_, mb, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
d.state = 4;
|
||||||
|
d.ws.get_io_service().post(bind_handler(
|
||||||
|
std::move(*this), ec));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
if(d.ws.failed_ || d.ws.wr_close_)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
ec = boost::asio::error::operation_aborted;
|
||||||
|
goto upcall;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 99:
|
||||||
|
goto upcall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upcall:
|
||||||
|
if(d.tmp)
|
||||||
|
{
|
||||||
|
boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(d.tmp, d.tmp_size, d.h);
|
||||||
|
d.tmp = nullptr;
|
||||||
|
}
|
||||||
|
if(d.ws.wr_block_ == &d)
|
||||||
|
d.ws.wr_block_ = nullptr;
|
||||||
|
d.ws.rd_op_.maybe_invoke();
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence, class WriteHandler>
|
||||||
|
typename async_completion<
|
||||||
|
WriteHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_write_frame(bool fin,
|
||||||
|
ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements not met");
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
WriteHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
write_frame_op<ConstBufferSequence, decltype(
|
||||||
|
completion.handler)>{completion.handler,
|
||||||
|
*this, fin, bs};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
write_frame(bool fin, ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
write_frame(fin, buffers, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
write_frame(bool fin,
|
||||||
|
ConstBufferSequence const& buffers, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
bool const compress = false;
|
||||||
|
if(! wr_.cont)
|
||||||
|
wr_prepare(compress);
|
||||||
|
detail::frame_header fh;
|
||||||
|
fh.op = wr_.cont ? opcode::cont : wr_opcode_;
|
||||||
|
fh.rsv1 = false;
|
||||||
|
fh.rsv2 = false;
|
||||||
|
fh.rsv3 = false;
|
||||||
|
fh.mask = role_ == detail::role_type::client;
|
||||||
|
auto remain = buffer_size(buffers);
|
||||||
|
if(compress)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
else if(wr_.autofrag)
|
||||||
|
{
|
||||||
|
consuming_buffers<ConstBufferSequence> cb(buffers);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
auto const room = wr_.max - wr_.size;
|
||||||
|
if(! fin && remain < room)
|
||||||
|
{
|
||||||
|
buffer_copy(
|
||||||
|
buffer(wr_.buf.get() + wr_.size, remain), cb);
|
||||||
|
wr_.size += remain;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto const n = detail::clamp(remain, room);
|
||||||
|
buffer_copy(
|
||||||
|
buffer(wr_.buf.get() + wr_.size, n), cb);
|
||||||
|
auto const mb = buffer(wr_.buf.get(), wr_.size + n);
|
||||||
|
if(fh.mask)
|
||||||
|
{
|
||||||
|
fh.key = maskgen_();
|
||||||
|
detail::prepared_key_type key;
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
detail::mask_inplace(mb, key);
|
||||||
|
}
|
||||||
|
fh.fin = fin && n == remain;
|
||||||
|
fh.len = buffer_size(mb);
|
||||||
|
detail::fh_streambuf fh_buf;
|
||||||
|
detail::write<static_streambuf>(fh_buf, fh);
|
||||||
|
// send header and payload
|
||||||
|
boost::asio::write(stream_,
|
||||||
|
buffer_cat(fh_buf.data(), mb), ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
remain -= n;
|
||||||
|
cb.consume(n);
|
||||||
|
wr_.size = 0;
|
||||||
|
fh.op = opcode::cont;
|
||||||
|
}
|
||||||
|
while(remain > 0);
|
||||||
|
wr_.cont = ! fh.fin;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(fh.mask)
|
||||||
|
{
|
||||||
|
consuming_buffers<ConstBufferSequence> cb(buffers);
|
||||||
|
fh.fin = fin;
|
||||||
|
fh.len = remain;
|
||||||
|
fh.key = maskgen_();
|
||||||
|
wr_.cont = ! fh.fin;
|
||||||
|
detail::fh_streambuf fh_buf;
|
||||||
|
detail::write<static_streambuf>(fh_buf, fh);
|
||||||
|
detail::prepared_key_type key;
|
||||||
|
detail::prepare_key(key, fh.key);
|
||||||
|
{
|
||||||
|
auto const n = detail::clamp(remain, wr_.max);
|
||||||
|
auto const mb = buffer(wr_.buf.get(), n);
|
||||||
|
buffer_copy(mb, cb);
|
||||||
|
cb.consume(n);
|
||||||
|
remain -= n;
|
||||||
|
detail::mask_inplace(mb, key);
|
||||||
|
// send header and payload
|
||||||
|
boost::asio::write(stream_,
|
||||||
|
buffer_cat(fh_buf.data(), mb), ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while(remain > 0)
|
||||||
|
{
|
||||||
|
auto const n = detail::clamp(remain, wr_.max);
|
||||||
|
auto const mb = buffer(wr_.buf.get(), n);
|
||||||
|
buffer_copy(mb, cb);
|
||||||
|
cb.consume(n);
|
||||||
|
remain -= n;
|
||||||
|
detail::mask_inplace(mb, key);
|
||||||
|
// send payload
|
||||||
|
boost::asio::write(stream_, mb, ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
if(failed_)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// send header and payload
|
||||||
|
fh.fin = fin;
|
||||||
|
fh.len = remain;
|
||||||
|
wr_.cont = ! fh.fin;
|
||||||
|
detail::fh_streambuf fh_buf;
|
||||||
|
detail::write<static_streambuf>(fh_buf, fh);
|
||||||
|
boost::asio::write(stream_,
|
||||||
|
buffer_cat(fh_buf.data(), buffers), ec);
|
||||||
|
failed_ = ec != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
class stream<NextLayer>::write_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data : op
|
||||||
|
{
|
||||||
|
stream<NextLayer>& ws;
|
||||||
|
consuming_buffers<Buffers> cb;
|
||||||
|
Handler h;
|
||||||
|
std::size_t remain;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_,
|
||||||
|
stream<NextLayer>& ws_, Buffers const& bs)
|
||||||
|
: ws(ws_)
|
||||||
|
, cb(bs)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, remain(boost::asio::buffer_size(cb))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> 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::allocate_shared<data>(alloc_type{h},
|
||||||
|
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, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
void asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
bool asio_handler_is_continuation(write_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Function>
|
||||||
|
friend
|
||||||
|
void asio_handler_invoke(Function&& f, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class Buffers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
write_op<Buffers, Handler>::
|
||||||
|
operator()(error_code ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
if(! ec)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
auto const n = d.remain;
|
||||||
|
d.remain -= n;
|
||||||
|
auto const fin = d.remain <= 0;
|
||||||
|
if(fin)
|
||||||
|
d.state = 99;
|
||||||
|
auto const pb = prepare_buffers(n, d.cb);
|
||||||
|
d.cb.consume(n);
|
||||||
|
d.ws.async_write_frame(fin, pb, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 99:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence, class WriteHandler>
|
||||||
|
typename async_completion<
|
||||||
|
WriteHandler, void(error_code)>::result_type
|
||||||
|
stream<NextLayer>::
|
||||||
|
async_write(ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||||
|
"AsyncStream requirements not met");
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
beast::async_completion<
|
||||||
|
WriteHandler, void(error_code)> completion(handler);
|
||||||
|
write_op<ConstBufferSequence, decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, bs};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
write(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
write(buffers, ec);
|
||||||
|
if(ec)
|
||||||
|
throw system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer>
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
stream<NextLayer>::
|
||||||
|
write(ConstBufferSequence const& buffers, error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncStream<next_layer_type>::value,
|
||||||
|
"SyncStream requirements not met");
|
||||||
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
write_frame(true, buffers, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
} // websocket
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
@@ -1,282 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP
|
|
||||||
#define BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP
|
|
||||||
|
|
||||||
#include <beast/core/buffer_cat.hpp>
|
|
||||||
#include <beast/core/bind_handler.hpp>
|
|
||||||
#include <beast/core/consuming_buffers.hpp>
|
|
||||||
#include <beast/core/handler_alloc.hpp>
|
|
||||||
#include <beast/core/static_streambuf.hpp>
|
|
||||||
#include <beast/websocket/detail/frame.hpp>
|
|
||||||
#include <boost/assert.hpp>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace websocket {
|
|
||||||
|
|
||||||
// write a frame
|
|
||||||
//
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Buffers, class Handler>
|
|
||||||
class stream<NextLayer>::write_frame_op
|
|
||||||
{
|
|
||||||
using alloc_type =
|
|
||||||
handler_alloc<char, Handler>;
|
|
||||||
|
|
||||||
struct data : op
|
|
||||||
{
|
|
||||||
stream<NextLayer>& ws;
|
|
||||||
consuming_buffers<Buffers> cb;
|
|
||||||
Handler h;
|
|
||||||
detail::frame_header fh;
|
|
||||||
detail::fh_streambuf fh_buf;
|
|
||||||
detail::prepared_key_type key;
|
|
||||||
void* tmp;
|
|
||||||
std::size_t tmp_size;
|
|
||||||
std::uint64_t remain;
|
|
||||||
bool cont;
|
|
||||||
int state = 0;
|
|
||||||
|
|
||||||
template<class DeducedHandler>
|
|
||||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
|
||||||
bool fin, Buffers const& bs)
|
|
||||||
: ws(ws_)
|
|
||||||
, cb(bs)
|
|
||||||
, h(std::forward<DeducedHandler>(h_))
|
|
||||||
, cont(boost_asio_handler_cont_helpers::
|
|
||||||
is_continuation(h))
|
|
||||||
{
|
|
||||||
fh.op = ws.wr_.cont ?
|
|
||||||
opcode::cont : ws.wr_opcode_;
|
|
||||||
ws.wr_.cont = ! fin;
|
|
||||||
fh.fin = fin;
|
|
||||||
fh.rsv1 = false;
|
|
||||||
fh.rsv2 = false;
|
|
||||||
fh.rsv3 = false;
|
|
||||||
fh.len = boost::asio::buffer_size(cb);
|
|
||||||
fh.mask = ws.role_ == detail::role_type::client;
|
|
||||||
if(fh.mask)
|
|
||||||
{
|
|
||||||
fh.key = ws.maskgen_();
|
|
||||||
detail::prepare_key(key, fh.key);
|
|
||||||
tmp_size = detail::clamp(
|
|
||||||
fh.len, ws.wr_buf_size_);
|
|
||||||
tmp = boost_asio_handler_alloc_helpers::
|
|
||||||
allocate(tmp_size, h);
|
|
||||||
remain = fh.len;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tmp = nullptr;
|
|
||||||
}
|
|
||||||
detail::write<static_streambuf>(fh_buf, fh);
|
|
||||||
}
|
|
||||||
|
|
||||||
~data()
|
|
||||||
{
|
|
||||||
if(tmp)
|
|
||||||
boost_asio_handler_alloc_helpers::
|
|
||||||
deallocate(tmp, tmp_size, h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<data> d_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
write_frame_op(write_frame_op&&) = default;
|
|
||||||
write_frame_op(write_frame_op const&) = default;
|
|
||||||
|
|
||||||
template<class DeducedHandler, class... Args>
|
|
||||||
write_frame_op(DeducedHandler&& h,
|
|
||||||
stream<NextLayer>& ws, Args&&... args)
|
|
||||||
: d_(std::make_shared<data>(
|
|
||||||
std::forward<DeducedHandler>(h), ws,
|
|
||||||
std::forward<Args>(args)...))
|
|
||||||
{
|
|
||||||
(*this)(error_code{}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()()
|
|
||||||
{
|
|
||||||
(*this)(error_code{});
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(error_code ec, std::size_t);
|
|
||||||
|
|
||||||
void operator()(error_code ec, bool again = true);
|
|
||||||
|
|
||||||
friend
|
|
||||||
void* asio_handler_allocate(
|
|
||||||
std::size_t size, write_frame_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
allocate(size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void asio_handler_deallocate(
|
|
||||||
void* p, std::size_t size, write_frame_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
deallocate(p, size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
bool asio_handler_is_continuation(write_frame_op* op)
|
|
||||||
{
|
|
||||||
return op->d_->cont;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Function>
|
|
||||||
friend
|
|
||||||
void asio_handler_invoke(Function&& f, write_frame_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_invoke_helpers::
|
|
||||||
invoke(f, op->d_->h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Buffers, class Handler>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
write_frame_op<Buffers, Handler>::
|
|
||||||
operator()(error_code ec, std::size_t)
|
|
||||||
{
|
|
||||||
auto& d = *d_;
|
|
||||||
if(ec)
|
|
||||||
d.ws.failed_ = true;
|
|
||||||
(*this)(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Buffers, class Handler>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
write_frame_op<Buffers, Handler>::
|
|
||||||
operator()(error_code ec, bool again)
|
|
||||||
{
|
|
||||||
using boost::asio::buffer_copy;
|
|
||||||
using boost::asio::mutable_buffers_1;
|
|
||||||
auto& d = *d_;
|
|
||||||
d.cont = d.cont || again;
|
|
||||||
if(ec)
|
|
||||||
goto upcall;
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
switch(d.state)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
if(d.ws.wr_block_)
|
|
||||||
{
|
|
||||||
// suspend
|
|
||||||
d.state = 3;
|
|
||||||
d.ws.wr_op_.template emplace<
|
|
||||||
write_frame_op>(std::move(*this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(d.ws.failed_ || d.ws.wr_close_)
|
|
||||||
{
|
|
||||||
// call handler
|
|
||||||
d.state = 99;
|
|
||||||
d.ws.get_io_service().post(
|
|
||||||
bind_handler(std::move(*this),
|
|
||||||
boost::asio::error::operation_aborted));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// fall through
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
if(! d.fh.mask)
|
|
||||||
{
|
|
||||||
// send header and entire payload
|
|
||||||
d.state = 99;
|
|
||||||
BOOST_ASSERT(! d.ws.wr_block_);
|
|
||||||
d.ws.wr_block_ = &d;
|
|
||||||
boost::asio::async_write(d.ws.stream_,
|
|
||||||
buffer_cat(d.fh_buf.data(), d.cb),
|
|
||||||
std::move(*this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto const n =
|
|
||||||
detail::clamp(d.remain, d.tmp_size);
|
|
||||||
mutable_buffers_1 mb{d.tmp, n};
|
|
||||||
buffer_copy(mb, d.cb);
|
|
||||||
d.cb.consume(n);
|
|
||||||
d.remain -= n;
|
|
||||||
detail::mask_inplace(mb, d.key);
|
|
||||||
// send header and payload
|
|
||||||
d.state = d.remain > 0 ? 2 : 99;
|
|
||||||
BOOST_ASSERT(! d.ws.wr_block_);
|
|
||||||
d.ws.wr_block_ = &d;
|
|
||||||
boost::asio::async_write(d.ws.stream_,
|
|
||||||
buffer_cat(d.fh_buf.data(),
|
|
||||||
mb), std::move(*this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sent masked payload
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
auto const n =
|
|
||||||
detail::clamp(d.remain, d.tmp_size);
|
|
||||||
mutable_buffers_1 mb{d.tmp,
|
|
||||||
static_cast<std::size_t>(n)};
|
|
||||||
buffer_copy(mb, d.cb);
|
|
||||||
d.cb.consume(n);
|
|
||||||
d.remain -= n;
|
|
||||||
detail::mask_inplace(mb, d.key);
|
|
||||||
// send payload
|
|
||||||
if(d.remain == 0)
|
|
||||||
d.state = 99;
|
|
||||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
|
||||||
boost::asio::async_write(
|
|
||||||
d.ws.stream_, mb, std::move(*this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
d.state = 4;
|
|
||||||
d.ws.get_io_service().post(bind_handler(
|
|
||||||
std::move(*this), ec));
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
if(d.ws.failed_ || d.ws.wr_close_)
|
|
||||||
{
|
|
||||||
// call handler
|
|
||||||
ec = boost::asio::error::operation_aborted;
|
|
||||||
goto upcall;
|
|
||||||
}
|
|
||||||
d.state = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 99:
|
|
||||||
goto upcall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
upcall:
|
|
||||||
if(d.tmp)
|
|
||||||
{
|
|
||||||
boost_asio_handler_alloc_helpers::
|
|
||||||
deallocate(d.tmp, d.tmp_size, d.h);
|
|
||||||
d.tmp = nullptr;
|
|
||||||
}
|
|
||||||
if(d.ws.wr_block_ == &d)
|
|
||||||
d.ws.wr_block_ = nullptr;
|
|
||||||
d.ws.rd_op_.maybe_invoke();
|
|
||||||
d.h(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // websocket
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,139 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
||||||
//
|
|
||||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP
|
|
||||||
#define BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP
|
|
||||||
|
|
||||||
#include <beast/core/consuming_buffers.hpp>
|
|
||||||
#include <beast/core/prepare_buffers.hpp>
|
|
||||||
#include <beast/core/handler_alloc.hpp>
|
|
||||||
#include <beast/websocket/detail/frame.hpp>
|
|
||||||
#include <boost/assert.hpp>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace websocket {
|
|
||||||
|
|
||||||
// write a message
|
|
||||||
//
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Buffers, class Handler>
|
|
||||||
class stream<NextLayer>::write_op
|
|
||||||
{
|
|
||||||
using alloc_type =
|
|
||||||
handler_alloc<char, Handler>;
|
|
||||||
|
|
||||||
struct data : op
|
|
||||||
{
|
|
||||||
stream<NextLayer>& ws;
|
|
||||||
consuming_buffers<Buffers> cb;
|
|
||||||
Handler h;
|
|
||||||
std::size_t remain;
|
|
||||||
bool cont;
|
|
||||||
int state = 0;
|
|
||||||
|
|
||||||
template<class DeducedHandler>
|
|
||||||
data(DeducedHandler&& h_,
|
|
||||||
stream<NextLayer>& ws_, Buffers const& bs)
|
|
||||||
: ws(ws_)
|
|
||||||
, cb(bs)
|
|
||||||
, h(std::forward<DeducedHandler>(h_))
|
|
||||||
, remain(boost::asio::buffer_size(cb))
|
|
||||||
, cont(boost_asio_handler_cont_helpers::
|
|
||||||
is_continuation(h))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<data> 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::allocate_shared<data>(alloc_type{h},
|
|
||||||
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, write_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
allocate(size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
void asio_handler_deallocate(
|
|
||||||
void* p, std::size_t size, write_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_alloc_helpers::
|
|
||||||
deallocate(p, size, op->d_->h);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend
|
|
||||||
bool asio_handler_is_continuation(write_op* op)
|
|
||||||
{
|
|
||||||
return op->d_->cont;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Function>
|
|
||||||
friend
|
|
||||||
void asio_handler_invoke(Function&& f, write_op* op)
|
|
||||||
{
|
|
||||||
return boost_asio_handler_invoke_helpers::
|
|
||||||
invoke(f, op->d_->h);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class NextLayer>
|
|
||||||
template<class Buffers, class Handler>
|
|
||||||
void
|
|
||||||
stream<NextLayer>::
|
|
||||||
write_op<Buffers, Handler>::
|
|
||||||
operator()(error_code ec, bool again)
|
|
||||||
{
|
|
||||||
auto& d = *d_;
|
|
||||||
d.cont = d.cont || again;
|
|
||||||
if(! ec)
|
|
||||||
{
|
|
||||||
switch(d.state)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
auto const n = d.remain;
|
|
||||||
d.remain -= n;
|
|
||||||
auto const fin = d.remain <= 0;
|
|
||||||
if(fin)
|
|
||||||
d.state = 99;
|
|
||||||
auto const pb = prepare_buffers(n, d.cb);
|
|
||||||
d.cb.consume(n);
|
|
||||||
d.ws.async_write_frame(fin, pb, std::move(*this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 99:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.h(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // websocket
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1552,6 +1552,12 @@ private:
|
|||||||
} // websocket
|
} // websocket
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/websocket/impl/accept.ipp>
|
||||||
|
#include <beast/websocket/impl/close.ipp>
|
||||||
|
#include <beast/websocket/impl/handshake.ipp>
|
||||||
|
#include <beast/websocket/impl/ping.ipp>
|
||||||
|
#include <beast/websocket/impl/read.ipp>
|
||||||
#include <beast/websocket/impl/stream.ipp>
|
#include <beast/websocket/impl/stream.ipp>
|
||||||
|
#include <beast/websocket/impl/write.ipp>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user