Files
boost_beast/include/boost/beast/websocket/impl/handshake.hpp
T
Mohammad Nejati 5f5507f647 Initiating functions no longer perform mutating operations
Using `asio::deferred` as a completion token defers the initiation of
operations and creates a lazy operation which might even be discarded.
We need to move mutating operations to the initiator function objects or
the constructor of operation objects, which are guaranteed to run when
the deferred operation is launched.
2024-08-06 15:42:40 +03:30

418 lines
12 KiB
C++

//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
#include <boost/beast/websocket/impl/stream_impl.hpp>
#include <boost/beast/websocket/detail/type_traits.hpp>
#include <boost/beast/http/empty_body.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/http/write.hpp>
#include <boost/beast/core/async_base.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <memory>
namespace boost {
namespace beast {
namespace websocket {
//------------------------------------------------------------------------------
// send the upgrade request and process the response
//
template<class NextLayer, bool deflateSupported>
template<class Handler>
class stream<NextLayer, deflateSupported>::handshake_op
: public beast::stable_async_base<Handler,
beast::executor_type<stream>>
, public asio::coroutine
{
struct data
{
// VFALCO This really should be two separate
// composed operations, to save on memory
request_type req;
http::response_parser<
typename response_type::body_type> p;
flat_buffer fb;
bool overflow = false; // could be a member of the op
explicit
data(request_type&& req_)
: req(std::move(req_))
{
}
};
boost::weak_ptr<impl_type> wp_;
detail::sec_ws_key_type key_;
response_type* res_p_;
data& d_;
public:
template<class Handler_>
handshake_op(
Handler_&& h,
boost::shared_ptr<impl_type> const& sp,
request_type&& req,
detail::sec_ws_key_type key,
response_type* res_p)
: stable_async_base<Handler,
beast::executor_type<stream>>(
std::forward<Handler_>(h),
sp->stream().get_executor())
, wp_(sp)
, key_(key)
, res_p_(res_p)
, d_(beast::allocate_stable<data>(
*this, std::move(req)))
{
sp->reset(); // VFALCO I don't like this
if(res_p)
res_p->result(http::status::internal_server_error);
(*this)({}, 0, false);
}
void
operator()(
error_code ec = {},
std::size_t bytes_used = 0,
bool cont = true)
{
boost::ignore_unused(bytes_used);
auto sp = wp_.lock();
if(! sp)
{
BOOST_BEAST_ASSIGN_EC(ec, net::error::operation_aborted);
return this->complete(cont, ec);
}
auto& impl = *sp;
BOOST_ASIO_CORO_REENTER(*this)
{
impl.change_status(status::handshake);
impl.update_timer(this->get_executor());
// write HTTP request
impl.do_pmd_config(d_.req);
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_handshake"));
http::async_write(impl.stream(),
d_.req, std::move(*this));
}
if(impl.check_stop_now(ec))
goto upcall;
// read HTTP response
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_handshake"));
http::async_read(impl.stream(),
impl.rd_buf, d_.p,
std::move(*this));
}
if(ec == http::error::buffer_overflow)
{
// If the response overflows the internal
// read buffer, switch to a dynamically
// allocated flat buffer.
d_.fb.commit(net::buffer_copy(
d_.fb.prepare(impl.rd_buf.size()),
impl.rd_buf.data()));
impl.rd_buf.clear();
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"websocket::async_handshake"));
http::async_read(impl.stream(),
d_.fb, d_.p, std::move(*this));
}
if(! ec)
{
// Copy any leftovers back into the read
// buffer, since this represents websocket
// frame data.
if(d_.fb.size() <= impl.rd_buf.capacity())
{
impl.rd_buf.commit(net::buffer_copy(
impl.rd_buf.prepare(d_.fb.size()),
d_.fb.data()));
}
else
{
BOOST_BEAST_ASSIGN_EC(ec, http::error::buffer_overflow);
}
}
// Do this before the upcall
d_.fb.clear();
}
if(impl.check_stop_now(ec))
goto upcall;
// success
impl.reset_idle();
impl.on_response(d_.p.get(), key_, ec);
if(res_p_)
swap(d_.p.get(), *res_p_);
upcall:
this->complete(cont ,ec);
}
}
};
template<class NextLayer, bool deflateSupported>
struct stream<NextLayer, deflateSupported>::
run_handshake_op
{
boost::shared_ptr<impl_type> const& self;
using executor_type = typename stream::executor_type;
executor_type
get_executor() const noexcept
{
return self->stream().get_executor();
}
template<class HandshakeHandler>
void operator()(
HandshakeHandler&& h,
request_type&& req,
detail::sec_ws_key_type key,
response_type* res_p)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
beast::detail::is_invocable<HandshakeHandler,
void(error_code)>::value,
"HandshakeHandler type requirements not met");
handshake_op<
typename std::decay<HandshakeHandler>::type>(
std::forward<HandshakeHandler>(h),
self, std::move(req), key, res_p);
}
};
//------------------------------------------------------------------------------
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer, deflateSupported>::
do_handshake(
response_type* res_p,
string_view host,
string_view target,
RequestDecorator const& decorator,
error_code& ec)
{
if(res_p)
res_p->result(http::status::internal_server_error);
auto& impl = *impl_;
impl.change_status(status::handshake);
impl.reset();
detail::sec_ws_key_type key;
{
auto const req = impl.build_request(
key, host, target, decorator);
impl.do_pmd_config(req);
http::write(impl.stream(), req, ec);
}
if(impl.check_stop_now(ec))
return;
http::response_parser<
typename response_type::body_type> p;
http::read(next_layer(), impl.rd_buf, p, ec);
if(ec == http::error::buffer_overflow)
{
// If the response overflows the internal
// read buffer, switch to a dynamically
// allocated flat buffer.
flat_buffer fb;
fb.commit(net::buffer_copy(
fb.prepare(impl.rd_buf.size()),
impl.rd_buf.data()));
impl.rd_buf.clear();
http::read(next_layer(), fb, p, ec);;
if(! ec)
{
// Copy any leftovers back into the read
// buffer, since this represents websocket
// frame data.
if(fb.size() <= impl.rd_buf.capacity())
{
impl.rd_buf.commit(net::buffer_copy(
impl.rd_buf.prepare(fb.size()),
fb.data()));
}
else
{
BOOST_BEAST_ASSIGN_EC(ec, http::error::buffer_overflow);
}
}
}
if(impl.check_stop_now(ec))
return;
if (res_p)
{
// If res_p is not null, move parser's response into it.
*res_p = p.release();
}
else
{
// Otherwise point res_p at the response in the parser.
res_p = &p.get();
}
impl.on_response(*res_p, key, ec);
}
//------------------------------------------------------------------------------
template<class NextLayer, bool deflateSupported>
template<BOOST_BEAST_ASYNC_TPARAM1 HandshakeHandler>
BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
stream<NextLayer, deflateSupported>::
async_handshake(
string_view host,
string_view target,
HandshakeHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream type requirements not met");
detail::sec_ws_key_type key;
auto req = impl_->build_request(
key, host, target, &default_decorate_req);
return net::async_initiate<
HandshakeHandler,
void(error_code)>(
run_handshake_op{impl_},
handler,
std::move(req),
key,
nullptr);
}
template<class NextLayer, bool deflateSupported>
template<BOOST_BEAST_ASYNC_TPARAM1 HandshakeHandler>
BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
stream<NextLayer, deflateSupported>::
async_handshake(
response_type& res,
string_view host,
string_view target,
HandshakeHandler&& handler)
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream type requirements not met");
detail::sec_ws_key_type key;
auto req = impl_->build_request(
key, host, target, &default_decorate_req);
return net::async_initiate<
HandshakeHandler,
void(error_code)>(
run_handshake_op{impl_},
handler,
std::move(req),
key,
&res);
}
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
handshake(string_view host,
string_view target)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream type requirements not met");
error_code ec;
handshake(
host, target, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
handshake(response_type& res,
string_view host,
string_view target)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream type requirements not met");
error_code ec;
handshake(res, host, target, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
handshake(string_view host,
string_view target, error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream type requirements not met");
do_handshake(nullptr,
host, target, &default_decorate_req, ec);
}
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
handshake(response_type& res,
string_view host,
string_view target,
error_code& ec)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream type requirements not met");
do_handshake(&res,
host, target, &default_decorate_req, ec);
}
} // websocket
} // beast
} // boost
#endif