Skip to content

Commit

Permalink
permessage-deflate is a compile-time feature (API Change):
Browse files Browse the repository at this point in the history
fix boostorg#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.
  • Loading branch information
vinniefalco committed Dec 30, 2017
1 parent 65d92f6 commit 841ab84
Show file tree
Hide file tree
Showing 20 changed files with 1,236 additions and 642 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
--------------------------------------------------------------------------------

Version 151:

* Sanitizer failures are errors
Expand All @@ -13,6 +15,7 @@ WebSocket:
API Changes:

* http::parser is not MoveConstructible
* permessage-deflate is a compile-time feature

--------------------------------------------------------------------------------

Expand Down
28 changes: 21 additions & 7 deletions doc/qbk/06_websocket/1_streams.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions include/boost/beast/websocket/detail/frame.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 =
Expand Down
80 changes: 1 addition & 79 deletions include/boost/beast/websocket/detail/pmd_extension.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <boost/beast/http/rfc7230.hpp>
#include <boost/asio/buffer.hpp>
#include <utility>
#include <type_traits>

namespace boost {
namespace beast {
Expand Down Expand Up @@ -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
Expand Down
141 changes: 141 additions & 0 deletions include/boost/beast/websocket/detail/stream_base.hpp
Original file line number Diff line number Diff line change
@@ -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 https://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
4 changes: 2 additions & 2 deletions include/boost/beast/websocket/detail/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading

0 comments on commit 841ab84

Please sign in to comment.