permessage-deflate is a compile-time feature (API Change):

fix #849

This adds an additional `bool` template parameter to
`websocket::stream`:

* When deflateSupported is `true`, the stream will be capable
  of negotiating the permessage-deflate websocket extension per
  the configured run-time settings.

* When deflateSupported is `false`, the stream will never negotiate
  the permessage-deflate websocket extension. Furthermore, all of the
  code necessary for implementing the permessage-deflate extension
  will be excluded from function instantiations. The resulting emitted
  object code should be smaller.
This commit is contained in:
Vinnie Falco
2017-11-18 16:52:18 -08:00
parent 65d92f62fb
commit 841ab8474b
20 changed files with 1315 additions and 721 deletions

View File

@ -1,3 +1,5 @@
--------------------------------------------------------------------------------
Version 151: Version 151:
* Sanitizer failures are errors * Sanitizer failures are errors
@ -13,6 +15,7 @@ WebSocket:
API Changes: API Changes:
* http::parser is not MoveConstructible * http::parser is not MoveConstructible
* permessage-deflate is a compile-time feature
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -10,13 +10,27 @@
[section Creating Streams] [section Creating Streams]
The interface to the WebSocket implementation is a single template class The interface to the WebSocket implementation is a single template class
[link beast.ref.boost__beast__websocket__stream `stream`] [link beast.ref.boost__beast__websocket__stream `stream`]:
which wraps an existing network transport object or other type of
octet oriented stream. The wrapped object is called the "next layer" [ws_snippet_26]
and must meet the requirements of __SyncStream__ if synchronous
operations are performed, __AsyncStream__ if asynchronous operations An instance of the stream wraps an existing network transport object
are performed, or both. Any arguments supplied during construction of or other type of octet oriented stream. The wrapped object is called
the stream wrapper are passed to next layer's constructor. the "next layer" and must meet the requirements of __SyncStream__ if
synchronous operations are performed, __AsyncStream__ if asynchronous
operations are performed, or both. Any arguments supplied during
construction of the stream wrapper are passed to next layer's constructor.
The value of `deflateSupported` determines if the stream will support
(but not require) the permessage-deflate extension
([@https://tools.ietf.org/html/rfc7692 rfc7692])
negotiation during handshaking. This extension allows messages to be
optionally automatically compressed using the deflate algorithm prior
to transmission. When this boolean value is `false`, the extension is
disabled. Applications which do not intend to use the permessage-deflate
extension may set the value to `false` to enjoy a reduction in the size
of the compiled output, as the necessary compression code (included with
Beast) will not be compiled in.
Here we declare a websocket stream over a TCP/IP socket with ownership Here we declare a websocket stream over a TCP/IP socket with ownership
of the socket. The `io_context` argument is forwarded to the wrapped of the socket. The `io_context` argument is forwarded to the wrapped

View File

@ -75,7 +75,7 @@ native_to_little_uint32(std::uint32_t v, void* buf)
p[3] = (v >> 24) & 0xff; p[3] = (v >> 24) & 0xff;
} }
/** WebSocket frame header opcodes. */ // frame header opcodes
enum class opcode : std::uint8_t enum class opcode : std::uint8_t
{ {
cont = 0, cont = 0,
@ -110,8 +110,7 @@ struct frame_header
}; };
// holds the largest possible frame header // holds the largest possible frame header
using fh_buffer = using fh_buffer = flat_static_buffer<14>;
flat_static_buffer<14>;
// holds the largest possible control frame // holds the largest possible control frame
using frame_buffer = using frame_buffer =

View File

@ -19,6 +19,7 @@
#include <boost/beast/http/rfc7230.hpp> #include <boost/beast/http/rfc7230.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <utility> #include <utility>
#include <type_traits>
namespace boost { namespace boost {
namespace beast { namespace beast {
@ -355,85 +356,6 @@ pmd_normalize(pmd_offer& offer)
} }
} }
//--------------------------------------------------------------------
// Compress a buffer sequence
// Returns: `true` if more calls are needed
//
template<class DeflateStream, class ConstBufferSequence>
bool
deflate(
DeflateStream& zo,
boost::asio::mutable_buffer& out,
buffers_suffix<ConstBufferSequence>& cb,
bool fin,
std::size_t& total_in,
error_code& ec)
{
using boost::asio::buffer;
BOOST_ASSERT(out.size() >= 6);
zlib::z_params zs;
zs.avail_in = 0;
zs.next_in = nullptr;
zs.avail_out = out.size();
zs.next_out = out.data();
for(auto in : beast::detail::buffers_range(cb))
{
zs.avail_in = in.size();
if(zs.avail_in == 0)
continue;
zs.next_in = in.data();
zo.write(zs, zlib::Flush::none, ec);
if(ec)
{
if(ec != zlib::error::need_buffers)
return false;
BOOST_ASSERT(zs.avail_out == 0);
BOOST_ASSERT(zs.total_out == out.size());
ec.assign(0, ec.category());
break;
}
if(zs.avail_out == 0)
{
BOOST_ASSERT(zs.total_out == out.size());
break;
}
BOOST_ASSERT(zs.avail_in == 0);
}
total_in = zs.total_in;
cb.consume(zs.total_in);
if(zs.avail_out > 0 && fin)
{
auto const remain = boost::asio::buffer_size(cb);
if(remain == 0)
{
// Inspired by Mark Adler
// https://github.com/madler/zlib/issues/149
//
// VFALCO We could do this flush twice depending
// on how much space is in the output.
zo.write(zs, zlib::Flush::block, ec);
BOOST_ASSERT(! ec || ec == zlib::error::need_buffers);
if(ec == zlib::error::need_buffers)
ec.assign(0, ec.category());
if(ec)
return false;
if(zs.avail_out >= 6)
{
zo.write(zs, zlib::Flush::full, ec);
BOOST_ASSERT(! ec);
// remove flush marker
zs.total_out -= 4;
out = buffer(out.data(), zs.total_out);
return false;
}
}
}
ec.assign(0, ec.category());
out = buffer(out.data(), zs.total_out);
return true;
}
} // detail } // detail
} // websocket } // websocket
} // beast } // beast

View File

@ -0,0 +1,141 @@
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_WEBSOCKET_STREAM_BASE_HPP
#define BOOST_BEAST_WEBSOCKET_STREAM_BASE_HPP
#include <boost/beast/websocket/option.hpp>
#include <boost/beast/websocket/detail/pmd_extension.hpp>
#include <boost/beast/zlib/deflate_stream.hpp>
#include <boost/beast/zlib/inflate_stream.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <memory>
namespace boost {
namespace beast {
namespace websocket {
namespace detail {
template<bool deflateSupported>
struct stream_base
{
// State information for the permessage-deflate extension
struct pmd_type
{
// `true` if current read message is compressed
bool rd_set = false;
zlib::deflate_stream zo;
zlib::inflate_stream zi;
};
std::unique_ptr<pmd_type> pmd_; // pmd settings or nullptr
permessage_deflate pmd_opts_; // local pmd options
detail::pmd_offer pmd_config_; // offer (client) or negotiation (server)
// return `true` if current message is deflated
bool
rd_deflated() const
{
return pmd_ && pmd_->rd_set;
}
// set whether current message is deflated
// returns `false` on protocol violation
bool
rd_deflated(bool rsv1)
{
if(pmd_)
{
pmd_->rd_set = rsv1;
return true;
}
return ! rsv1; // pmd not negotiated
}
template<class ConstBufferSequence>
bool
deflate(
boost::asio::mutable_buffer& out,
buffers_suffix<ConstBufferSequence>& cb,
bool fin,
std::size_t& total_in,
error_code& ec);
void
do_context_takeover_write(role_type role);
void
inflate(
zlib::z_params& zs,
zlib::Flush flush,
error_code& ec);
void
do_context_takeover_read(role_type role);
};
template<>
struct stream_base<false>
{
// These stubs are for avoiding linking in the zlib
// code when permessage-deflate is not enabled.
bool
rd_deflated() const
{
return false;
}
bool
rd_deflated(bool rsv1)
{
return ! rsv1;
}
template<class ConstBufferSequence>
bool
deflate(
boost::asio::mutable_buffer&,
buffers_suffix<ConstBufferSequence>&,
bool,
std::size_t&,
error_code&)
{
return false;
}
void
do_context_takeover_write(role_type)
{
}
void
inflate(
zlib::z_params&,
zlib::Flush,
error_code&)
{
}
void
do_context_takeover_read(role_type)
{
}
};
} // detail
} // websocket
} // beast
} // boost
#endif

View File

