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:
* Sanitizer failures are errors
@ -13,6 +15,7 @@ WebSocket:
API Changes:
* http::parser is not MoveConstructible
* permessage-deflate is a compile-time feature
--------------------------------------------------------------------------------

View File

@ -10,13 +10,27 @@
[section Creating Streams]
The interface to the WebSocket implementation is a single template class
[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"
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.
[link beast.ref.boost__beast__websocket__stream `stream`]:
[ws_snippet_26]
An instance of the stream wraps an existing network transport object
or other type of octet oriented stream. The wrapped object is called
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
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;
}
/** WebSocket frame header opcodes. */
// frame header opcodes
enum class opcode : std::uint8_t
{
cont = 0,
@ -110,8 +110,7 @@ struct frame_header
};
// holds the largest possible frame header
using fh_buffer =
flat_static_buffer<14>;
using fh_buffer = flat_static_buffer<14>;
// holds the largest possible control frame
using frame_buffer =

View File

@ -19,6 +19,7 @@
#include <boost/beast/http/rfc7230.hpp>
#include <boost/asio/buffer.hpp>
#include <utility>
#include <type_traits>
namespace boost {
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
} // websocket
} // 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 {
template<class F>
using is_RequestDecorator =
using is_request_decorator =
typename beast::detail::is_invocable<F,
void(request_type&)>::type;
template<class F>
using is_ResponseDecorator =
using is_response_decorator =
typename beast::detail::is_invocable<F,
void(response_type&)>::type;

View File

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

View File