@ -19,12 +19,12 @@ namespace websocket {
namespace detail { namespace detail {
template<class F> template<class F>
using is_RequestDecorator = using is_request_decorator =
typename beast::detail::is_invocable<F, typename beast::detail::is_invocable<F,
void(request_type&)>::type; void(request_type&)>::type;
template<class F> template<class F>
using is_ResponseDecorator = using is_response_decorator =
typename beast::detail::is_invocable<F, typename beast::detail::is_invocable<F,
void(response_type&)>::type; void(response_type&)>::type;

View File

@ -34,20 +34,23 @@ namespace beast {
namespace websocket { namespace websocket {
// Respond to an upgrade HTTP request // Respond to an upgrade HTTP request
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Handler> template<class Handler>
class stream<NextLayer>::response_op class stream<NextLayer, deflateSupported>::response_op
: public boost::asio::coroutine : public boost::asio::coroutine
{ {
struct data struct data
{ {
stream<NextLayer>& ws; stream<NextLayer, deflateSupported>& ws;
response_type res; response_type res;
template<class Body, class Allocator, class Decorator> template<class Body, class Allocator, class Decorator>
data(Handler const&, stream<NextLayer>& ws_, http::request< data(
Body, http::basic_fields<Allocator>> const& req, Handler const&,
Decorator const& decorator) stream<NextLayer, deflateSupported>& ws_,
http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator)
: ws(ws_) : ws(ws_)
, res(ws_.build_response(req, decorator)) , res(ws_.build_response(req, decorator))
{ {
@ -62,7 +65,7 @@ public:
template<class DeducedHandler, class... Args> template<class DeducedHandler, class... Args>
response_op(DeducedHandler&& h, response_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args) stream<NextLayer, deflateSupported>& ws, Args&&... args)
: d_(std::forward<DeducedHandler>(h), : d_(std::forward<DeducedHandler>(h),
ws, std::forward<Args>(args)...) ws, std::forward<Args>(args)...)
{ {
@ -78,7 +81,8 @@ public:
} }
using executor_type = boost::asio::associated_executor_t< using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>; Handler, decltype(std::declval<
stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type executor_type
get_executor() const noexcept get_executor() const noexcept
@ -100,10 +104,10 @@ public:
} }
}; };
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Handler> template<class Handler>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
response_op<Handler>:: response_op<Handler>::
operator()( operator()(
error_code ec, error_code ec,
@ -121,7 +125,7 @@ operator()(
ec = error::handshake_failed; ec = error::handshake_failed;
if(! ec) if(! ec)
{ {
pmd_read(d.ws.pmd_config_, d.res); d.ws.do_pmd_config(d.res, is_deflate_supported{});
d.ws.open(role_type::server); d.ws.open(role_type::server);
} }
d_.invoke(ec); d_.invoke(ec);
@ -132,18 +136,20 @@ operator()(
// read and respond to an upgrade request // read and respond to an upgrade request
// //
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Decorator, class Handler> template<class Decorator, class Handler>
class stream<NextLayer>::accept_op class stream<NextLayer, deflateSupported>::accept_op
: public boost::asio::coroutine : public boost::asio::coroutine
{ {
struct data struct data
{ {
stream<NextLayer>& ws; stream<NextLayer, deflateSupported>& ws;
Decorator decorator; Decorator decorator;
http::request_parser<http::empty_body> p; http::request_parser<http::empty_body> p;
data(Handler const&, stream<NextLayer>& ws_, data(
Decorator const& decorator_) Handler const&,
stream<NextLayer, deflateSupported>& ws_,
Decorator const& decorator_)
: ws(ws_) : ws(ws_)
, decorator(decorator_) , decorator(decorator_)
{ {
@ -158,7 +164,7 @@ public:
template<class DeducedHandler, class... Args> template<class DeducedHandler, class... Args>
accept_op(DeducedHandler&& h, accept_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args) stream<NextLayer, deflateSupported>& ws, Args&&... args)
: d_(std::forward<DeducedHandler>(h), : d_(std::forward<DeducedHandler>(h),
ws, std::forward<Args>(args)...) ws, std::forward<Args>(args)...)
{ {
@ -174,7 +180,7 @@ public:
} }
using executor_type = boost::asio::associated_executor_t< using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>; Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type executor_type
get_executor() const noexcept get_executor() const noexcept
@ -199,11 +205,11 @@ public:
} }
}; };
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Decorator, class Handler> template<class Decorator, class Handler>
template<class Buffers> template<class Buffers>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept_op<Decorator, Handler>:: accept_op<Decorator, Handler>::
run(Buffers const& buffers) run(Buffers const& buffers)
{ {
@ -228,10 +234,10 @@ run(Buffers const& buffers)
(*this)(ec); (*this)(ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Decorator, class Handler> template<class Decorator, class Handler>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept_op<Decorator, Handler>:: accept_op<Decorator, Handler>::
operator()(error_code ec, std::size_t) operator()(error_code ec, std::size_t)
{ {
@ -280,9 +286,9 @@ operator()(error_code ec, std::size_t)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept() accept()
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -293,15 +299,15 @@ accept()
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ResponseDecorator> template<class ResponseDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept_ex(ResponseDecorator const& decorator) accept_ex(ResponseDecorator const& decorator)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_ResponseDecorator< static_assert(detail::is_response_decorator<
ResponseDecorator>::value, ResponseDecorator>::value,
"ResponseDecorator requirements not met"); "ResponseDecorator requirements not met");
error_code ec; error_code ec;
@ -310,9 +316,9 @@ accept_ex(ResponseDecorator const& decorator)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept(error_code& ec) accept(error_code& ec)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -321,26 +327,26 @@ accept(error_code& ec)
do_accept(&default_decorate_res, ec); do_accept(&default_decorate_res, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ResponseDecorator> template<class ResponseDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept_ex(ResponseDecorator const& decorator, error_code& ec) accept_ex(ResponseDecorator const& decorator, error_code& ec)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_ResponseDecorator< static_assert(detail::is_response_decorator<
ResponseDecorator>::value, ResponseDecorator>::value,
"ResponseDecorator requirements not met"); "ResponseDecorator requirements not met");
reset(); reset();
do_accept(decorator, ec); do_accept(decorator, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence> template<class ConstBufferSequence>
typename std::enable_if<! http::detail::is_header< typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type ConstBufferSequence>::value>::type
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept(ConstBufferSequence const& buffers) accept(ConstBufferSequence const& buffers)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -354,13 +360,13 @@ accept(ConstBufferSequence const& buffers)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class ConstBufferSequence, class ConstBufferSequence,
class ResponseDecorator> class ResponseDecorator>
typename std::enable_if<! http::detail::is_header< typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type ConstBufferSequence>::value>::type
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept_ex( accept_ex(
ConstBufferSequence const& buffers, ConstBufferSequence const& buffers,
ResponseDecorator const &decorator) ResponseDecorator const &decorator)
@ -370,7 +376,7 @@ accept_ex(
static_assert(boost::asio::is_const_buffer_sequence< static_assert(boost::asio::is_const_buffer_sequence<
ConstBufferSequence>::value, ConstBufferSequence>::value,
"ConstBufferSequence requirements not met"); "ConstBufferSequence requirements not met");
static_assert(detail::is_ResponseDecorator< static_assert(detail::is_response_decorator<
ResponseDecorator>::value, ResponseDecorator>::value,
"ResponseDecorator requirements not met"); "ResponseDecorator requirements not met");
error_code ec; error_code ec;
@ -379,11 +385,11 @@ accept_ex(
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence> template<class ConstBufferSequence>
typename std::enable_if<! http::detail::is_header< typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type ConstBufferSequence>::value>::type
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept( accept(
ConstBufferSequence const& buffers, error_code& ec) ConstBufferSequence const& buffers, error_code& ec)
{ {
@ -412,13 +418,13 @@ accept(
do_accept(&default_decorate_res, ec); do_accept(&default_decorate_res, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class ConstBufferSequence, class ConstBufferSequence,
class ResponseDecorator> class ResponseDecorator>
typename std::enable_if<! http::detail::is_header< typename std::enable_if<! http::detail::is_header<
ConstBufferSequence>::value>::type ConstBufferSequence>::value>::type
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept_ex( accept_ex(
ConstBufferSequence const& buffers, ConstBufferSequence const& buffers,
ResponseDecorator const& decorator, ResponseDecorator const& decorator,
@ -451,10 +457,10 @@ accept_ex(
do_accept(decorator, ec); do_accept(decorator, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator> template<class Body, class Allocator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept( accept(
http::request<Body, http::request<Body,
http::basic_fields<Allocator>> const& req) http::basic_fields<Allocator>> const& req)
@ -467,12 +473,12 @@ accept(
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class Body, class Allocator, class Body, class Allocator,
class ResponseDecorator> class ResponseDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept_ex( accept_ex(
http::request<Body, http::request<Body,
http::basic_fields<Allocator>> const& req, http::basic_fields<Allocator>> const& req,
@ -480,7 +486,7 @@ accept_ex(
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_ResponseDecorator< static_assert(detail::is_response_decorator<
ResponseDecorator>::value, ResponseDecorator>::value,
"ResponseDecorator requirements not met"); "ResponseDecorator requirements not met");
error_code ec; error_code ec;
@ -489,10 +495,10 @@ accept_ex(
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator> template<class Body, class Allocator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept( accept(
http::request<Body, http::request<Body,
http::basic_fields<Allocator>> const& req, http::basic_fields<Allocator>> const& req,
@ -504,12 +510,12 @@ accept(
do_accept(req, &default_decorate_res, ec); do_accept(req, &default_decorate_res, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class Body, class Allocator, class Body, class Allocator,
class ResponseDecorator> class ResponseDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
accept_ex( accept_ex(
http::request<Body, http::request<Body,
http::basic_fields<Allocator>> const& req, http::basic_fields<Allocator>> const& req,
@ -518,7 +524,7 @@ accept_ex(
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_ResponseDecorator< static_assert(detail::is_response_decorator<
ResponseDecorator>::value, ResponseDecorator>::value,
"ResponseDecorator requirements not met"); "ResponseDecorator requirements not met");
reset(); reset();
@ -527,12 +533,12 @@ accept_ex(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class AcceptHandler> class AcceptHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code)) AcceptHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_accept( async_accept(
AcceptHandler&& handler) AcceptHandler&& handler)
{ {
@ -551,20 +557,20 @@ async_accept(
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class ResponseDecorator, class ResponseDecorator,
class AcceptHandler> class AcceptHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code)) AcceptHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_accept_ex( async_accept_ex(
ResponseDecorator const& decorator, ResponseDecorator const& decorator,
AcceptHandler&& handler) AcceptHandler&& handler)
{ {
static_assert(is_async_stream<next_layer_type>::value, static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met"); "AsyncStream requirements not met");
static_assert(detail::is_ResponseDecorator< static_assert(detail::is_response_decorator<
ResponseDecorator>::value, ResponseDecorator>::value,
"ResponseDecorator requirements not met"); "ResponseDecorator requirements not met");
boost::asio::async_completion<AcceptHandler, boost::asio::async_completion<AcceptHandler,
@ -580,7 +586,7 @@ async_accept_ex(
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class ConstBufferSequence, class ConstBufferSequence,
class AcceptHandler> class AcceptHandler>
@ -588,7 +594,7 @@ typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value, ! http::detail::is_header<ConstBufferSequence>::value,
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code))>::type AcceptHandler, void(error_code))>::type
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_accept( async_accept(
ConstBufferSequence const& buffers, ConstBufferSequence const& buffers,
AcceptHandler&& handler) AcceptHandler&& handler)
@ -611,7 +617,7 @@ async_accept(
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class ConstBufferSequence, class ConstBufferSequence,
class ResponseDecorator, class ResponseDecorator,
@ -620,7 +626,7 @@ typename std::enable_if<
! http::detail::is_header<ConstBufferSequence>::value, ! http::detail::is_header<ConstBufferSequence>::value,
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code))>::type AcceptHandler, void(error_code))>::type
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_accept_ex( async_accept_ex(
ConstBufferSequence const& buffers, ConstBufferSequence const& buffers,
ResponseDecorator const& decorator, ResponseDecorator const& decorator,
@ -631,7 +637,7 @@ async_accept_ex(
static_assert(boost::asio::is_const_buffer_sequence< static_assert(boost::asio::is_const_buffer_sequence<
ConstBufferSequence>::value, ConstBufferSequence>::value,
"ConstBufferSequence requirements not met"); "ConstBufferSequence requirements not met");
static_assert(detail::is_ResponseDecorator< static_assert(detail::is_response_decorator<
ResponseDecorator>::value, ResponseDecorator>::value,
"ResponseDecorator requirements not met"); "ResponseDecorator requirements not met");
boost::asio::async_completion<AcceptHandler, boost::asio::async_completion<AcceptHandler,
@ -647,13 +653,13 @@ async_accept_ex(
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class Body, class Allocator, class Body, class Allocator,
class AcceptHandler> class AcceptHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code)) AcceptHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_accept( async_accept(
http::request<Body, http::basic_fields<Allocator>> const& req, http::request<Body, http::basic_fields<Allocator>> const& req,
AcceptHandler&& handler) AcceptHandler&& handler)
@ -674,14 +680,14 @@ async_accept(
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class Body, class Allocator, class Body, class Allocator,
class ResponseDecorator, class ResponseDecorator,
class AcceptHandler> class AcceptHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
AcceptHandler, void(error_code)) AcceptHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_accept_ex( async_accept_ex(
http::request<Body, http::basic_fields<Allocator>> const& req, http::request<Body, http::basic_fields<Allocator>> const& req,
ResponseDecorator const& decorator, ResponseDecorator const& decorator,
@ -689,7 +695,7 @@ async_accept_ex(
{ {
static_assert(is_async_stream<next_layer_type>::value, static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met"); "AsyncStream requirements not met");
static_assert(detail::is_ResponseDecorator< static_assert(detail::is_response_decorator<
ResponseDecorator>::value, ResponseDecorator>::value,
"ResponseDecorator requirements not met"); "ResponseDecorator requirements not met");
boost::asio::async_completion<AcceptHandler, boost::asio::async_completion<AcceptHandler,
@ -708,10 +714,10 @@ async_accept_ex(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Decorator> template<class Decorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
do_accept( do_accept(
Decorator const& decorator, Decorator const& decorator,
error_code& ec) error_code& ec)
@ -725,13 +731,14 @@ do_accept(
do_accept(p.get(), decorator, ec); do_accept(p.get(), decorator, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator, template<class Body, class Allocator,
class Decorator> class Decorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
do_accept( do_accept(
http::request<Body,http::basic_fields<Allocator>> const& req, http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator, Decorator const& decorator,
error_code& ec) error_code& ec)
{ {
@ -746,7 +753,7 @@ do_accept(
// teardown if Connection: close. // teardown if Connection: close.
return; return;
} }
pmd_read(pmd_config_, res); do_pmd_config(res, is_deflate_supported{});
open(role_type::server); open(role_type::server);
} }

View File

@ -34,14 +34,14 @@ namespace websocket {
frame. Finally it invokes the teardown operation to shut down the frame. Finally it invokes the teardown operation to shut down the
underlying connection. underlying connection.
*/ */
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Handler> template<class Handler>
class stream<NextLayer>::close_op class stream<NextLayer, deflateSupported>::close_op
: public boost::asio::coroutine : public boost::asio::coroutine
{ {
struct state struct state
{ {
stream<NextLayer>& ws; stream<NextLayer, deflateSupported>& ws;
detail::frame_buffer fb; detail::frame_buffer fb;
error_code ev; error_code ev;
token tok; token tok;
@ -49,7 +49,7 @@ class stream<NextLayer>::close_op
state( state(
Handler const&, Handler const&,
stream<NextLayer>& ws_, stream<NextLayer, deflateSupported>& ws_,
close_reason const& cr) close_reason const& cr)
: ws(ws_) : ws(ws_)
, tok(ws.tok_.unique()) , tok(ws.tok_.unique())
@ -69,7 +69,7 @@ public:
template<class DeducedHandler> template<class DeducedHandler>
close_op( close_op(
DeducedHandler&& h, DeducedHandler&& h,
stream<NextLayer>& ws, stream<NextLayer, deflateSupported>& ws,
close_reason const& cr) close_reason const& cr)
: d_(std::forward<DeducedHandler>(h), ws, cr) : d_(std::forward<DeducedHandler>(h), ws, cr)
{ {
@ -85,7 +85,7 @@ public:
} }
using executor_type = boost::asio::associated_executor_t< using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>; Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type executor_type
get_executor() const noexcept get_executor() const noexcept
@ -109,10 +109,10 @@ public:
} }
}; };
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Handler> template<class Handler>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
close_op<Handler>:: close_op<Handler>::
operator()( operator()(
error_code ec, error_code ec,
@ -327,9 +327,9 @@ operator()(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
close(close_reason const& cr) close(close_reason const& cr)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -340,9 +340,9 @@ close(close_reason const& cr)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
close(close_reason const& cr, error_code& ec) close(close_reason const& cr, error_code& ec)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -434,11 +434,11 @@ close(close_reason const& cr, error_code& ec)
ec.assign(0, ec.category()); ec.assign(0, ec.category());
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class CloseHandler> template<class CloseHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
CloseHandler, void(error_code)) CloseHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_close(close_reason const& cr, CloseHandler&& handler) async_close(close_reason const& cr, CloseHandler&& handler)
{ {
static_assert(is_async_stream<next_layer_type>::value, static_assert(is_async_stream<next_layer_type>::value,

View File

@ -33,25 +33,27 @@ namespace websocket {
// send the upgrade request and process the response // send the upgrade request and process the response
// //
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Handler> template<class Handler>
class stream<NextLayer>::handshake_op class stream<NextLayer, deflateSupported>::handshake_op
: public boost::asio::coroutine : public boost::asio::coroutine
{ {
struct data struct data
{ {
stream<NextLayer>& ws; stream<NextLayer, deflateSupported>& ws;
response_type* res_p; response_type* res_p;
detail::sec_ws_key_type key; detail::sec_ws_key_type key;
http::request<http::empty_body> req; http::request<http::empty_body> req;
response_type res; response_type res;
template<class Decorator> template<class Decorator>
data(Handler const&, stream<NextLayer>& ws_, data(
Handler const&,
stream<NextLayer, deflateSupported>& ws_,
response_type* res_p_, response_type* res_p_,
string_view host, string_view host,
string_view target, string_view target,
Decorator const& decorator) Decorator const& decorator)
: ws(ws_) : ws(ws_)
, res_p(res_p_) , res_p(res_p_)
, req(ws.build_request(key, , req(ws.build_request(key,
@ -69,7 +71,7 @@ public:
template<class DeducedHandler, class... Args> template<class DeducedHandler, class... Args>
handshake_op(DeducedHandler&& h, handshake_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args) stream<NextLayer, deflateSupported>& ws, Args&&... args)
: d_(std::forward<DeducedHandler>(h), : d_(std::forward<DeducedHandler>(h),
ws, std::forward<Args>(args)...) ws, std::forward<Args>(args)...)
{ {
@ -85,7 +87,7 @@ public:
} }
using executor_type = boost::asio::associated_executor_t< using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>; Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type executor_type
get_executor() const noexcept get_executor() const noexcept
@ -108,17 +110,18 @@ public:
} }
}; };
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Handler> template<class Handler>
void void
stream<NextLayer>::handshake_op<Handler>:: stream<NextLayer, deflateSupported>::
handshake_op<Handler>::
operator()(error_code ec, std::size_t) operator()(error_code ec, std::size_t)
{ {
auto& d = *d_; auto& d = *d_;
BOOST_ASIO_CORO_REENTER(*this) BOOST_ASIO_CORO_REENTER(*this)
{ {
// Send HTTP Upgrade // Send HTTP Upgrade
pmd_read(d.ws.pmd_config_, d.req); d.ws.do_pmd_config(d.req, is_deflate_supported{});
BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD
http::async_write(d.ws.stream_, http::async_write(d.ws.stream_,
d.req, std::move(*this)); d.req, std::move(*this));
@ -146,11 +149,11 @@ operator()(error_code ec, std::size_t)
} }
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class HandshakeHandler> template<class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code)) HandshakeHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_handshake(string_view host, async_handshake(string_view host,
string_view target, string_view target,
HandshakeHandler&& handler) HandshakeHandler&& handler)
@ -166,11 +169,11 @@ async_handshake(string_view host,
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class HandshakeHandler> template<class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code)) HandshakeHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_handshake(response_type& res, async_handshake(response_type& res,
string_view host, string_view host,
string_view target, string_view target,
@ -187,11 +190,11 @@ async_handshake(response_type& res,
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler> template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code)) HandshakeHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_handshake_ex(string_view host, async_handshake_ex(string_view host,
string_view target, string_view target,
RequestDecorator const& decorator, RequestDecorator const& decorator,
@ -199,7 +202,7 @@ async_handshake_ex(string_view host,
{ {
static_assert(is_async_stream<next_layer_type>::value, static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met"); "AsyncStream requirements not met");
static_assert(detail::is_RequestDecorator< static_assert(detail::is_request_decorator<
RequestDecorator>::value, RequestDecorator>::value,
"RequestDecorator requirements not met"); "RequestDecorator requirements not met");
boost::asio::async_completion<HandshakeHandler, boost::asio::async_completion<HandshakeHandler,
@ -211,11 +214,11 @@ async_handshake_ex(string_view host,
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler> template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code)) HandshakeHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_handshake_ex(response_type& res, async_handshake_ex(response_type& res,
string_view host, string_view host,
string_view target, string_view target,
@ -224,7 +227,7 @@ async_handshake_ex(response_type& res,
{ {
static_assert(is_async_stream<next_layer_type>::value, static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met"); "AsyncStream requirements not met");
static_assert(detail::is_RequestDecorator< static_assert(detail::is_request_decorator<
RequestDecorator>::value, RequestDecorator>::value,
"RequestDecorator requirements not met"); "RequestDecorator requirements not met");
boost::asio::async_completion<HandshakeHandler, boost::asio::async_completion<HandshakeHandler,
@ -236,9 +239,9 @@ async_handshake_ex(response_type& res,
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
handshake(string_view host, handshake(string_view host,
string_view target) string_view target)
{ {
@ -251,9 +254,9 @@ handshake(string_view host,
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
handshake(response_type& res, handshake(response_type& res,
string_view host, string_view host,
string_view target) string_view target)
@ -266,17 +269,17 @@ handshake(response_type& res,
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class RequestDecorator> template<class RequestDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
handshake_ex(string_view host, handshake_ex(string_view host,
string_view target, string_view target,
RequestDecorator const& decorator) RequestDecorator const& decorator)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_RequestDecorator< static_assert(detail::is_request_decorator<
RequestDecorator>::value, RequestDecorator>::value,
"RequestDecorator requirements not met"); "RequestDecorator requirements not met");
error_code ec; error_code ec;
@ -285,10 +288,10 @@ handshake_ex(string_view host,
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class RequestDecorator> template<class RequestDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
handshake_ex(response_type& res, handshake_ex(response_type& res,
string_view host, string_view host,
string_view target, string_view target,
@ -296,7 +299,7 @@ handshake_ex(response_type& res,
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_RequestDecorator< static_assert(detail::is_request_decorator<
RequestDecorator>::value, RequestDecorator>::value,
"RequestDecorator requirements not met"); "RequestDecorator requirements not met");
error_code ec; error_code ec;
@ -305,9 +308,9 @@ handshake_ex(response_type& res,
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
handshake(string_view host, handshake(string_view host,
string_view target, error_code& ec) string_view target, error_code& ec)
{ {
@ -317,9 +320,9 @@ handshake(string_view host,
host, target, &default_decorate_req, ec); host, target, &default_decorate_req, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
handshake(response_type& res, handshake(response_type& res,
string_view host, string_view host,
string_view target, string_view target,
@ -331,10 +334,10 @@ handshake(response_type& res,
host, target, &default_decorate_req, ec); host, target, &default_decorate_req, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class RequestDecorator> template<class RequestDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
handshake_ex(string_view host, handshake_ex(string_view host,
string_view target, string_view target,
RequestDecorator const& decorator, RequestDecorator const& decorator,
@ -342,17 +345,17 @@ handshake_ex(string_view host,
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_RequestDecorator< static_assert(detail::is_request_decorator<
RequestDecorator>::value, RequestDecorator>::value,
"RequestDecorator requirements not met"); "RequestDecorator requirements not met");
do_handshake(nullptr, do_handshake(nullptr,
host, target, decorator, ec); host, target, decorator, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class RequestDecorator> template<class RequestDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
handshake_ex(response_type& res, handshake_ex(response_type& res,
string_view host, string_view host,
string_view target, string_view target,
@ -361,7 +364,7 @@ handshake_ex(response_type& res,
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met"); "SyncStream requirements not met");
static_assert(detail::is_RequestDecorator< static_assert(detail::is_request_decorator<
RequestDecorator>::value, RequestDecorator>::value,
"RequestDecorator requirements not met"); "RequestDecorator requirements not met");
do_handshake(&res, do_handshake(&res,
@ -370,10 +373,10 @@ handshake_ex(response_type& res,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class RequestDecorator> template<class RequestDecorator>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
do_handshake( do_handshake(
response_type* res_p, response_type* res_p,
string_view host, string_view host,
@ -387,7 +390,7 @@ do_handshake(
{ {
auto const req = build_request( auto const req = build_request(
key, host, target, decorator); key, host, target, decorator);
pmd_read(pmd_config_, req); do_pmd_config(req, is_deflate_supported{});
http::write(stream_, req, ec); http::write(stream_, req, ec);
} }
if(ec) if(ec)

View File

@ -32,20 +32,20 @@ namespace websocket {
It only sends the frames it does not make attempts to read It only sends the frames it does not make attempts to read
any frame data. any frame data.
*/ */
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Handler> template<class Handler>
class stream<NextLayer>::ping_op class stream<NextLayer, deflateSupported>::ping_op
: public boost::asio::coroutine : public boost::asio::coroutine
{ {
struct state struct state
{ {
stream<NextLayer>& ws; stream<NextLayer, deflateSupported>& ws;
detail::frame_buffer fb; detail::frame_buffer fb;
token tok; token tok;
state( state(
Handler const&, Handler const&,
stream<NextLayer>& ws_, stream<NextLayer, deflateSupported>& ws_,
detail::opcode op, detail::opcode op,
ping_data const& payload) ping_data const& payload)
: ws(ws_) : ws(ws_)
@ -67,7 +67,7 @@ public:
template<class DeducedHandler> template<class DeducedHandler>
ping_op( ping_op(
DeducedHandler&& h, DeducedHandler&& h,
stream<NextLayer>& ws, stream<NextLayer, deflateSupported>& ws,
detail::opcode op, detail::opcode op,
ping_data const& payload) ping_data const& payload)
: d_(std::forward<DeducedHandler>(h), : d_(std::forward<DeducedHandler>(h),
@ -85,7 +85,7 @@ public:
} }
using executor_type = boost::asio::associated_executor_t< using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>; Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type executor_type
get_executor() const noexcept get_executor() const noexcept
@ -107,10 +107,10 @@ public:
} }
}; };
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Handler> template<class Handler>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
ping_op<Handler>:: ping_op<Handler>::
operator()(error_code ec, std::size_t) operator()(error_code ec, std::size_t)
{ {
@ -174,9 +174,9 @@ operator()(error_code ec, std::size_t)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
ping(ping_data const& payload) ping(ping_data const& payload)
{ {
error_code ec; error_code ec;
@ -185,9 +185,9 @@ ping(ping_data const& payload)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
ping(ping_data const& payload, error_code& ec) ping(ping_data const& payload, error_code& ec)
{ {
// Make sure the stream is open // Make sure the stream is open
@ -201,9 +201,9 @@ ping(ping_data const& payload, error_code& ec)
return; return;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
pong(ping_data const& payload) pong(ping_data const& payload)
{ {
error_code ec; error_code ec;
@ -212,9 +212,9 @@ pong(ping_data const& payload)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
pong(ping_data const& payload, error_code& ec) pong(ping_data const& payload, error_code& ec)
{ {
// Make sure the stream is open // Make sure the stream is open
@ -228,11 +228,11 @@ pong(ping_data const& payload, error_code& ec)
return; return;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class WriteHandler> template<class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code)) WriteHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_ping(ping_data const& payload, WriteHandler&& handler) async_ping(ping_data const& payload, WriteHandler&& handler)
{ {
static_assert(is_async_stream<next_layer_type>::value, static_assert(is_async_stream<next_layer_type>::value,
@ -246,11 +246,11 @@ async_ping(ping_data const& payload, WriteHandler&& handler)
return init.result.get(); return init.result.get();
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class WriteHandler> template<class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code)) WriteHandler, void(error_code))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_pong(ping_data const& payload, WriteHandler&& handler) async_pong(ping_data const& payload, WriteHandler&& handler)
{ {
static_assert(is_async_stream<next_layer_type>::value, static_assert(is_async_stream<next_layer_type>::value,

View File

@ -35,19 +35,52 @@ namespace boost {
namespace beast { namespace beast {
namespace websocket { namespace websocket {
namespace detail {
template<>
inline
void
stream_base<true>::
inflate(
zlib::z_params& zs,
zlib::Flush flush,
error_code& ec)
{
this->pmd_->zi.write(zs, flush, ec);
}
template<>
inline
void
stream_base<true>::
do_context_takeover_read(role_type role)
{
if((role == role_type::client &&
pmd_config_.server_no_context_takeover) ||
(role == role_type::server &&
pmd_config_.client_no_context_takeover))
{
pmd_->zi.reset();
}
}
} // detail
//------------------------------------------------------------------------------
/* Read some message frame data. /* Read some message frame data.
Also reads and handles control frames. Also reads and handles control frames.
*/ */
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class MutableBufferSequence, class MutableBufferSequence,
class Handler> class Handler>
class stream<NextLayer>::read_some_op class stream<NextLayer, deflateSupported>::read_some_op
: public boost::asio::coroutine : public boost::asio::coroutine
{ {
Handler h_; Handler h_;
stream<NextLayer>& ws_; stream<NextLayer, deflateSupported>& ws_;
MutableBufferSequence bs_; MutableBufferSequence bs_;
buffers_suffix<MutableBufferSequence> cb_; buffers_suffix<MutableBufferSequence> cb_;
std::size_t bytes_written_ = 0; std::size_t bytes_written_ = 0;
@ -64,7 +97,7 @@ public:
template<class DeducedHandler> template<class DeducedHandler>
read_some_op( read_some_op(
DeducedHandler&& h, DeducedHandler&& h,
stream<NextLayer>& ws, stream<NextLayer, deflateSupported>& ws,
MutableBufferSequence const& bs) MutableBufferSequence const& bs)
: h_(std::forward<DeducedHandler>(h)) : h_(std::forward<DeducedHandler>(h))
, ws_(ws) , ws_(ws)
@ -85,7 +118,7 @@ public:
} }
using executor_type = boost::asio::associated_executor_t< using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>; Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type executor_type
get_executor() const noexcept get_executor() const noexcept
@ -114,10 +147,10 @@ public:
} }
}; };
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class MutableBufferSequence, class Handler> template<class MutableBufferSequence, class Handler>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read_some_op<MutableBufferSequence, Handler>:: read_some_op<MutableBufferSequence, Handler>::
operator()( operator()(
error_code ec, error_code ec,
@ -404,7 +437,7 @@ operator()(
} }
ws_.rd_done_ = false; ws_.rd_done_ = false;
} }
if(! ws_.pmd_ || ! ws_.pmd_->rd_set) if(! ws_.rd_deflated())
{ {
if(ws_.rd_remain_ > 0) if(ws_.rd_remain_ > 0)
{ {
@ -546,7 +579,7 @@ operator()(
0x00, 0x00, 0xff, 0xff }; 0x00, 0x00, 0xff, 0xff };
zs.next_in = empty_block; zs.next_in = empty_block;
zs.avail_in = sizeof(empty_block); zs.avail_in = sizeof(empty_block);
ws_.pmd_->zi.write(zs, zlib::Flush::sync, ec); ws_.inflate(zs, zlib::Flush::sync, ec);
if(! ec) if(! ec)
{ {
// https://github.com/madler/zlib/issues/280 // https://github.com/madler/zlib/issues/280
@ -555,12 +588,7 @@ operator()(
} }
if(! ws_.check_ok(ec)) if(! ws_.check_ok(ec))
goto upcall; goto upcall;
if( ws_.do_context_takeover_read(ws_.role_);
(ws_.role_ == role_type::client &&
ws_.pmd_config_.server_no_context_takeover) ||
(ws_.role_ == role_type::server &&
ws_.pmd_config_.client_no_context_takeover))
ws_.pmd_->zi.reset();
ws_.rd_done_ = true; ws_.rd_done_ = true;
break; break;
} }
@ -568,7 +596,7 @@ operator()(
{ {
break; break;
} }
ws_.pmd_->zi.write(zs, zlib::Flush::sync, ec); ws_.inflate(zs, zlib::Flush::sync, ec);
if(! ws_.check_ok(ec)) if(! ws_.check_ok(ec))
goto upcall; goto upcall;
if(ws_.rd_msg_max_ && beast::detail::sum_exceeds( if(ws_.rd_msg_max_ && beast::detail::sum_exceeds(
@ -699,15 +727,15 @@ operator()(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template< template<
class DynamicBuffer, class DynamicBuffer,
class Handler> class Handler>
class stream<NextLayer>::read_op class stream<NextLayer, deflateSupported>::read_op
: public boost::asio::coroutine : public boost::asio::coroutine
{ {
Handler h_; Handler h_;
stream<NextLayer>& ws_; stream<NextLayer, deflateSupported>& ws_;
DynamicBuffer& b_; DynamicBuffer& b_;
std::size_t limit_; std::size_t limit_;
std::size_t bytes_written_ = 0; std::size_t bytes_written_ = 0;
@ -723,7 +751,7 @@ public:
template<class DeducedHandler> template<class DeducedHandler>
read_op( read_op(
DeducedHandler&& h, DeducedHandler&& h,
stream<NextLayer>& ws, stream<NextLayer, deflateSupported>& ws,
DynamicBuffer& b, DynamicBuffer& b,
std::size_t limit, std::size_t limit,
bool some) bool some)
@ -743,7 +771,7 @@ public:
} }
using executor_type = boost::asio::associated_executor_t< using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>; Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type executor_type
get_executor() const noexcept get_executor() const noexcept
@ -765,10 +793,10 @@ public:
} }
}; };
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class Handler> template<class DynamicBuffer, class Handler>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read_op<DynamicBuffer, Handler>:: read_op<DynamicBuffer, Handler>::
operator()( operator()(
error_code ec, error_code ec,
@ -816,10 +844,10 @@ operator()(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer> template<class DynamicBuffer>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read(DynamicBuffer& buffer) read(DynamicBuffer& buffer)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -834,10 +862,10 @@ read(DynamicBuffer& buffer)
return bytes_written; return bytes_written;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer> template<class DynamicBuffer>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read(DynamicBuffer& buffer, error_code& ec) read(DynamicBuffer& buffer, error_code& ec)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -856,11 +884,11 @@ read(DynamicBuffer& buffer, error_code& ec)
return bytes_written; return bytes_written;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class ReadHandler> template<class DynamicBuffer, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_read(DynamicBuffer& buffer, ReadHandler&& handler) async_read(DynamicBuffer& buffer, ReadHandler&& handler)
{ {
static_assert(is_async_stream<next_layer_type>::value, static_assert(is_async_stream<next_layer_type>::value,
@ -884,10 +912,10 @@ async_read(DynamicBuffer& buffer, ReadHandler&& handler)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer> template<class DynamicBuffer>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read_some( read_some(
DynamicBuffer& buffer, DynamicBuffer& buffer,
std::size_t limit) std::size_t limit)
@ -905,10 +933,10 @@ read_some(
return bytes_written; return bytes_written;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer> template<class DynamicBuffer>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read_some( read_some(
DynamicBuffer& buffer, DynamicBuffer& buffer,
std::size_t limit, std::size_t limit,
@ -941,11 +969,11 @@ read_some(
return bytes_written; return bytes_written;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class ReadHandler> template<class DynamicBuffer, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_read_some( async_read_some(
DynamicBuffer& buffer, DynamicBuffer& buffer,
std::size_t limit, std::size_t limit,
@ -972,10 +1000,10 @@ async_read_some(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class MutableBufferSequence> template<class MutableBufferSequence>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read_some( read_some(
MutableBufferSequence const& buffers) MutableBufferSequence const& buffers)
{ {
@ -991,10 +1019,10 @@ read_some(
return bytes_written; return bytes_written;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class MutableBufferSequence> template<class MutableBufferSequence>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read_some( read_some(
MutableBufferSequence const& buffers, MutableBufferSequence const& buffers,
error_code& ec) error_code& ec)
@ -1125,7 +1153,7 @@ loop:
{ {
ec.assign(0, ec.category()); ec.assign(0, ec.category());
} }
if(! pmd_ || ! pmd_->rd_set) if(! this->rd_deflated())
{ {
if(rd_remain_ > 0) if(rd_remain_ > 0)
{ {
@ -1273,7 +1301,7 @@ loop:
0x00, 0x00, 0xff, 0xff }; 0x00, 0x00, 0xff, 0xff };
zs.next_in = empty_block; zs.next_in = empty_block;
zs.avail_in = sizeof(empty_block); zs.avail_in = sizeof(empty_block);
pmd_->zi.write(zs, zlib::Flush::sync, ec); this->inflate(zs, zlib::Flush::sync, ec);
if(! ec) if(! ec)
{ {
// https://github.com/madler/zlib/issues/280 // https://github.com/madler/zlib/issues/280
@ -1282,12 +1310,7 @@ loop:
} }
if(! check_ok(ec)) if(! check_ok(ec))
return bytes_written; return bytes_written;
if( this->do_context_takeover_read(role_);
(role_ == role_type::client &&
pmd_config_.server_no_context_takeover) ||
(role_ == role_type::server &&
pmd_config_.client_no_context_takeover))
pmd_->zi.reset();
rd_done_ = true; rd_done_ = true;
break; break;
} }
@ -1295,7 +1318,7 @@ loop:
{ {
break; break;
} }
pmd_->zi.write(zs, zlib::Flush::sync, ec); this->inflate(zs, zlib::Flush::sync, ec);
if(! check_ok(ec)) if(! check_ok(ec))
return bytes_written; return bytes_written;
if(rd_msg_max_ && beast::detail::sum_exceeds( if(rd_msg_max_ && beast::detail::sum_exceeds(
@ -1328,11 +1351,11 @@ loop:
return bytes_written; return bytes_written;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class MutableBufferSequence, class ReadHandler> template<class MutableBufferSequence, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_read_some( async_read_some(
MutableBufferSequence const& buffers, MutableBufferSequence const& buffers,
ReadHandler&& handler) ReadHandler&& handler)

View File

@ -40,9 +40,9 @@ namespace boost {
namespace beast { namespace beast {
namespace websocket { namespace websocket {
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class... Args> template<class... Args>
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
stream(Args&&... args) stream(Args&&... args)
: stream_(std::forward<Args>(args)...) : stream_(std::forward<Args>(args)...)
, tok_(1) , tok_(1)
@ -51,16 +51,221 @@ stream(Args&&... args)
max_control_frame_size); max_control_frame_size);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read_size_hint(DynamicBuffer& buffer) const
{
static_assert(
boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
auto const initial_size = (std::min)(
+tcp_frame_size,
buffer.max_size() - buffer.size());
if(initial_size == 0)
return 1; // buffer is full
return read_size_hint(initial_size);
}
//------------------------------------------------------------------------------
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
set_option(permessage_deflate const& o, std::true_type)
{
if( o.server_max_window_bits > 15 ||
o.server_max_window_bits < 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid server_max_window_bits"});
if( o.client_max_window_bits > 15 ||
o.client_max_window_bits < 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid client_max_window_bits"});
if( o.compLevel < 0 ||
o.compLevel > 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid compLevel"});
if( o.memLevel < 1 ||
o.memLevel > 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid memLevel"});
this->pmd_opts_ = o;
}
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
set_option(permessage_deflate const& o, std::false_type)
{
if(o.client_enable || o.server_enable)
{
// Can't enable permessage-deflate
// when deflateSupported == false.
//
BOOST_THROW_EXCEPTION(std::invalid_argument{
"deflateSupported == false"});
}
}
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
open(role_type role)
{
// VFALCO TODO analyze and remove dupe code in reset()
role_ = role;
status_ = status::open;
rd_remain_ = 0;
rd_cont_ = false;
rd_done_ = true;
// Can't clear this because accept uses it
//rd_buf_.reset();
rd_fh_.fin = false;
rd_close_ = false;
wr_close_ = false;
wr_block_.reset();
rd_block_.reset();
cr_.code = close_code::none;
wr_cont_ = false;
wr_buf_size_ = 0;
open_pmd(is_deflate_supported{});
}
template<class NextLayer, bool deflateSupported>
inline
void
stream<NextLayer, deflateSupported>::
open_pmd(std::true_type)
{
if(((role_ == role_type::client &&
this->pmd_opts_.client_enable) ||
(role_ == role_type::server &&
this->pmd_opts_.server_enable)) &&
this->pmd_config_.accept)
{
pmd_normalize(this->pmd_config_);
this->pmd_.reset(new typename
detail::stream_base<deflateSupported>::pmd_type);
if(role_ == role_type::client)
{
this->pmd_->zi.reset(
this->pmd_config_.server_max_window_bits);
this->pmd_->zo.reset(
this->pmd_opts_.compLevel,
this->pmd_config_.client_max_window_bits,
this->pmd_opts_.memLevel,
zlib::Strategy::normal);
}
else
{
this->pmd_->zi.reset(
this->pmd_config_.client_max_window_bits);
this->pmd_->zo.reset(
this->pmd_opts_.compLevel,
this->pmd_config_.server_max_window_bits,
this->pmd_opts_.memLevel,
zlib::Strategy::normal);
}
}
}
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
close()
{
wr_buf_.reset();
close_pmd(is_deflate_supported{});
}
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
reset()
{
BOOST_ASSERT(status_ != status::open);
rd_remain_ = 0;
rd_cont_ = false;
rd_done_ = true;
rd_buf_.consume(rd_buf_.size());
rd_fh_.fin = false;
rd_close_ = false;
wr_close_ = false;
wr_cont_ = false;
wr_block_.reset();
rd_block_.reset();
cr_.code = close_code::none;
}
// Called before each write frame
template<class NextLayer, bool deflateSupported>
inline
void
stream<NextLayer, deflateSupported>::
begin_msg(std::true_type)
{
wr_frag_ = wr_frag_opt_;
wr_compress_ = static_cast<bool>(this->pmd_);
// Maintain the write buffer
if( wr_compress_ ||
role_ == role_type::client)
{
if(! wr_buf_ || wr_buf_size_ != wr_buf_opt_)
{
wr_buf_size_ = wr_buf_opt_;
wr_buf_ = boost::make_unique_noinit<
std::uint8_t[]>(wr_buf_size_);
}
}
else
{
wr_buf_size_ = wr_buf_opt_;
wr_buf_.reset();
}
}
// Called before each write frame
template<class NextLayer, bool deflateSupported>
inline
void
stream<NextLayer, deflateSupported>::
begin_msg(std::false_type)
{
wr_frag_ = wr_frag_opt_;
// Maintain the write buffer
if(role_ == role_type::client)
{
if(! wr_buf_ || wr_buf_size_ != wr_buf_opt_)
{
wr_buf_size_ = wr_buf_opt_;
wr_buf_ = boost::make_unique_noinit<
std::uint8_t[]>(wr_buf_size_);
}
}
else
{
wr_buf_size_ = wr_buf_opt_;
wr_buf_.reset();
}
}
template<class NextLayer, bool deflateSupported>
std::size_t
stream<NextLayer, deflateSupported>::
read_size_hint( read_size_hint(
std::size_t initial_size) const std::size_t initial_size,
std::true_type) const
{ {
using beast::detail::clamp; using beast::detail::clamp;
std::size_t result; std::size_t result;
BOOST_ASSERT(initial_size > 0); BOOST_ASSERT(initial_size > 0);
if(! pmd_ || (! rd_done_ && ! pmd_->rd_set)) if(! this->pmd_ || (! rd_done_ && ! this->pmd_->rd_set))
{ {
// current message is uncompressed // current message is uncompressed
@ -85,164 +290,45 @@ done:
return result; return result;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
read_size_hint(DynamicBuffer& buffer) const read_size_hint(
std::size_t initial_size,
std::false_type) const
{ {
static_assert( using beast::detail::clamp;
boost::asio::is_dynamic_buffer<DynamicBuffer>::value, std::size_t result;
"DynamicBuffer requirements not met"); BOOST_ASSERT(initial_size > 0);
auto const initial_size = (std::min)( // compression is not supported
+tcp_frame_size, if(rd_done_)
buffer.max_size() - buffer.size());
if(initial_size == 0)
return 1; // buffer is full
return read_size_hint(initial_size);
}
template<class NextLayer>
void
stream<NextLayer>::
set_option(permessage_deflate const& o)
{
if( o.server_max_window_bits > 15 ||
o.server_max_window_bits < 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid server_max_window_bits"});
if( o.client_max_window_bits > 15 ||
o.client_max_window_bits < 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid client_max_window_bits"});
if( o.compLevel < 0 ||
o.compLevel > 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid compLevel"});
if( o.memLevel < 1 ||
o.memLevel > 9)
BOOST_THROW_EXCEPTION(std::invalid_argument{
"invalid memLevel"});
pmd_opts_ = o;
}
//------------------------------------------------------------------------------
template<class NextLayer>
void
stream<NextLayer>::
open(role_type role)
{
// VFALCO TODO analyze and remove dupe code in reset()
role_ = role;
status_ = status::open;
rd_remain_ = 0;
rd_cont_ = false;
rd_done_ = true;
// Can't clear this because accept uses it
//rd_buf_.reset();
rd_fh_.fin = false;
rd_close_ = false;
wr_close_ = false;
wr_block_.reset();
rd_block_.reset();
cr_.code = close_code::none;
wr_cont_ = false;
wr_buf_size_ = 0;
if(((role_ == role_type::client && pmd_opts_.client_enable) ||
(role_ == role_type::server && pmd_opts_.server_enable)) &&
pmd_config_.accept)
{ {
pmd_normalize(pmd_config_); // first message frame
pmd_.reset(new pmd_t); result = initial_size;
if(role_ == role_type::client)
{
pmd_->zi.reset(
pmd_config_.server_max_window_bits);
pmd_->zo.reset(
pmd_opts_.compLevel,
pmd_config_.client_max_window_bits,
pmd_opts_.memLevel,
zlib::Strategy::normal);
}
else
{
pmd_->zi.reset(
pmd_config_.client_max_window_bits);
pmd_->zo.reset(
pmd_opts_.compLevel,
pmd_config_.server_max_window_bits,
pmd_opts_.memLevel,
zlib::Strategy::normal);
}
} }
} else if(rd_fh_.fin)
template<class NextLayer>
void
stream<NextLayer>::
close()
{
wr_buf_.reset();
pmd_.reset();
}
template<class NextLayer>
void
stream<NextLayer>::
reset()
{
BOOST_ASSERT(status_ != status::open);
rd_remain_ = 0;
rd_cont_ = false;
rd_done_ = true;
rd_buf_.consume(rd_buf_.size());
rd_fh_.fin = false;
rd_close_ = false;
wr_close_ = false;
wr_cont_ = false;
wr_block_.reset();
rd_block_.reset();
cr_.code = close_code::none;
}
// Called before each write frame
template<class NextLayer>
void
stream<NextLayer>::
begin_msg()
{
wr_frag_ = wr_frag_opt_;
wr_compress_ = static_cast<bool>(pmd_);
// Maintain the write buffer
if( wr_compress_ ||
role_ == role_type::client)
{ {
if(! wr_buf_ || wr_buf_size_ != wr_buf_opt_) // last message frame
{ BOOST_ASSERT(rd_remain_ > 0);
wr_buf_size_ = wr_buf_opt_; result = clamp(rd_remain_);
wr_buf_ = boost::make_unique_noinit<
std::uint8_t[]>(wr_buf_size_);
}
} }
else else
{ {
wr_buf_size_ = wr_buf_opt_; result = (std::max)(
wr_buf_.reset(); initial_size, clamp(rd_remain_));
} }
BOOST_ASSERT(result != 0);
return result;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Attempt to read a complete frame header. // Attempt to read a complete frame header.
// Returns `false` if more bytes are needed // Returns `false` if more bytes are needed
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer> template<class DynamicBuffer>
bool bool
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
parse_fh( parse_fh(
detail::frame_header& fh, detail::frame_header& fh,
DynamicBuffer& b, DynamicBuffer& b,
@ -301,14 +387,12 @@ parse_fh(
// new data frame when continuation expected // new data frame when continuation expected
return err(close_code::protocol_error); return err(close_code::protocol_error);
} }
if((fh.rsv1 && ! pmd_) || if(fh.rsv2 || fh.rsv3 ||
fh.rsv2 || fh.rsv3) ! this->rd_deflated(fh.rsv1))
{ {
// reserved bits not cleared // reserved bits not cleared
return err(close_code::protocol_error); return err(close_code::protocol_error);
} }
if(pmd_)
pmd_->rd_set = fh.rsv1;
break; break;
case detail::opcode::cont: case detail::opcode::cont:
@ -411,7 +495,7 @@ parse_fh(
std::uint64_t>::max)() - fh.len) std::uint64_t>::max)() - fh.len)
return err(close_code::too_big); return err(close_code::too_big);
} }
if(! pmd_ || ! pmd_->rd_set) if(! this->rd_deflated())
{ {
if(rd_msg_max_ && beast::detail::sum_exceeds( if(rd_msg_max_ && beast::detail::sum_exceeds(
rd_size_, fh.len, rd_msg_max_)) rd_size_, fh.len, rd_msg_max_))
@ -425,10 +509,10 @@ parse_fh(
return true; return true;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer> template<class DynamicBuffer>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
write_close(DynamicBuffer& db, close_reason const& cr) write_close(DynamicBuffer& db, close_reason const& cr)
{ {
using namespace boost::endian; using namespace boost::endian;
@ -479,10 +563,10 @@ write_close(DynamicBuffer& db, close_reason const& cr)
} }
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer> template<class DynamicBuffer>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
write_ping(DynamicBuffer& db, write_ping(DynamicBuffer& db,
detail::opcode code, ping_data const& data) detail::opcode code, ping_data const& data)
{ {
@ -513,14 +597,13 @@ write_ping(DynamicBuffer& db,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Decorator> template<class Decorator>
request_type request_type
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
build_request(detail::sec_ws_key_type& key, build_request(detail::sec_ws_key_type& key,
string_view host, string_view host, string_view target,
string_view target, Decorator const& decorator)
Decorator const& decorator)
{ {
request_type req; request_type req;
req.target(target); req.target(target);
@ -532,20 +615,7 @@ build_request(detail::sec_ws_key_type& key,
detail::make_sec_ws_key(key, wr_gen_); detail::make_sec_ws_key(key, wr_gen_);
req.set(http::field::sec_websocket_key, key); req.set(http::field::sec_websocket_key, key);
req.set(http::field::sec_websocket_version, "13"); req.set(http::field::sec_websocket_version, "13");
if(pmd_opts_.client_enable) build_request_pmd(req, is_deflate_supported{});
{
detail::pmd_offer config;
config.accept = true;
config.server_max_window_bits =
pmd_opts_.server_max_window_bits;
config.client_max_window_bits =
pmd_opts_.client_max_window_bits;
config.server_no_context_takeover =
pmd_opts_.server_no_context_takeover;
config.client_no_context_takeover =
pmd_opts_.client_no_context_takeover;
detail::pmd_write(req, config);
}
decorator(req); decorator(req);
if(! req.count(http::field::user_agent)) if(! req.count(http::field::user_agent))
req.set(http::field::user_agent, req.set(http::field::user_agent,
@ -553,13 +623,36 @@ build_request(detail::sec_ws_key_type& key,
return req; return req;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
inline
void
stream<NextLayer, deflateSupported>::
build_request_pmd(request_type& req, std::true_type)
{
if(this->pmd_opts_.client_enable)
{
detail::pmd_offer config;
config.accept = true;
config.server_max_window_bits =
this->pmd_opts_.server_max_window_bits;
config.client_max_window_bits =
this->pmd_opts_.client_max_window_bits;
config.server_no_context_takeover =
this->pmd_opts_.server_no_context_takeover;
config.client_no_context_takeover =
this->pmd_opts_.client_no_context_takeover;
detail::pmd_write(req, config);
}
}
template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator, class Decorator> template<class Body, class Allocator, class Decorator>
response_type response_type
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
build_response(http::request<Body, build_response(
http::basic_fields<Allocator>> const& req, http::request<Body,
Decorator const& decorator) http::basic_fields<Allocator>> const& req,
Decorator const& decorator)
{ {
auto const decorate = auto const decorate =
[&decorator](response_type& res) [&decorator](response_type& res)
@ -614,12 +707,7 @@ build_response(http::request<Body,
} }
response_type res; response_type res;
{ build_response_pmd(res, req, is_deflate_supported{});
detail::pmd_offer offer;
detail::pmd_offer unused;
pmd_read(offer, req);
pmd_negotiate(res, unused, offer, pmd_opts_);
}
res.result(http::status::switching_protocols); res.result(http::status::switching_protocols);
res.version(req.version()); res.version(req.version());
res.set(http::field::upgrade, "websocket"); res.set(http::field::upgrade, "websocket");
@ -633,11 +721,31 @@ build_response(http::request<Body,
return res; return res;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator>
inline
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
on_response(response_type const& res, build_response_pmd(
detail::sec_ws_key_type const& key, error_code& ec) response_type& res,
http::request<Body,
http::basic_fields<Allocator>> const& req,
std::true_type)
{
detail::pmd_offer offer;
detail::pmd_offer unused;
pmd_read(offer, req);
pmd_negotiate(res, unused, offer, this->pmd_opts_);
}
// Called when the WebSocket Upgrade response is received
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer, deflateSupported>::
on_response(
response_type const& res,
detail::sec_ws_key_type const& key,
error_code& ec)
{ {
bool const success = [&]() bool const success = [&]()
{ {
@ -664,18 +772,29 @@ on_response(response_type const& res,
return; return;
} }
ec.assign(0, ec.category()); ec.assign(0, ec.category());
on_response_pmd(res, is_deflate_supported{});
open(role_type::client);
}
template<class NextLayer, bool deflateSupported>
inline
void
stream<NextLayer, deflateSupported>::
on_response_pmd(
response_type const& res,
std::true_type)
{
detail::pmd_offer offer; detail::pmd_offer offer;
pmd_read(offer, res); pmd_read(offer, res);
// VFALCO see if offer satisfies pmd_config_, // VFALCO see if offer satisfies pmd_config_,
// return an error if not. // return an error if not.
pmd_config_ = offer; // overwrite for now this->pmd_config_ = offer; // overwrite for now
open(role_type::client);
} }
// _Fail the WebSocket Connection_ // _Fail the WebSocket Connection_
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
do_fail( do_fail(
std::uint16_t code, // if set, send a close frame first std::uint16_t code, // if set, send a close frame first
error_code ev, // error code to use upon success error_code ev, // error code to use upon success

View File

@ -33,13 +33,113 @@ namespace boost {
namespace beast { namespace beast {
namespace websocket { namespace websocket {
template<class NextLayer> namespace detail {
// Compress a buffer sequence
// Returns: `true` if more calls are needed
//
template<>
template<class ConstBufferSequence>
bool
stream_base<true>::
deflate(
boost::asio::mutable_buffer& out,
buffers_suffix<ConstBufferSequence>& cb,
bool fin,
std::size_t& total_in,
error_code& ec)
{
using boost::asio::buffer;
BOOST_ASSERT(out.size() >= 6);
auto& zo = this->pmd_->zo;
zlib::z_params zs;
zs.avail_in = 0;
zs.next_in = nullptr;
zs.avail_out = out.size();
zs.next_out = out.data();
for(auto in : beast::detail::buffers_range(cb))
{
zs.avail_in = in.size();
if(zs.avail_in == 0)
continue;
zs.next_in = in.data();
zo.write(zs, zlib::Flush::none, ec);
if(ec)
{
if(ec != zlib::error::need_buffers)
return false;
BOOST_ASSERT(zs.avail_out == 0);
BOOST_ASSERT(zs.total_out == out.size());
ec.assign(0, ec.category());
break;
}
if(zs.avail_out == 0)
{
BOOST_ASSERT(zs.total_out == out.size());
break;
}
BOOST_ASSERT(zs.avail_in == 0);
}
total_in = zs.total_in;
cb.consume(zs.total_in);
if(zs.avail_out > 0 && fin)
{
auto const remain = boost::asio::buffer_size(cb);
if(remain == 0)
{
// Inspired by Mark Adler
// https://github.com/madler/zlib/issues/149
//
// VFALCO We could do this flush twice depending
// on how much space is in the output.
zo.write(zs, zlib::Flush::block, ec);
BOOST_ASSERT(! ec || ec == zlib::error::need_buffers);
if(ec == zlib::error::need_buffers)
ec.assign(0, ec.category());
if(ec)
return false;
if(zs.avail_out >= 6)
{
zo.write(zs, zlib::Flush::full, ec);
BOOST_ASSERT(! ec);
// remove flush marker
zs.total_out -= 4;
out = buffer(out.data(), zs.total_out);
return false;
}
}
}
ec.assign(0, ec.category());
out = buffer(out.data(), zs.total_out);
return true;
}
template<>
inline
void
stream_base<true>::
do_context_takeover_write(role_type role)
{
if((role == role_type::client &&
this->pmd_config_.client_no_context_takeover) ||
(role == role_type::server &&
this->pmd_config_.server_no_context_takeover))
{
this->pmd_->zo.reset();
}
}
} // detail
//------------------------------------------------------------------------------
template<class NextLayer, bool deflateSupported>
template<class Buffers, class Handler> template<class Buffers, class Handler>
class stream<NextLayer>::write_some_op class stream<NextLayer, deflateSupported>::write_some_op
: public boost::asio::coroutine : public boost::asio::coroutine
{ {
Handler h_; Handler h_;
stream<NextLayer>& ws_; stream<NextLayer, deflateSupported>& ws_;
buffers_suffix<Buffers> cb_; buffers_suffix<Buffers> cb_;
detail::frame_header fh_; detail::frame_header fh_;
detail::prepared_key key_; detail::prepared_key key_;
@ -59,7 +159,7 @@ public:
template<class DeducedHandler> template<class DeducedHandler>
write_some_op( write_some_op(
DeducedHandler&& h, DeducedHandler&& h,
stream<NextLayer>& ws, stream<NextLayer, deflateSupported>& ws,
bool fin, bool fin,
Buffers const& bs) Buffers const& bs)
: h_(std::forward<DeducedHandler>(h)) : h_(std::forward<DeducedHandler>(h))
@ -80,7 +180,7 @@ public:
} }
using executor_type = boost::asio::associated_executor_t< using executor_type = boost::asio::associated_executor_t<
Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>; Handler, decltype(std::declval<stream<NextLayer, deflateSupported>&>().get_executor())>;
executor_type executor_type
get_executor() const noexcept get_executor() const noexcept
@ -109,10 +209,10 @@ public:
} }
}; };
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class Buffers, class Handler> template<class Buffers, class Handler>
void void
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
write_some_op<Buffers, Handler>:: write_some_op<Buffers, Handler>::
operator()( operator()(
error_code ec, error_code ec,
@ -397,8 +497,7 @@ operator()(
{ {
b = buffer(ws_.wr_buf_.get(), b = buffer(ws_.wr_buf_.get(),
ws_.wr_buf_size_); ws_.wr_buf_size_);
more_ = detail::deflate(ws_.pmd_->zo, more_ = ws_.deflate(b, cb_, fin_, in_, ec);
b, cb_, fin_, in_, ec);
if(! ws_.check_ok(ec)) if(! ws_.check_ok(ec))
goto upcall; goto upcall;
n = buffer_size(b); n = buffer_size(b);
@ -450,12 +549,8 @@ operator()(
} }
else else
{ {
if(fh_.fin && ( if(fh_.fin)
(ws_.role_ == role_type::client && ws_.do_context_takeover_write(ws_.role_);
ws_.pmd_config_.client_no_context_takeover) ||
(ws_.role_ == role_type::server &&
ws_.pmd_config_.server_no_context_takeover)))
ws_.pmd_->zo.reset();
goto upcall; goto upcall;
} }
} }
@ -479,10 +574,10 @@ operator()(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
write_some(bool fin, ConstBufferSequence const& buffers) write_some(bool fin, ConstBufferSequence const& buffers)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -498,10 +593,10 @@ write_some(bool fin, ConstBufferSequence const& buffers)
return bytes_transferred; return bytes_transferred;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
write_some(bool fin, write_some(bool fin,
ConstBufferSequence const& buffers, error_code& ec) ConstBufferSequence const& buffers, error_code& ec)
{ {
@ -543,9 +638,8 @@ write_some(bool fin,
{ {
auto b = buffer( auto b = buffer(
wr_buf_.get(), wr_buf_size_); wr_buf_.get(), wr_buf_size_);
auto const more = detail::deflate( auto const more = this->deflate(
pmd_->zo, b, cb, fin, b, cb, fin, bytes_transferred, ec);
bytes_transferred, ec);
if(! check_ok(ec)) if(! check_ok(ec))
return bytes_transferred; return bytes_transferred;
auto const n = buffer_size(b); auto const n = buffer_size(b);
@ -581,12 +675,8 @@ write_some(bool fin,
fh.op = detail::opcode::cont; fh.op = detail::opcode::cont;
fh.rsv1 = false; fh.rsv1 = false;
} }
if(fh.fin && ( if(fh.fin)
(role_ == role_type::client && this->do_context_takeover_write(role_);
pmd_config_.client_no_context_takeover) ||
(role_ == role_type::server &&
pmd_config_.server_no_context_takeover)))
pmd_->zo.reset();
} }
else if(! fh.mask) else if(! fh.mask)
{ {
@ -711,11 +801,11 @@ write_some(bool fin,
return bytes_transferred; return bytes_transferred;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence, class WriteHandler> template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code, std::size_t)) WriteHandler, void(error_code, std::size_t))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_write_some(bool fin, async_write_some(bool fin,
ConstBufferSequence const& bs, WriteHandler&& handler) ConstBufferSequence const& bs, WriteHandler&& handler)
{ {
@ -735,10 +825,10 @@ async_write_some(bool fin,
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
write(ConstBufferSequence const& buffers) write(ConstBufferSequence const& buffers)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -753,10 +843,10 @@ write(ConstBufferSequence const& buffers)
return bytes_transferred; return bytes_transferred;
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
write(ConstBufferSequence const& buffers, error_code& ec) write(ConstBufferSequence const& buffers, error_code& ec)
{ {
static_assert(is_sync_stream<next_layer_type>::value, static_assert(is_sync_stream<next_layer_type>::value,
@ -767,11 +857,11 @@ write(ConstBufferSequence const& buffers, error_code& ec)
return write_some(true, buffers, ec); return write_some(true, buffers, ec);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence, class WriteHandler> template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code, std::size_t)) WriteHandler, void(error_code, std::size_t))
stream<NextLayer>:: stream<NextLayer, deflateSupported>::
async_write( async_write(
ConstBufferSequence const& bs, WriteHandler&& handler) ConstBufferSequence const& bs, WriteHandler&& handler)
{ {

View File

@ -21,6 +21,7 @@
#include <boost/beast/websocket/detail/mask.hpp> #include <boost/beast/websocket/detail/mask.hpp>
#include <boost/beast/websocket/detail/pausation.hpp> #include <boost/beast/websocket/detail/pausation.hpp>
#include <boost/beast/websocket/detail/pmd_extension.hpp> #include <boost/beast/websocket/detail/pmd_extension.hpp>
#include <boost/beast/websocket/detail/stream_base.hpp>
#include <boost/beast/websocket/detail/utf8_checker.hpp> #include <boost/beast/websocket/detail/utf8_checker.hpp>
#include <boost/beast/core/static_buffer.hpp> #include <boost/beast/core/static_buffer.hpp>
#include <boost/beast/core/string.hpp> #include <boost/beast/core/string.hpp>
@ -29,8 +30,6 @@
#include <boost/beast/http/message.hpp> #include <boost/beast/http/message.hpp>
#include <boost/beast/http/string_body.hpp> #include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/detail/type_traits.hpp> #include <boost/beast/http/detail/type_traits.hpp>
#include <boost/beast/zlib/deflate_stream.hpp>
#include <boost/beast/zlib/inflate_stream.hpp>
#include <boost/asio/async_result.hpp> #include <boost/asio/async_result.hpp>
#include <boost/asio/error.hpp> #include <boost/asio/error.hpp>
#include <algorithm> #include <algorithm>
@ -108,6 +107,12 @@ class frame_test;
For asynchronous operations, the type must support the For asynchronous operations, the type must support the
@b AsyncStream concept. @b AsyncStream concept.
@tparam deflateSupported A `bool` indicating whether or not the
stream will be capable of negotiating the permessage-deflate websocket
extension. Note that even if this is set to `true`, the permessage
deflate options (set by the caller at runtime) must still have the
feature enabled for a successful negotiation to occur.
@note A stream object must not be moved or destroyed while there @note A stream object must not be moved or destroyed while there
are pending asynchronous operations associated with it. are pending asynchronous operations associated with it.
@ -116,8 +121,13 @@ class frame_test;
@b DynamicBuffer, @b DynamicBuffer,
@b SyncStream @b SyncStream
*/ */
template<class NextLayer> template<
class NextLayer,
bool deflateSupported>
class stream class stream
#ifndef BOOST_BEAST_DOXYGEN
: private detail::stream_base<deflateSupported>
#endif
{ {
friend class close_test; friend class close_test;
friend class frame_test; friend class frame_test;
@ -126,7 +136,7 @@ class stream
friend class read2_test; friend class read2_test;
friend class stream_test; friend class stream_test;
friend class write_test; friend class write_test;
/* The read buffer has to be at least as large /* The read buffer has to be at least as large
as the largest possible control frame including as the largest possible control frame including
the frame header. the frame header.
@ -134,8 +144,6 @@ class stream
static std::size_t constexpr max_control_frame_size = 2 + 8 + 4 + 125; static std::size_t constexpr max_control_frame_size = 2 + 8 + 4 + 125;
static std::size_t constexpr tcp_frame_size = 1536; static std::size_t constexpr tcp_frame_size = 1536;
struct op {};
using control_cb_type = using control_cb_type =
std::function<void(frame_type, string_view)>; std::function<void(frame_type, string_view)>;
@ -154,16 +162,6 @@ class stream
void reset() { id_ = 0; } void reset() { id_ = 0; }
}; };
// State information for the permessage-deflate extension
struct pmd_t
{
// `true` if current read message is compressed
bool rd_set = false;
zlib::deflate_stream zo;
zlib::inflate_stream zi;
};
enum class status enum class status
{ {
open, open,
@ -231,14 +229,14 @@ class stream
detail::pausation paused_wr_; // paused write op detail::pausation paused_wr_; // paused write op
detail::pausation paused_ping_; // paused ping op detail::pausation paused_ping_; // paused ping op
detail::pausation paused_close_; // paused close op detail::pausation paused_close_; // paused close op
detail::pausation paused_r_rd_; // paused read op (read) detail::pausation paused_r_rd_; // paused read op (async read)
detail::pausation paused_r_close_;// paused close op (read) detail::pausation paused_r_close_;// paused close op (async read)
std::unique_ptr<pmd_t> pmd_; // pmd settings or nullptr
permessage_deflate pmd_opts_; // local pmd options
detail::pmd_offer pmd_config_; // offer (client) or negotiation (server)
public: public:
/// Indicates if the permessage-deflate extension is supported
using is_deflate_supported =
std::integral_constant<bool, deflateSupported>;
/// The type of the next layer. /// The type of the next layer.
using next_layer_type = using next_layer_type =
typename std::remove_reference<NextLayer>::type; typename std::remove_reference<NextLayer>::type;
@ -444,7 +442,11 @@ public:
*/ */
std::size_t std::size_t
read_size_hint( read_size_hint(
std::size_t initial_size = +tcp_frame_size) const; std::size_t initial_size = +tcp_frame_size) const
{
return read_size_hint(initial_size,
is_deflate_supported{});
}
/** Returns a suggested maximum buffer size for the next call to read. /** Returns a suggested maximum buffer size for the next call to read.
@ -475,15 +477,22 @@ public:
// //
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Set the permessage-deflate extension options /** Set the permessage-deflate extension options
@throws invalid_argument if `deflateSupported == false`, and either
`client_enable` or `server_enable` is `true`.
*/
void void
set_option(permessage_deflate const& o); set_option(permessage_deflate const& o)
{
set_option(o, is_deflate_supported{});
}
/// Get the permessage-deflate extension options /// Get the permessage-deflate extension options
void void
get_option(permessage_deflate& o) get_option(permessage_deflate& o)
{ {
o = pmd_opts_; get_option(o, is_deflate_supported{});
} }
/** Set the automatic fragmentation option. /** Set the automatic fragmentation option.
@ -2353,8 +2362,8 @@ public:
next layer's `async_write_some` functions, and is known as a next layer's `async_write_some` functions, and is known as a
<em>composed operation</em>. The program must ensure that the <em>composed operation</em>. The program must ensure that the
stream performs no other write operations (such as @ref async_ping, stream performs no other write operations (such as @ref async_ping,
@ref stream::async_write, @ref stream::async_write_some, or @ref async_write, @ref async_write_some, or @ref async_close)
@ref stream::async_close) until this operation completes. until this operation completes.
If the close reason specifies a close code other than If the close reason specifies a close code other than
@ref beast::websocket::close_code::none, the close frame is @ref beast::websocket::close_code::none, the close frame is
@ -3172,8 +3181,8 @@ public:
to the next layer's `async_write_some` functions, and is known to the next layer's `async_write_some` functions, and is known
as a <em>composed operation</em>. The program must ensure that as a <em>composed operation</em>. The program must ensure that
the stream performs no other write operations (such as the stream performs no other write operations (such as
stream::async_write, stream::async_write_some, or @ref async_write, @ref async_write_some, or
stream::async_close). @ref async_close).
The current setting of the @ref binary option controls The current setting of the @ref binary option controls
whether the message opcode is set to text or binary. If the whether the message opcode is set to text or binary. If the
@ -3301,8 +3310,8 @@ public:
as a <em>composed operation</em>. The actual payload sent as a <em>composed operation</em>. The actual payload sent
may be transformed as per the WebSocket protocol settings. The may be transformed as per the WebSocket protocol settings. The
program must ensure that the stream performs no other write program must ensure that the stream performs no other write
operations (such as stream::async_write, stream::async_write_some, operations (such as @ref async_write, @ref async_write_some,
or stream::async_close). or @ref async_close).
If this is the beginning of a new message, the message opcode If this is the beginning of a new message, the message opcode
will be set to text or binary as per the current setting of will be set to text or binary as per the current setting of
@ -3351,10 +3360,65 @@ private:
static void default_decorate_req(request_type&) {} static void default_decorate_req(request_type&) {}
static void default_decorate_res(response_type&) {} static void default_decorate_res(response_type&) {}
void
set_option(permessage_deflate const& o, std::true_type);
void
set_option(permessage_deflate const&, std::false_type);
void
get_option(permessage_deflate& o, std::true_type)
{
o = this->pmd_opts_;
}
void
get_option(permessage_deflate& o, std::false_type)
{
o = {};
o.client_enable = false;
o.server_enable = false;
}
void open(role_type role); void open(role_type role);
void open_pmd(std::true_type);
void open_pmd(std::false_type)
{
}
void close(); void close();
void close_pmd(std::true_type)
{
this->pmd_.reset();
}
void close_pmd(std::false_type)
{
}
void reset(); void reset();
void begin_msg();
void begin_msg()
{
begin_msg(is_deflate_supported{});
}
void begin_msg(std::true_type);
void begin_msg(std::false_type);
std::size_t
read_size_hint(
std::size_t initial_size,
std::true_type) const;
std::size_t
read_size_hint(
std::size_t initial_size,
std::false_type) const;
bool bool
check_open(error_code& ec) check_open(error_code& ec)
@ -3394,6 +3458,10 @@ private:
write_ping(DynamicBuffer& b, write_ping(DynamicBuffer& b,
detail::opcode op, ping_data const& data); detail::opcode op, ping_data const& data);
//
// upgrade
//
template<class Decorator> template<class Decorator>
request_type request_type
build_request(detail::sec_ws_key_type& key, build_request(detail::sec_ws_key_type& key,
@ -3401,28 +3469,94 @@ private:
string_view target, string_view target,
Decorator const& decorator); Decorator const& decorator);
template<class Body, void
class Allocator, class Decorator> build_request_pmd(request_type& req, std::true_type);
response_type
build_response(http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator);
void void
on_response(response_type const& resp, build_request_pmd(request_type&, std::false_type)
detail::sec_ws_key_type const& key, error_code& ec); {
}
template<
class Body, class Allocator, class Decorator>
response_type
build_response(
http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator);
template<class Body, class Allocator>
void
build_response_pmd(
response_type& res,
http::request<Body,
http::basic_fields<Allocator>> const& req,
std::true_type);
template<class Body, class Allocator>
void
build_response_pmd(
response_type&,
http::request<Body,
http::basic_fields<Allocator>> const&,
std::false_type)
{
}
void
on_response(
response_type const& res,
detail::sec_ws_key_type const& key,
error_code& ec);
void
on_response_pmd(
response_type const& res,
std::true_type);
void
on_response_pmd(
response_type const&,
std::false_type)
{
}
//
// accept / handshake
//
template<class Allocator>
void
do_pmd_config(
http::basic_fields<Allocator> const& h,
std::true_type)
{
pmd_read(this->pmd_config_, h);
}
template<class Allocator>
void
do_pmd_config(
http::basic_fields<Allocator> const&,
std::false_type)
{
}
template<class Decorator> template<class Decorator>
void void
do_accept(Decorator const& decorator, do_accept(
Decorator const& decorator,
error_code& ec); error_code& ec);
template<class Body, class Allocator, template<
class Body, class Allocator,
class Decorator> class Decorator>
void void
do_accept(http::request<Body, do_accept(
http::basic_fields<Allocator>> const& req, http::request<Body,
Decorator const& decorator, error_code& ec); http::basic_fields<Allocator>> const& req,
Decorator const& decorator,
error_code& ec);
template<class RequestDecorator> template<class RequestDecorator>
void void
@ -3431,6 +3565,10 @@ private:
RequestDecorator const& decorator, RequestDecorator const& decorator,
error_code& ec); error_code& ec);
//
// fail
//
void void
do_fail( do_fail(
std::uint16_t code, std::uint16_t code,

View File

@ -17,7 +17,8 @@ namespace beast {
namespace websocket { namespace websocket {
template< template<
class NextLayer> class NextLayer,
bool deflateSupported = true>
class stream; class stream;
} // websocket } // websocket

View File

@ -23,11 +23,11 @@ namespace websocket {
class read1_test : public websocket_test_suite class read1_test : public websocket_test_suite
{ {
public: public:
template<class Wrap> template<class Wrap, bool deflateSupported>
void void
doReadTest( doReadTest(
Wrap const& w, Wrap const& w,
ws_type& ws, ws_type_t<deflateSupported>& ws,
close_code code) close_code code)
{ {
try try
@ -45,11 +45,11 @@ public:
} }
} }
template<class Wrap> template<class Wrap, bool deflateSupported>
void void
doFailTest( doFailTest(
Wrap const& w, Wrap const& w,
ws_type& ws, ws_type_t<deflateSupported>& ws,
error_code ev) error_code ev)
{ {
try try
@ -65,7 +65,7 @@ public:
} }
} }
template<class Wrap> template<bool deflateSupported = true, class Wrap>
void void
doTestRead(Wrap const& w) doTestRead(Wrap const& w)
{ {
@ -78,7 +78,7 @@ public:
// already closed // already closed
{ {
echo_server es{log}; echo_server es{log};
stream<test::stream> ws{ioc_}; stream<test::stream, deflateSupported> ws{ioc_};
ws.next_layer().connect(es.stream()); ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/"); ws.handshake("localhost", "/");
ws.close({}); ws.close({});
@ -97,7 +97,8 @@ public:
} }
// empty, fragmented message // empty, fragmented message
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
ws.next_layer().append( ws.next_layer().append(
string_view( string_view(
@ -109,7 +110,8 @@ public:
// two part message // two part message
// triggers "fill the read buffer first" // triggers "fill the read buffer first"
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
w.write_raw(ws, sbuf( w.write_raw(ws, sbuf(
"\x01\x81\xff\xff\xff\xff")); "\x01\x81\xff\xff\xff\xff"));
@ -123,7 +125,8 @@ public:
}); });
// ping // ping
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
put(ws.next_layer().buffer(), cbuf( put(ws.next_layer().buffer(), cbuf(
0x89, 0x00)); 0x89, 0x00));
@ -144,7 +147,8 @@ public:
}); });
// ping // ping
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
put(ws.next_layer().buffer(), cbuf( put(ws.next_layer().buffer(), cbuf(
0x88, 0x00)); 0x88, 0x00));
@ -161,7 +165,8 @@ public:
}); });
// ping then message // ping then message
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
bool once = false; bool once = false;
ws.control_callback( ws.control_callback(
@ -183,7 +188,8 @@ public:
}); });
// ping then fragmented message // ping then fragmented message
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
bool once = false; bool once = false;
ws.control_callback( ws.control_callback(
@ -208,7 +214,7 @@ public:
doStreamLoop([&](test::stream& ts) doStreamLoop([&](test::stream& ts)
{ {
echo_server es{log, kind::async_client}; echo_server es{log, kind::async_client};
ws_type ws{ts}; ws_type_t<deflateSupported> ws{ts};
ws.next_layer().connect(es.stream()); ws.next_layer().connect(es.stream());
ws.set_option(pmd); ws.set_option(pmd);
es.async_handshake(); es.async_handshake();
@ -237,7 +243,7 @@ public:
{ {
echo_server es{log, kind::async}; echo_server es{log, kind::async};
boost::asio::io_context ioc; boost::asio::io_context ioc;
stream<test::stream> ws{ioc, fc}; stream<test::stream, deflateSupported> ws{ioc, fc};
ws.next_layer().connect(es.stream()); ws.next_layer().connect(es.stream());
ws.handshake("localhost", "/"); ws.handshake("localhost", "/");
// Cause close to be received // Cause close to be received
@ -257,7 +263,8 @@ public:
}); });
// already closed // already closed
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
w.close(ws, {}); w.close(ws, {});
multi_buffer b; multi_buffer b;
@ -266,7 +273,8 @@ public:
}); });
// buffer overflow // buffer overflow
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
std::string const s = "Hello, world!"; std::string const s = "Hello, world!";
ws.auto_fragment(false); ws.auto_fragment(false);
@ -286,7 +294,8 @@ public:
}); });
// bad utf8, big // bad utf8, big
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
auto const s = std::string(2000, '*') + auto const s = std::string(2000, '*') +
random_string(); random_string();
@ -296,7 +305,8 @@ public:
}); });
// invalid fixed frame header // invalid fixed frame header
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
w.write_raw(ws, cbuf( w.write_raw(ws, cbuf(
0x8f, 0x80, 0xff, 0xff, 0xff, 0xff)); 0x8f, 0x80, 0xff, 0xff, 0xff, 0xff));
@ -304,7 +314,8 @@ public:
}); });
// bad close // bad close
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
put(ws.next_layer().buffer(), cbuf( put(ws.next_layer().buffer(), cbuf(
0x88, 0x02, 0x03, 0xed)); 0x88, 0x02, 0x03, 0xed));
@ -312,7 +323,8 @@ public:
}); });
// message size above 2^64 // message size above 2^64
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
w.write_some(ws, false, sbuf("*")); w.write_some(ws, false, sbuf("*"));
w.write_raw(ws, cbuf( w.write_raw(ws, cbuf(
@ -322,7 +334,8 @@ public:
}); });
// message size exceeds max // message size exceeds max
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
ws.read_message_max(1); ws.read_message_max(1);
w.write(ws, sbuf("**")); w.write(ws, sbuf("**"));
@ -330,7 +343,8 @@ public:
}); });
// bad utf8 // bad utf8
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
put(ws.next_layer().buffer(), cbuf( put(ws.next_layer().buffer(), cbuf(
0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc)); 0x81, 0x06, 0x03, 0xea, 0xf0, 0x28, 0x8c, 0xbc));
@ -338,7 +352,8 @@ public:
}); });
// incomplete utf8 // incomplete utf8
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
std::string const s = std::string const s =
"Hello, world!" "\xc0"; "Hello, world!" "\xc0";
@ -347,7 +362,8 @@ public:
}); });
// incomplete utf8, big // incomplete utf8, big
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
std::string const s = std::string const s =
"\x81\x7e\x0f\xa1" + "\x81\x7e\x0f\xa1" +
@ -375,7 +391,7 @@ public:
[&](error_code ev, string_view s) [&](error_code ev, string_view s)
{ {
echo_server es{log}; echo_server es{log};
stream<test::stream> ws{ioc_}; stream<test::stream, deflateSupported> ws{ioc_};
ws.next_layer().connect(es.stream()); ws.next_layer().connect(es.stream());
w.handshake(ws, "localhost", "/"); w.handshake(ws, "localhost", "/");
ws.next_layer().append(s); ws.next_layer().append(s);
@ -410,11 +426,15 @@ public:
check(error::closed, check(error::closed,
"\x88\x06\xfc\x15utf8"); "\x88\x06\xfc\x15utf8");
} }
}
// template<class Wrap>
// permessage-deflate void
// doTestReadDeflate(Wrap const& w)
{
using boost::asio::buffer;
permessage_deflate pmd;
pmd.client_enable = true; pmd.client_enable = true;
pmd.server_enable = true; pmd.server_enable = true;
pmd.client_max_window_bits = 9; pmd.client_max_window_bits = 9;
@ -422,7 +442,8 @@ public:
pmd.compLevel = 1; pmd.compLevel = 1;
// message size limit // message size limit
doTest(pmd, [&](ws_type& ws) doTest<true>(pmd,
[&](ws_type_t<true>& ws)
{ {
std::string const s = std::string(128, '*'); std::string const s = std::string(128, '*');
w.write(ws, buffer(s)); w.write(ws, buffer(s));
@ -431,7 +452,8 @@ public:
}); });
// invalid inflate block // invalid inflate block
doTest(pmd, [&](ws_type& ws) doTest<true>(pmd,
[&](ws_type_t<true>& ws)
{ {
auto const& s = random_string(); auto const& s = random_string();
ws.binary(true); ws.binary(true);
@ -458,7 +480,8 @@ public:
// no_context_takeover // no_context_takeover
pmd.server_no_context_takeover = true; pmd.server_no_context_takeover = true;
doTest(pmd, [&](ws_type& ws) doTest<true>(pmd,
[&](ws_type_t<true>& ws)
{ {
auto const& s = random_string(); auto const& s = random_string();
ws.binary(true); ws.binary(true);
@ -587,10 +610,14 @@ public:
{ {
using boost::asio::buffer; using boost::asio::buffer;
doTestRead(SyncClient{}); doTestRead<false>(SyncClient{});
doTestRead<true>(SyncClient{});
doTestReadDeflate(SyncClient{});
yield_to([&](yield_context yield) yield_to([&](yield_context yield)
{ {
doTestRead(AsyncClient{yield}); doTestRead<false>(AsyncClient{yield});
doTestRead<true>(AsyncClient{yield});
doTestReadDeflate(AsyncClient{yield});
}); });
permessage_deflate pmd; permessage_deflate pmd;

View File

@ -122,6 +122,8 @@ public:
BOOST_STATIC_ASSERT(! std::is_move_assignable< BOOST_STATIC_ASSERT(! std::is_move_assignable<
stream<test::stream&>>::value); stream<test::stream&>>::value);
log << "sizeof(websocket::stream_base<true>) == " <<
sizeof(websocket::detail::stream_base<true>) << std::endl;
log << "sizeof(websocket::stream) == " << log << "sizeof(websocket::stream) == " <<
sizeof(websocket::stream<test::stream&>) << std::endl; sizeof(websocket::stream<test::stream&>) << std::endl;

View File

@ -35,6 +35,10 @@ class websocket_test_suite
, public test::enable_yield_to , public test::enable_yield_to
{ {
public: public:
template<bool deflateSupported>
using ws_type_t =
websocket::stream<test::stream&, deflateSupported>;
using ws_type = using ws_type =
websocket::stream<test::stream&>; websocket::stream<test::stream&>;
@ -303,7 +307,7 @@ public:
, limit); , limit);
} }
template<class Test> template<bool deflateSupported = true, class Test>
void void
doTest( doTest(
permessage_deflate const& pmd, permessage_deflate const& pmd,
@ -320,7 +324,7 @@ public:
{ {
test::fail_counter fc{n}; test::fail_counter fc{n};
test::stream ts{ioc_, fc}; test::stream ts{ioc_, fc};
ws_type ws{ts}; ws_type_t<deflateSupported> ws{ts};
ws.set_option(pmd); ws.set_option(pmd);
echo_server es{log, i==1 ? echo_server es{log, i==1 ?
@ -481,146 +485,171 @@ public:
struct SyncClient struct SyncClient
{ {
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
accept(stream<NextLayer>& ws) const accept(
stream<NextLayer, deflateSupported>& ws) const
{ {
ws.accept(); ws.accept();
} }
template<class NextLayer, class Buffers> template<
class NextLayer, bool deflateSupported,
class Buffers>
typename std::enable_if< typename std::enable_if<
! http::detail::is_header<Buffers>::value>::type ! http::detail::is_header<Buffers>::value>::type
accept(stream<NextLayer>& ws, accept(stream<NextLayer, deflateSupported>& ws,
Buffers const& buffers) const Buffers const& buffers) const
{ {
ws.accept(buffers); ws.accept(buffers);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
accept(stream<NextLayer>& ws, accept(
stream<NextLayer, deflateSupported>& ws,
http::request<http::empty_body> const& req) const http::request<http::empty_body> const& req) const
{ {
ws.accept(req); ws.accept(req);
} }
template<class NextLayer, class Decorator> template<
class NextLayer, bool deflateSupported,
class Decorator>
void void
accept_ex(stream<NextLayer>& ws, accept_ex(
stream<NextLayer, deflateSupported>& ws,
Decorator const& d) const Decorator const& d) const
{ {
ws.accept_ex(d); ws.accept_ex(d);
} }
template<class NextLayer, template<
class NextLayer, bool deflateSupported,
class Buffers, class Decorator> class Buffers, class Decorator>
typename std::enable_if< typename std::enable_if<
! http::detail::is_header<Buffers>::value>::type ! http::detail::is_header<Buffers>::value>::type
accept_ex(stream<NextLayer>& ws, accept_ex(
stream<NextLayer, deflateSupported>& ws,
Buffers const& buffers, Buffers const& buffers,
Decorator const& d) const Decorator const& d) const
{ {
ws.accept_ex(buffers, d); ws.accept_ex(buffers, d);
} }
template<class NextLayer, class Decorator> template<
class NextLayer, bool deflateSupported,
class Decorator>
void void
accept_ex(stream<NextLayer>& ws, accept_ex(
stream<NextLayer, deflateSupported>& ws,
http::request<http::empty_body> const& req, http::request<http::empty_body> const& req,
Decorator const& d) const Decorator const& d) const
{ {
ws.accept_ex(req, d); ws.accept_ex(req, d);
} }
template<class NextLayer, template<
class NextLayer, bool deflateSupported,
class Buffers, class Decorator> class Buffers, class Decorator>
void void
accept_ex(stream<NextLayer>& ws, accept_ex(
stream<NextLayer, deflateSupported>& ws,
http::request<http::empty_body> const& req, http::request<http::empty_body> const& req,
Buffers const& buffers, Buffers const& buffers,
Decorator const& d) const Decorator const& d) const
{ {
ws.accept_ex(req, buffers, d); ws.accept_ex(req, buffers, d);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
handshake(stream<NextLayer>& ws, handshake(
stream<NextLayer, deflateSupported>& ws,
string_view uri, string_view uri,
string_view path) const string_view path) const
{ {
ws.handshake(uri, path); ws.handshake(uri, path);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
handshake(stream<NextLayer>& ws, handshake(
stream<NextLayer, deflateSupported>& ws,
response_type& res, response_type& res,
string_view uri, string_view uri,
string_view path) const string_view path) const
{ {
ws.handshake(res, uri, path); ws.handshake(res, uri, path);
} }
template<class NextLayer, class Decorator> template<
class NextLayer, bool deflateSupported,
class Decorator>
void void
handshake_ex(stream<NextLayer>& ws, handshake_ex(
stream<NextLayer, deflateSupported>& ws,
string_view uri, string_view uri,
string_view path, string_view path,
Decorator const& d) const Decorator const& d) const
{ {
ws.handshake_ex(uri, path, d); ws.handshake_ex(uri, path, d);
} }
template<class NextLayer, class Decorator> template<
class NextLayer, bool deflateSupported,
class Decorator>
void void
handshake_ex(stream<NextLayer>& ws, handshake_ex(
stream<NextLayer, deflateSupported>& ws,
response_type& res, response_type& res,
string_view uri, string_view uri,
string_view path, string_view path,
Decorator const& d) const Decorator const& d) const
{ {
ws.handshake_ex(res, uri, path, d); ws.handshake_ex(res, uri, path, d);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
ping(stream<NextLayer>& ws, ping(stream<NextLayer, deflateSupported>& ws,
ping_data const& payload) const ping_data const& payload) const
{ {
ws.ping(payload); ws.ping(payload);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
pong(stream<NextLayer>& ws, pong(stream<NextLayer, deflateSupported>& ws,
ping_data const& payload) const ping_data const& payload) const
{ {
ws.pong(payload); ws.pong(payload);
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
close(stream<NextLayer>& ws, close(stream<NextLayer, deflateSupported>& ws,
close_reason const& cr) const close_reason const& cr) const
{ {
ws.close(cr); ws.close(cr);
} }
template< template<
class NextLayer, class DynamicBuffer> class NextLayer, bool deflateSupported,
class DynamicBuffer>
std::size_t std::size_t
read(stream<NextLayer>& ws, read(stream<NextLayer, deflateSupported>& ws,
DynamicBuffer& buffer) const DynamicBuffer& buffer) const
{ {
return ws.read(buffer); return ws.read(buffer);
} }
template< template<
class NextLayer, class DynamicBuffer> class NextLayer, bool deflateSupported,
class DynamicBuffer>
std::size_t std::size_t
read_some(stream<NextLayer>& ws, read_some(
stream<NextLayer, deflateSupported>& ws,
std::size_t limit, std::size_t limit,
DynamicBuffer& buffer) const DynamicBuffer& buffer) const
{ {
@ -628,36 +657,45 @@ public:
} }
template< template<
class NextLayer, class MutableBufferSequence> class NextLayer, bool deflateSupported,
class MutableBufferSequence>
std::size_t std::size_t
read_some(stream<NextLayer>& ws, read_some(
stream<NextLayer, deflateSupported>& ws,
MutableBufferSequence const& buffers) const MutableBufferSequence const& buffers) const
{ {
return ws.read_some(buffers); return ws.read_some(buffers);
} }
template< template<
class NextLayer, class ConstBufferSequence> class NextLayer, bool deflateSupported,
class ConstBufferSequence>
std::size_t std::size_t
write(stream<NextLayer>& ws, write(
stream<NextLayer, deflateSupported>& ws,
ConstBufferSequence const& buffers) const ConstBufferSequence const& buffers) const
{ {
return ws.write(buffers); return ws.write(buffers);
} }
template< template<
class NextLayer, class ConstBufferSequence> class NextLayer, bool deflateSupported,
class ConstBufferSequence>
std::size_t std::size_t
write_some(stream<NextLayer>& ws, bool fin, write_some(
stream<NextLayer, deflateSupported>& ws,
bool fin,
ConstBufferSequence const& buffers) const ConstBufferSequence const& buffers) const
{ {
return ws.write_some(fin, buffers); return ws.write_some(fin, buffers);
} }
template< template<
class NextLayer, class ConstBufferSequence> class NextLayer, bool deflateSupported,
class ConstBufferSequence>
std::size_t std::size_t
write_raw(stream<NextLayer>& ws, write_raw(
stream<NextLayer, deflateSupported>& ws,
ConstBufferSequence const& buffers) const ConstBufferSequence const& buffers) const
{ {
return boost::asio::write( return boost::asio::write(
@ -678,9 +716,9 @@ public:
{ {
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
accept(stream<NextLayer>& ws) const accept(stream<NextLayer, deflateSupported>& ws) const
{ {
error_code ec; error_code ec;
ws.async_accept(yield_[ec]); ws.async_accept(yield_[ec]);
@ -688,10 +726,13 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer, class Buffers> template<
class NextLayer, bool deflateSupported,
class Buffers>
typename std::enable_if< typename std::enable_if<
! http::detail::is_header<Buffers>::value>::type ! http::detail::is_header<Buffers>::value>::type
accept(stream<NextLayer>& ws, accept(
stream<NextLayer, deflateSupported>& ws,
Buffers const& buffers) const Buffers const& buffers) const
{ {
error_code ec; error_code ec;
@ -700,9 +741,10 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
accept(stream<NextLayer>& ws, accept(
stream<NextLayer, deflateSupported>& ws,
http::request<http::empty_body> const& req) const http::request<http::empty_body> const& req) const
{ {
error_code ec; error_code ec;
@ -711,10 +753,12 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer, template<
class NextLayer, bool deflateSupported,
class Decorator> class Decorator>
void void
accept_ex(stream<NextLayer>& ws, accept_ex(
stream<NextLayer, deflateSupported>& ws,
Decorator const& d) const Decorator const& d) const
{ {
error_code ec; error_code ec;
@ -723,13 +767,15 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer, template<
class NextLayer, bool deflateSupported,
class Buffers, class Decorator> class Buffers, class Decorator>
typename std::enable_if< typename std::enable_if<
! http::detail::is_header<Buffers>::value>::type ! http::detail::is_header<Buffers>::value>::type
accept_ex(stream<NextLayer>& ws, accept_ex(
stream<NextLayer, deflateSupported>& ws,
Buffers const& buffers, Buffers const& buffers,
Decorator const& d) const Decorator const& d) const
{ {
error_code ec; error_code ec;
ws.async_accept_ex(buffers, d, yield_[ec]); ws.async_accept_ex(buffers, d, yield_[ec]);
@ -737,11 +783,14 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer, class Decorator> template<
class NextLayer, bool deflateSupported,
class Decorator>
void void
accept_ex(stream<NextLayer>& ws, accept_ex(
stream<NextLayer, deflateSupported>& ws,
http::request<http::empty_body> const& req, http::request<http::empty_body> const& req,
Decorator const& d) const Decorator const& d) const
{ {
error_code ec; error_code ec;
ws.async_accept_ex(req, d, yield_[ec]); ws.async_accept_ex(req, d, yield_[ec]);
@ -749,13 +798,15 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer, template<
class NextLayer, bool deflateSupported,
class Buffers, class Decorator> class Buffers, class Decorator>
void void
accept_ex(stream<NextLayer>& ws, accept_ex(
stream<NextLayer, deflateSupported>& ws,
http::request<http::empty_body> const& req, http::request<http::empty_body> const& req,
Buffers const& buffers, Buffers const& buffers,
Decorator const& d) const Decorator const& d) const
{ {
error_code ec; error_code ec;
ws.async_accept_ex( ws.async_accept_ex(
@ -764,11 +815,13 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer> template<
class NextLayer, bool deflateSupported>
void void
handshake(stream<NextLayer>& ws, handshake(
stream<NextLayer, deflateSupported>& ws,
string_view uri, string_view uri,
string_view path) const string_view path) const
{ {
error_code ec; error_code ec;
ws.async_handshake( ws.async_handshake(
@ -777,12 +830,13 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
handshake(stream<NextLayer>& ws, handshake(
stream<NextLayer, deflateSupported>& ws,
response_type& res, response_type& res,
string_view uri, string_view uri,
string_view path) const string_view path) const
{ {
error_code ec; error_code ec;
ws.async_handshake( ws.async_handshake(
@ -791,12 +845,15 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer, class Decorator> template<
class NextLayer, bool deflateSupported,
class Decorator>
void void
handshake_ex(stream<NextLayer>& ws, handshake_ex(
stream<NextLayer, deflateSupported>& ws,
string_view uri, string_view uri,
string_view path, string_view path,
Decorator const &d) const Decorator const &d) const
{ {
error_code ec; error_code ec;
ws.async_handshake_ex( ws.async_handshake_ex(
@ -805,13 +862,16 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer, class Decorator> template<
class NextLayer, bool deflateSupported,
class Decorator>
void void
handshake_ex(stream<NextLayer>& ws, handshake_ex(
stream<NextLayer, deflateSupported>& ws,
response_type& res, response_type& res,
string_view uri, string_view uri,
string_view path, string_view path,
Decorator const &d) const Decorator const &d) const
{ {
error_code ec; error_code ec;
ws.async_handshake_ex( ws.async_handshake_ex(
@ -820,9 +880,10 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
ping(stream<NextLayer>& ws, ping(
stream<NextLayer, deflateSupported>& ws,
ping_data const& payload) const ping_data const& payload) const
{ {
error_code ec; error_code ec;
@ -831,9 +892,10 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
pong(stream<NextLayer>& ws, pong(
stream<NextLayer, deflateSupported>& ws,
ping_data const& payload) const ping_data const& payload) const
{ {
error_code ec; error_code ec;
@ -842,9 +904,10 @@ public:
throw system_error{ec}; throw system_error{ec};
} }
template<class NextLayer> template<class NextLayer, bool deflateSupported>
void void
close(stream<NextLayer>& ws, close(
stream<NextLayer, deflateSupported>& ws,
close_reason const& cr) const close_reason const& cr) const
{ {
error_code ec; error_code ec;
@ -854,9 +917,11 @@ public:
} }
template< template<
class NextLayer, class DynamicBuffer> class NextLayer, bool deflateSupported,
class DynamicBuffer>
std::size_t std::size_t
read(stream<NextLayer>& ws, read(
stream<NextLayer, deflateSupported>& ws,
DynamicBuffer& buffer) const DynamicBuffer& buffer) const
{ {
error_code ec; error_code ec;
@ -868,9 +933,11 @@ public:
} }
template< template<
class NextLayer, class DynamicBuffer> class NextLayer, bool deflateSupported,
class DynamicBuffer>
std::size_t std::size_t
read_some(stream<NextLayer>& ws, read_some(
stream<NextLayer, deflateSupported>& ws,
std::size_t limit, std::size_t limit,
DynamicBuffer& buffer) const DynamicBuffer& buffer) const
{ {
@ -883,9 +950,11 @@ public:
} }
template< template<
class NextLayer, class MutableBufferSequence> class NextLayer, bool deflateSupported,
class MutableBufferSequence>
std::size_t std::size_t
read_some(stream<NextLayer>& ws, read_some(
stream<NextLayer, deflateSupported>& ws,
MutableBufferSequence const& buffers) const MutableBufferSequence const& buffers) const
{ {
error_code ec; error_code ec;
@ -897,9 +966,11 @@ public:
} }
template< template<
class NextLayer, class ConstBufferSequence> class NextLayer, bool deflateSupported,
class ConstBufferSequence>
std::size_t std::size_t
write(stream<NextLayer>& ws, write(
stream<NextLayer, deflateSupported>& ws,
ConstBufferSequence const& buffers) const ConstBufferSequence const& buffers) const
{ {
error_code ec; error_code ec;
@ -911,9 +982,12 @@ public:
} }
template< template<
class NextLayer, class ConstBufferSequence> class NextLayer, bool deflateSupported,
class ConstBufferSequence>
std::size_t std::size_t
write_some(stream<NextLayer>& ws, bool fin, write_some(
stream<NextLayer, deflateSupported>& ws,
bool fin,
ConstBufferSequence const& buffers) const ConstBufferSequence const& buffers) const
{ {
error_code ec; error_code ec;
@ -925,9 +999,11 @@ public:
} }
template< template<
class NextLayer, class ConstBufferSequence> class NextLayer, bool deflateSupported,
class ConstBufferSequence>
std::size_t std::size_t
write_raw(stream<NextLayer>& ws, write_raw(
stream<NextLayer, deflateSupported>& ws,
ConstBufferSequence const& buffers) const ConstBufferSequence const& buffers) const
{ {
error_code ec; error_code ec;

View File

@ -19,7 +19,7 @@ namespace websocket {
class write_test : public websocket_test_suite class write_test : public websocket_test_suite
{ {
public: public:
template<class Wrap> template<bool deflateSupported, class Wrap>
void void
doTestWrite(Wrap const& w) doTestWrite(Wrap const& w)
{ {
@ -50,7 +50,8 @@ public:
} }
// message // message
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
ws.auto_fragment(false); ws.auto_fragment(false);
ws.binary(false); ws.binary(false);
@ -63,7 +64,8 @@ public:
}); });
// empty message // empty message
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
ws.text(true); ws.text(true);
w.write(ws, boost::asio::const_buffer{}); w.write(ws, boost::asio::const_buffer{});
@ -74,7 +76,8 @@ public:
}); });
// fragmented message // fragmented message
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
ws.auto_fragment(false); ws.auto_fragment(false);
ws.binary(false); ws.binary(false);
@ -88,7 +91,8 @@ public:
}); });
// continuation // continuation
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
std::string const s = "Hello"; std::string const s = "Hello";
std::size_t const chop = 3; std::size_t const chop = 3;
@ -103,7 +107,8 @@ public:
}); });
// mask // mask
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
ws.auto_fragment(false); ws.auto_fragment(false);
std::string const s = "Hello"; std::string const s = "Hello";
@ -114,7 +119,8 @@ public:
}); });
// mask (large) // mask (large)
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
ws.auto_fragment(false); ws.auto_fragment(false);
ws.write_buffer_size(16); ws.write_buffer_size(16);
@ -126,7 +132,8 @@ public:
}); });
// mask, autofrag // mask, autofrag
doTest(pmd, [&](ws_type& ws) doTest<deflateSupported>(pmd,
[&](ws_type_t<deflateSupported>& ws)
{ {
ws.auto_fragment(true); ws.auto_fragment(true);
std::string const s(16384, '*'); std::string const s(16384, '*');
@ -140,7 +147,7 @@ public:
doStreamLoop([&](test::stream& ts) doStreamLoop([&](test::stream& ts)
{ {
echo_server es{log, kind::async_client}; echo_server es{log, kind::async_client};
ws_type ws{ts}; ws_type_t<deflateSupported> ws{ts};
ws.next_layer().connect(es.stream()); ws.next_layer().connect(es.stream());
try try
{ {
@ -166,7 +173,7 @@ public:
doStreamLoop([&](test::stream& ts) doStreamLoop([&](test::stream& ts)
{ {
echo_server es{log, kind::async_client}; echo_server es{log, kind::async_client};
ws_type ws{ts}; ws_type_t<deflateSupported> ws{ts};
ws.next_layer().connect(es.stream()); ws.next_layer().connect(es.stream());
try try
{ {
@ -187,7 +194,15 @@ public:
} }
ts.close(); ts.close();
}); });
}
template<class Wrap>
void
doTestWriteDeflate(Wrap const& w)
{
using boost::asio::buffer;
permessage_deflate pmd;
pmd.client_enable = true; pmd.client_enable = true;
pmd.server_enable = true; pmd.server_enable = true;
pmd.compLevel = 1; pmd.compLevel = 1;
@ -238,11 +253,15 @@ public:
{ {
using boost::asio::buffer; using boost::asio::buffer;
doTestWrite(SyncClient{}); doTestWrite<false>(SyncClient{});
doTestWrite<true>(SyncClient{});
doTestWriteDeflate(SyncClient{});
yield_to([&](yield_context yield) yield_to([&](yield_context yield)
{ {
doTestWrite(AsyncClient{yield}); doTestWrite<false>(AsyncClient{yield});
doTestWrite<true>(AsyncClient{yield});
doTestWriteDeflate(AsyncClient{yield});
}); });
} }

View File

@ -57,7 +57,7 @@ boost::asio::ip::tcp::socket sock{ioc};
{ {
//[ws_snippet_6 //[ws_snippet_6
std::string const host = "mywebapp.com"; std::string const host = "example.com";
boost::asio::ip::tcp::resolver r{ioc}; boost::asio::ip::tcp::resolver r{ioc};
stream<boost::asio::ip::tcp::socket> ws{ioc}; stream<boost::asio::ip::tcp::socket> ws{ioc};
auto const results = r.resolve(host, "ws"); auto const results = r.resolve(host, "ws");
@ -310,6 +310,16 @@ struct custom_wrapper
//] //]
//[ws_snippet_26
// A WebSocket stream
template<
class NextLayer,
bool deflateSupported = true>
class stream;
//]
} // doc_ws_snippets } // doc_ws_snippets
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------