@ -34,14 +34,14 @@ namespace websocket {
frame. Finally it invokes the teardown operation to shut down the
underlying connection.
*/
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class Handler>
class stream<NextLayer>::close_op
class stream<NextLayer, deflateSupported>::close_op
: public boost::asio::coroutine
{
struct state
{
stream<NextLayer>& ws;
stream<NextLayer, deflateSupported>& ws;
detail::frame_buffer fb;
error_code ev;
token tok;
@ -49,7 +49,7 @@ class stream<NextLayer>::close_op
state(
Handler const&,
stream<NextLayer>& ws_,
stream<NextLayer, deflateSupported>& ws_,
close_reason const& cr)
: ws(ws_)
, tok(ws.tok_.unique())
@ -69,7 +69,7 @@ public:
template<class DeducedHandler>
close_op(
DeducedHandler&& h,
stream<NextLayer>& ws,
stream<NextLayer, deflateSupported>& ws,
close_reason const& cr)
: d_(std::forward<DeducedHandler>(h), ws, cr)
{
@ -85,7 +85,7 @@ public:
}
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
get_executor() const noexcept
@ -109,10 +109,10 @@ public:
}
};
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class Handler>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
close_op<Handler>::
operator()(
error_code ec,
@ -327,9 +327,9 @@ operator()(
//------------------------------------------------------------------------------
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
close(close_reason const& cr)
{
static_assert(is_sync_stream<next_layer_type>::value,
@ -340,9 +340,9 @@ close(close_reason const& cr)
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
close(close_reason const& cr, error_code& ec)
{
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());
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class CloseHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
CloseHandler, void(error_code))
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
async_close(close_reason const& cr, CloseHandler&& handler)
{
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
//
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class Handler>
class stream<NextLayer>::handshake_op
class stream<NextLayer, deflateSupported>::handshake_op
: public boost::asio::coroutine
{
struct data
{
stream<NextLayer>& ws;
stream<NextLayer, deflateSupported>& ws;
response_type* res_p;
detail::sec_ws_key_type key;
http::request<http::empty_body> req;
response_type res;
template<class Decorator>
data(Handler const&, stream<NextLayer>& ws_,
data(
Handler const&,
stream<NextLayer, deflateSupported>& ws_,
response_type* res_p_,
string_view host,
string_view target,
Decorator const& decorator)
string_view host,
string_view target,
Decorator const& decorator)
: ws(ws_)
, res_p(res_p_)
, req(ws.build_request(key,
@ -69,7 +71,7 @@ public:
template<class DeducedHandler, class... Args>
handshake_op(DeducedHandler&& h,
stream<NextLayer>& ws, Args&&... args)
stream<NextLayer, deflateSupported>& ws, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
ws, std::forward<Args>(args)...)
{
@ -85,7 +87,7 @@ public:
}
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
get_executor() const noexcept
@ -108,17 +110,18 @@ public:
}
};
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class Handler>
void
stream<NextLayer>::handshake_op<Handler>::
stream<NextLayer, deflateSupported>::
handshake_op<Handler>::
operator()(error_code ec, std::size_t)
{
auto& d = *d_;
BOOST_ASIO_CORO_REENTER(*this)
{
// 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
http::async_write(d.ws.stream_,
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>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
async_handshake(string_view host,
string_view target,
HandshakeHandler&& handler)
@ -166,11 +169,11 @@ async_handshake(string_view host,
return init.result.get();
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
async_handshake(response_type& res,
string_view host,
string_view target,
@ -187,11 +190,11 @@ async_handshake(response_type& res,
return init.result.get();
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
async_handshake_ex(string_view host,
string_view target,
RequestDecorator const& decorator,
@ -199,7 +202,7 @@ async_handshake_ex(string_view host,
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
boost::asio::async_completion<HandshakeHandler,
@ -211,11 +214,11 @@ async_handshake_ex(string_view host,
return init.result.get();
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator, class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
HandshakeHandler, void(error_code))
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
async_handshake_ex(response_type& res,
string_view host,
string_view target,
@ -224,7 +227,7 @@ async_handshake_ex(response_type& res,
{
static_assert(is_async_stream<next_layer_type>::value,
"AsyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
boost::asio::async_completion<HandshakeHandler,
@ -236,9 +239,9 @@ async_handshake_ex(response_type& res,
return init.result.get();
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
handshake(string_view host,
string_view target)
{
@ -251,9 +254,9 @@ handshake(string_view host,
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
handshake(response_type& res,
string_view host,
string_view target)
@ -266,17 +269,17 @@ handshake(response_type& res,
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
handshake_ex(string_view host,
string_view target,
RequestDecorator const& decorator)
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
error_code ec;
@ -285,10 +288,10 @@ handshake_ex(string_view host,
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
handshake_ex(response_type& res,
string_view host,
string_view target,
@ -296,7 +299,7 @@ handshake_ex(response_type& res,
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
error_code ec;
@ -305,9 +308,9 @@ handshake_ex(response_type& res,
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
handshake(string_view host,
string_view target, error_code& ec)
{
@ -317,9 +320,9 @@ handshake(string_view host,
host, target, &default_decorate_req, ec);
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
handshake(response_type& res,
string_view host,
string_view target,
@ -331,10 +334,10 @@ handshake(response_type& res,
host, target, &default_decorate_req, ec);
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
handshake_ex(string_view host,
string_view target,
RequestDecorator const& decorator,
@ -342,17 +345,17 @@ handshake_ex(string_view host,
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
do_handshake(nullptr,
host, target, decorator, ec);
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
handshake_ex(response_type& res,
string_view host,
string_view target,
@ -361,7 +364,7 @@ handshake_ex(response_type& res,
{
static_assert(is_sync_stream<next_layer_type>::value,
"SyncStream requirements not met");
static_assert(detail::is_RequestDecorator<
static_assert(detail::is_request_decorator<
RequestDecorator>::value,
"RequestDecorator requirements not met");
do_handshake(&res,
@ -370,10 +373,10 @@ handshake_ex(response_type& res,
//------------------------------------------------------------------------------
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class RequestDecorator>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
do_handshake(
response_type* res_p,
string_view host,
@ -387,7 +390,7 @@ do_handshake(
{
auto const req = build_request(
key, host, target, decorator);
pmd_read(pmd_config_, req);
do_pmd_config(req, is_deflate_supported{});
http::write(stream_, req, ec);
}
if(ec)

View File

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

View File

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

View File

@ -40,9 +40,9 @@ namespace boost {
namespace beast {
namespace websocket {
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class... Args>
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
stream(Args&&... args)
: stream_(std::forward<Args>(args)...)
, tok_(1)
@ -51,16 +51,221 @@ stream(Args&&... args)
max_control_frame_size);
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer, class>
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(
std::size_t initial_size) const
std::size_t initial_size,
std::true_type) const
{
using beast::detail::clamp;
std::size_t result;
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
@ -85,164 +290,45 @@ done:
return result;
}
template<class NextLayer>
template<class DynamicBuffer, class>
template<class NextLayer, bool deflateSupported>
std::size_t
stream<NextLayer>::
read_size_hint(DynamicBuffer& buffer) const
stream<NextLayer, deflateSupported>::
read_size_hint(
std::size_t initial_size,
std::false_type) 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>
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)
using beast::detail::clamp;
std::size_t result;
BOOST_ASSERT(initial_size > 0);
// compression is not supported
if(rd_done_)
{
pmd_normalize(pmd_config_);
pmd_.reset(new pmd_t);
if(role_ == role_type::client)
{
pmd_->zi.reset(
pmd_config_.server_max_window_bits);
pmd_->zo.reset(
pmd_opts_.compLevel,
pmd_config_.client_max_window_bits,
pmd_opts_.memLevel,
zlib::Strategy::normal);
}
else
{
pmd_->zi.reset(
pmd_config_.client_max_window_bits);
pmd_->zo.reset(
pmd_opts_.compLevel,
pmd_config_.server_max_window_bits,
pmd_opts_.memLevel,
zlib::Strategy::normal);
}
// first message frame
result = initial_size;
}
}
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)
else if(rd_fh_.fin)
{
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_);
}
// last message frame
BOOST_ASSERT(rd_remain_ > 0);
result = clamp(rd_remain_);
}
else
{
wr_buf_size_ = wr_buf_opt_;
wr_buf_.reset();
result = (std::max)(
initial_size, clamp(rd_remain_));
}
BOOST_ASSERT(result != 0);
return result;
}
//------------------------------------------------------------------------------
// Attempt to read a complete frame header.
// Returns `false` if more bytes are needed
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
bool
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
parse_fh(
detail::frame_header& fh,
DynamicBuffer& b,
@ -301,14 +387,12 @@ parse_fh(
// new data frame when continuation expected
return err(close_code::protocol_error);
}
if((fh.rsv1 && ! pmd_) ||
fh.rsv2 || fh.rsv3)
if(fh.rsv2 || fh.rsv3 ||
! this->rd_deflated(fh.rsv1))
{
// reserved bits not cleared
return err(close_code::protocol_error);
}
if(pmd_)
pmd_->rd_set = fh.rsv1;
break;
case detail::opcode::cont:
@ -411,7 +495,7 @@ parse_fh(
std::uint64_t>::max)() - fh.len)
return err(close_code::too_big);
}
if(! pmd_ || ! pmd_->rd_set)
if(! this->rd_deflated())
{
if(rd_msg_max_ && beast::detail::sum_exceeds(
rd_size_, fh.len, rd_msg_max_))
@ -425,10 +509,10 @@ parse_fh(
return true;
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class DynamicBuffer>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
write_close(DynamicBuffer& db, close_reason const& cr)
{
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>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
write_ping(DynamicBuffer& db,
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>
request_type
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
build_request(detail::sec_ws_key_type& key,
string_view host,
string_view target,
Decorator const& decorator)
string_view host, string_view target,
Decorator const& decorator)
{
request_type req;
req.target(target);
@ -532,20 +615,7 @@ build_request(detail::sec_ws_key_type& key,
detail::make_sec_ws_key(key, wr_gen_);
req.set(http::field::sec_websocket_key, key);
req.set(http::field::sec_websocket_version, "13");
if(pmd_opts_.client_enable)
{
detail::pmd_offer config;
config.accept = true;
config.server_max_window_bits =
pmd_opts_.server_max_window_bits;
config.client_max_window_bits =
pmd_opts_.client_max_window_bits;
config.server_no_context_takeover =
pmd_opts_.server_no_context_takeover;
config.client_no_context_takeover =
pmd_opts_.client_no_context_takeover;
detail::pmd_write(req, config);
}
build_request_pmd(req, is_deflate_supported{});
decorator(req);
if(! req.count(http::field::user_agent))
req.set(http::field::user_agent,
@ -553,13 +623,36 @@ build_request(detail::sec_ws_key_type& key,
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>
response_type
stream<NextLayer>::
build_response(http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator)
stream<NextLayer, deflateSupported>::
build_response(
http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator)
{
auto const decorate =
[&decorator](response_type& res)
@ -614,12 +707,7 @@ build_response(http::request<Body,
}
response_type res;
{
detail::pmd_offer offer;
detail::pmd_offer unused;
pmd_read(offer, req);
pmd_negotiate(res, unused, offer, pmd_opts_);
}
build_response_pmd(res, req, is_deflate_supported{});
res.result(http::status::switching_protocols);
res.version(req.version());
res.set(http::field::upgrade, "websocket");
@ -633,11 +721,31 @@ build_response(http::request<Body,
return res;
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class Body, class Allocator>
inline
void
stream<NextLayer>::
on_response(response_type const& res,
detail::sec_ws_key_type const& key, error_code& ec)
stream<NextLayer, deflateSupported>::
build_response_pmd(
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 = [&]()
{
@ -664,18 +772,29 @@ on_response(response_type const& res,
return;
}
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;
pmd_read(offer, res);
// VFALCO see if offer satisfies pmd_config_,
// return an error if not.
pmd_config_ = offer; // overwrite for now
open(role_type::client);
this->pmd_config_ = offer; // overwrite for now
}
// _Fail the WebSocket Connection_
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
do_fail(
std::uint16_t code, // if set, send a close frame first
error_code ev, // error code to use upon success

View File

@ -33,13 +33,113 @@ namespace boost {
namespace beast {
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>
class stream<NextLayer>::write_some_op
class stream<NextLayer, deflateSupported>::write_some_op
: public boost::asio::coroutine
{
Handler h_;
stream<NextLayer>& ws_;
stream<NextLayer, deflateSupported>& ws_;
buffers_suffix<Buffers> cb_;
detail::frame_header fh_;
detail::prepared_key key_;
@ -59,7 +159,7 @@ public:
template<class DeducedHandler>
write_some_op(
DeducedHandler&& h,
stream<NextLayer>& ws,
stream<NextLayer, deflateSupported>& ws,
bool fin,
Buffers const& bs)
: h_(std::forward<DeducedHandler>(h))
@ -80,7 +180,7 @@ public:
}
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
get_executor() const noexcept
@ -109,10 +209,10 @@ public:
}
};
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class Buffers, class Handler>
void
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
write_some_op<Buffers, Handler>::
operator()(
error_code ec,
@ -397,8 +497,7 @@ operator()(
{
b = buffer(ws_.wr_buf_.get(),
ws_.wr_buf_size_);
more_ = detail::deflate(ws_.pmd_->zo,
b, cb_, fin_, in_, ec);
more_ = ws_.deflate(b, cb_, fin_, in_, ec);
if(! ws_.check_ok(ec))
goto upcall;
n = buffer_size(b);
@ -450,12 +549,8 @@ operator()(
}
else
{
if(fh_.fin && (
(ws_.role_ == role_type::client &&
ws_.pmd_config_.client_no_context_takeover) ||
(ws_.role_ == role_type::server &&
ws_.pmd_config_.server_no_context_takeover)))
ws_.pmd_->zo.reset();
if(fh_.fin)
ws_.do_context_takeover_write(ws_.role_);
goto upcall;
}
}
@ -479,10 +574,10 @@ operator()(
//------------------------------------------------------------------------------
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
write_some(bool fin, ConstBufferSequence const& buffers)
{
static_assert(is_sync_stream<next_layer_type>::value,
@ -498,10 +593,10 @@ write_some(bool fin, ConstBufferSequence const& buffers)
return bytes_transferred;
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
write_some(bool fin,
ConstBufferSequence const& buffers, error_code& ec)
{
@ -543,9 +638,8 @@ write_some(bool fin,
{
auto b = buffer(
wr_buf_.get(), wr_buf_size_);
auto const more = detail::deflate(
pmd_->zo, b, cb, fin,
bytes_transferred, ec);
auto const more = this->deflate(
b, cb, fin, bytes_transferred, ec);
if(! check_ok(ec))
return bytes_transferred;
auto const n = buffer_size(b);
@ -581,12 +675,8 @@ write_some(bool fin,
fh.op = detail::opcode::cont;
fh.rsv1 = false;
}
if(fh.fin && (
(role_ == role_type::client &&
pmd_config_.client_no_context_takeover) ||
(role_ == role_type::server &&
pmd_config_.server_no_context_takeover)))
pmd_->zo.reset();
if(fh.fin)
this->do_context_takeover_write(role_);
}
else if(! fh.mask)
{
@ -711,11 +801,11 @@ write_some(bool fin,
return bytes_transferred;
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code, std::size_t))
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
async_write_some(bool fin,
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>
std::size_t
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
write(ConstBufferSequence const& buffers)
{
static_assert(is_sync_stream<next_layer_type>::value,
@ -753,10 +843,10 @@ write(ConstBufferSequence const& buffers)
return bytes_transferred;
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
write(ConstBufferSequence const& buffers, error_code& ec)
{
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);
}
template<class NextLayer>
template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence, class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code, std::size_t))
stream<NextLayer>::
stream<NextLayer, deflateSupported>::
async_write(
ConstBufferSequence const& bs, WriteHandler&& handler)
{

View File

@ -21,6 +21,7 @@
#include <boost/beast/websocket/detail/mask.hpp>
#include <boost/beast/websocket/detail/pausation.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/core/static_buffer.hpp>
#include <boost/beast/core/string.hpp>
@ -29,8 +30,6 @@
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/string_body.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/error.hpp>
#include <algorithm>
@ -108,6 +107,12 @@ class frame_test;
For asynchronous operations, the type must support the
@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
are pending asynchronous operations associated with it.
@ -116,8 +121,13 @@ class frame_test;
@b DynamicBuffer,
@b SyncStream
*/
template<class NextLayer>
template<
class NextLayer,
bool deflateSupported>
class stream
#ifndef BOOST_BEAST_DOXYGEN
: private detail::stream_base<deflateSupported>
#endif
{
friend class close_test;
friend class frame_test;
@ -126,7 +136,7 @@ class stream
friend class read2_test;
friend class stream_test;
friend class write_test;
/* The read buffer has to be at least as large
as the largest possible control frame including
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 tcp_frame_size = 1536;
struct op {};
using control_cb_type =
std::function<void(frame_type, string_view)>;
@ -154,16 +162,6 @@ class stream
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
{
open,
@ -231,14 +229,14 @@ class stream
detail::pausation paused_wr_; // paused write op
detail::pausation paused_ping_; // paused ping op
detail::pausation paused_close_; // paused close op
detail::pausation paused_r_rd_; // paused read op (read)
detail::pausation paused_r_close_;// paused close op (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)
detail::pausation paused_r_rd_; // paused read op (async read)
detail::pausation paused_r_close_;// paused close op (async read)
public:
/// Indicates if the permessage-deflate extension is supported
using is_deflate_supported =
std::integral_constant<bool, deflateSupported>;
/// The type of the next layer.
using next_layer_type =
typename std::remove_reference<NextLayer>::type;
@ -444,7 +442,11 @@ public:
*/
std::size_t
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.
@ -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
set_option(permessage_deflate const& o);
set_option(permessage_deflate const& o)
{
set_option(o, is_deflate_supported{});
}
/// Get the permessage-deflate extension options
void
get_option(permessage_deflate& o)
{
o = pmd_opts_;
get_option(o, is_deflate_supported{});
}
/** Set the automatic fragmentation option.
@ -2353,8 +2362,8 @@ public:
next layer's `async_write_some` functions, and is known as a
<em>composed operation</em>. The program must ensure that the
stream performs no other write operations (such as @ref async_ping,
@ref stream::async_write, @ref stream::async_write_some, or
@ref stream::async_close) until this operation completes.
@ref async_write, @ref async_write_some, or @ref async_close)
until this operation completes.
If the close reason specifies a close code other than
@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
as a <em>composed operation</em>. The program must ensure that
the stream performs no other write operations (such as
stream::async_write, stream::async_write_some, or
stream::async_close).
@ref async_write, @ref async_write_some, or
@ref async_close).
The current setting of the @ref binary option controls
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
may be transformed as per the WebSocket protocol settings. The
program must ensure that the stream performs no other write
operations (such as stream::async_write, stream::async_write_some,
or stream::async_close).
operations (such as @ref async_write, @ref async_write_some,
or @ref async_close).
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
@ -3351,10 +3360,65 @@ private:
static void default_decorate_req(request_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_pmd(std::true_type);
void open_pmd(std::false_type)
{
}
void close();
void close_pmd(std::true_type)
{
this->pmd_.reset();
}
void close_pmd(std::false_type)
{
}
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
check_open(error_code& ec)
@ -3394,6 +3458,10 @@ private:
write_ping(DynamicBuffer& b,
detail::opcode op, ping_data const& data);
//
// upgrade
//
template<class Decorator>
request_type
build_request(detail::sec_ws_key_type& key,
@ -3401,28 +3469,94 @@ private:
string_view target,
Decorator const& decorator);
template<class Body,
class Allocator, class Decorator>
response_type
build_response(http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator);
void
build_request_pmd(request_type& req, std::true_type);
void
on_response(response_type const& resp,
detail::sec_ws_key_type const& key, error_code& ec);
build_request_pmd(request_type&, std::false_type)
{
}
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>
void
do_accept(Decorator const& decorator,
do_accept(
Decorator const& decorator,
error_code& ec);
template<class Body, class Allocator,
template<
class Body, class Allocator,
class Decorator>
void
do_accept(http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator, error_code& ec);
do_accept(
http::request<Body,
http::basic_fields<Allocator>> const& req,
Decorator const& decorator,
error_code& ec);
template<class RequestDecorator>
void
@ -3431,6 +3565,10 @@ private:
RequestDecorator const& decorator,
error_code& ec);
//
// fail
//
void
do_fail(
std::uint16_t code,

View File

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

View File

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

View File

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

View File

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

View File

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