summaryrefslogtreecommitdiff
path: root/boost/beast
diff options
context:
space:
mode:
Diffstat (limited to 'boost/beast')
-rw-r--r--boost/beast/core.hpp41
-rw-r--r--boost/beast/core/bind_handler.hpp80
-rw-r--r--boost/beast/core/buffered_read_stream.hpp373
-rw-r--r--boost/beast/core/buffers_adapter.hpp164
-rw-r--r--boost/beast/core/buffers_cat.hpp119
-rw-r--r--boost/beast/core/buffers_prefix.hpp237
-rw-r--r--boost/beast/core/buffers_suffix.hpp161
-rw-r--r--boost/beast/core/buffers_to_string.hpp59
-rw-r--r--boost/beast/core/detail/allocator.hpp42
-rw-r--r--boost/beast/core/detail/base64.hpp251
-rw-r--r--boost/beast/core/detail/bind_handler.hpp189
-rw-r--r--boost/beast/core/detail/buffers_ref.hpp67
-rw-r--r--boost/beast/core/detail/clamp.hpp59
-rw-r--r--boost/beast/core/detail/config.hpp57
-rw-r--r--boost/beast/core/detail/cpu_info.hpp99
-rw-r--r--boost/beast/core/detail/empty_base_optimization.hpp100
-rw-r--r--boost/beast/core/detail/in_place_init.hpp43
-rw-r--r--boost/beast/core/detail/integer_sequence.hpp143
-rw-r--r--boost/beast/core/detail/ostream.hpp319
-rw-r--r--boost/beast/core/detail/sha1.hpp313
-rw-r--r--boost/beast/core/detail/static_ostream.hpp142
-rw-r--r--boost/beast/core/detail/static_string.hpp135
-rw-r--r--boost/beast/core/detail/type_traits.hpp353
-rw-r--r--boost/beast/core/detail/variant.hpp195
-rw-r--r--boost/beast/core/detail/varint.hpp79
-rw-r--r--boost/beast/core/error.hpp58
-rw-r--r--boost/beast/core/file.hpp45
-rw-r--r--boost/beast/core/file_base.hpp89
-rw-r--r--boost/beast/core/file_posix.hpp175
-rw-r--r--boost/beast/core/file_stdio.hpp158
-rw-r--r--boost/beast/core/file_win32.hpp177
-rw-r--r--boost/beast/core/flat_buffer.hpp342
-rw-r--r--boost/beast/core/flat_static_buffer.hpp254
-rw-r--r--boost/beast/core/handler_ptr.hpp213
-rw-r--r--boost/beast/core/impl/buffered_read_stream.ipp248
-rw-r--r--boost/beast/core/impl/buffers_adapter.ipp517
-rw-r--r--boost/beast/core/impl/buffers_cat.ipp503
-rw-r--r--boost/beast/core/impl/buffers_prefix.ipp258
-rw-r--r--boost/beast/core/impl/buffers_suffix.ipp249
-rw-r--r--boost/beast/core/impl/file_posix.ipp349
-rw-r--r--boost/beast/core/impl/file_stdio.ipp239
-rw-r--r--boost/beast/core/impl/file_win32.ipp364
-rw-r--r--boost/beast/core/impl/flat_buffer.ipp475
-rw-r--r--boost/beast/core/impl/flat_static_buffer.ipp151
-rw-r--r--boost/beast/core/impl/handler_ptr.ipp147
-rw-r--r--boost/beast/core/impl/multi_buffer.ipp1063
-rw-r--r--boost/beast/core/impl/read_size.ipp80
-rw-r--r--boost/beast/core/impl/static_buffer.ipp148
-rw-r--r--boost/beast/core/impl/static_string.ipp618
-rw-r--r--boost/beast/core/impl/string_param.ipp108
-rw-r--r--boost/beast/core/multi_buffer.hpp322
-rw-r--r--boost/beast/core/ostream.hpp104
-rw-r--r--boost/beast/core/read_size.hpp64
-rw-r--r--boost/beast/core/span.hpp215
-rw-r--r--boost/beast/core/static_buffer.hpp217
-rw-r--r--boost/beast/core/static_string.hpp1112
-rw-r--r--boost/beast/core/string.hpp155
-rw-r--r--boost/beast/core/string_param.hpp130
-rw-r--r--boost/beast/core/type_traits.hpp490
-rw-r--r--boost/beast/http.hpp38
-rw-r--r--boost/beast/http/basic_dynamic_body.hpp169
-rw-r--r--boost/beast/http/basic_file_body.hpp538
-rw-r--r--boost/beast/http/basic_parser.hpp621
-rw-r--r--boost/beast/http/buffer_body.hpp228
-rw-r--r--boost/beast/http/chunk_encode.hpp736
-rw-r--r--boost/beast/http/detail/basic_parsed_list.hpp198
-rw-r--r--boost/beast/http/detail/basic_parser.hpp896
-rw-r--r--boost/beast/http/detail/chunk_encode.hpp247
-rw-r--r--boost/beast/http/detail/rfc7230.hpp473
-rw-r--r--boost/beast/http/detail/type_traits.hpp202
-rw-r--r--boost/beast/http/dynamic_body.hpp30
-rw-r--r--boost/beast/http/empty_body.hpp132
-rw-r--r--boost/beast/http/error.hpp161
-rw-r--r--boost/beast/http/field.hpp409
-rw-r--r--boost/beast/http/fields.hpp755
-rw-r--r--boost/beast/http/file_body.hpp35
-rw-r--r--boost/beast/http/impl/basic_parser.ipp928
-rw-r--r--boost/beast/http/impl/chunk_encode.ipp707
-rw-r--r--boost/beast/http/impl/error.ipp120
-rw-r--r--boost/beast/http/impl/field.ipp561
-rw-r--r--boost/beast/http/impl/fields.ipp1380
-rw-r--r--boost/beast/http/impl/file_body_win32.ipp584
-rw-r--r--boost/beast/http/impl/message.ipp437
-rw-r--r--boost/beast/http/impl/parser.ipp56
-rw-r--r--boost/beast/http/impl/read.ipp821
-rw-r--r--boost/beast/http/impl/rfc7230.ipp572
-rw-r--r--boost/beast/http/impl/serializer.ipp430
-rw-r--r--boost/beast/http/impl/status.ipp252
-rw-r--r--boost/beast/http/impl/verb.ipp337
-rw-r--r--boost/beast/http/impl/write.ipp887
-rw-r--r--boost/beast/http/message.hpp999
-rw-r--r--boost/beast/http/parser.hpp436
-rw-r--r--boost/beast/http/read.hpp765
-rw-r--r--boost/beast/http/rfc7230.hpp329
-rw-r--r--boost/beast/http/serializer.hpp370
-rw-r--r--boost/beast/http/span_body.hpp171
-rw-r--r--boost/beast/http/status.hpp167
-rw-r--r--boost/beast/http/string_body.hpp199
-rw-r--r--boost/beast/http/type_traits.hpp185
-rw-r--r--boost/beast/http/vector_body.hpp185
-rw-r--r--boost/beast/http/verb.hpp157
-rw-r--r--boost/beast/http/write.hpp569
-rw-r--r--boost/beast/version.hpp28
-rw-r--r--boost/beast/websocket.hpp21
-rw-r--r--boost/beast/websocket/detail/frame.hpp304
-rw-r--r--boost/beast/websocket/detail/hybi13.hpp75
-rw-r--r--boost/beast/websocket/detail/mask.hpp267
-rw-r--r--boost/beast/websocket/detail/pausation.hpp229
-rw-r--r--boost/beast/websocket/detail/pmd_extension.hpp442
-rw-r--r--boost/beast/websocket/detail/type_traits.hpp36
-rw-r--r--boost/beast/websocket/detail/utf8_checker.hpp344
-rw-r--r--boost/beast/websocket/error.hpp45
-rw-r--r--boost/beast/websocket/impl/accept.ipp757
-rw-r--r--boost/beast/websocket/impl/close.ipp459
-rw-r--r--boost/beast/websocket/impl/error.ipp96
-rw-r--r--boost/beast/websocket/impl/handshake.ipp407
-rw-r--r--boost/beast/websocket/impl/ping.ipp271
-rw-r--r--boost/beast/websocket/impl/read.ipp1323
-rw-r--r--boost/beast/websocket/impl/rfc6455.ipp40
-rw-r--r--boost/beast/websocket/impl/ssl.ipp61
-rw-r--r--boost/beast/websocket/impl/stream.ipp717
-rw-r--r--boost/beast/websocket/impl/teardown.ipp187
-rw-r--r--boost/beast/websocket/impl/write.ipp797
-rw-r--r--boost/beast/websocket/option.hpp73
-rw-r--r--boost/beast/websocket/rfc6455.hpp215
-rw-r--r--boost/beast/websocket/role.hpp59
-rw-r--r--boost/beast/websocket/ssl.hpp84
-rw-r--r--boost/beast/websocket/stream.hpp3458
-rw-r--r--boost/beast/websocket/teardown.hpp174
-rw-r--r--boost/beast/zlib.hpp20
-rw-r--r--boost/beast/zlib/deflate_stream.hpp404
-rw-r--r--boost/beast/zlib/detail/bitstream.hpp207
-rw-r--r--boost/beast/zlib/detail/deflate_stream.hpp3006
-rw-r--r--boost/beast/zlib/detail/inflate_stream.hpp1310
-rw-r--r--boost/beast/zlib/detail/ranges.hpp104
-rw-r--r--boost/beast/zlib/detail/window.hpp167
-rw-r--r--boost/beast/zlib/error.hpp139
-rw-r--r--boost/beast/zlib/impl/error.ipp140
-rw-r--r--boost/beast/zlib/inflate_stream.hpp220
-rw-r--r--boost/beast/zlib/zlib.hpp184
140 files changed, 48999 insertions, 0 deletions
diff --git a/boost/beast/core.hpp b/boost/beast/core.hpp
new file mode 100644
index 0000000000..620d42ada6
--- /dev/null
+++ b/boost/beast/core.hpp
@@ -0,0 +1,41 @@
+//
+// 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_CORE_HPP
+#define BOOST_BEAST_CORE_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/buffered_read_stream.hpp>
+#include <boost/beast/core/buffers_adapter.hpp>
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/buffers_to_string.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/file.hpp>
+#include <boost/beast/core/file_base.hpp>
+#include <boost/beast/core/file_posix.hpp>
+#include <boost/beast/core/file_stdio.hpp>
+#include <boost/beast/core/file_win32.hpp>
+#include <boost/beast/core/flat_buffer.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/multi_buffer.hpp>
+#include <boost/beast/core/ostream.hpp>
+#include <boost/beast/core/read_size.hpp>
+#include <boost/beast/core/span.hpp>
+#include <boost/beast/core/static_buffer.hpp>
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/string_param.hpp>
+#include <boost/beast/core/type_traits.hpp>
+
+#endif
diff --git a/boost/beast/core/bind_handler.hpp b/boost/beast/core/bind_handler.hpp
new file mode 100644
index 0000000000..0756bc5aaa
--- /dev/null
+++ b/boost/beast/core/bind_handler.hpp
@@ -0,0 +1,80 @@
+//
+// 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_BIND_HANDLER_HPP
+#define BOOST_BEAST_BIND_HANDLER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/bind_handler.hpp>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+/** Bind parameters to a completion handler, creating a new handler.
+
+ This function creates a new handler which, when invoked, calls
+ the original handler with the list of bound arguments. Any
+ parameters passed in the invocation will be subtituted for
+ placeholders present in the list of bound arguments. Parameters
+ which are not matched to placeholders are silently discarded.
+ The passed handler and arguments are forwarded into the returned
+ handler, which provides the same `io_context` execution guarantees
+ as the original handler.
+
+ Unlike `boost::asio::io_context::wrap`, the returned handler can
+ be used in a subsequent call to `boost::asio::io_context::post`
+ instead of `boost::asio::io_context::dispatch`, to ensure that
+ the handler will not be invoked immediately by the calling
+ function.
+
+ Example:
+
+ @code
+ template<class AsyncReadStream, class ReadHandler>
+ void
+ signal_aborted(AsyncReadStream& stream, ReadHandler&& handler)
+ {
+ boost::asio::post(
+ stream.get_executor(),
+ bind_handler(std::forward<ReadHandler>(handler),
+ boost::asio::error::operation_aborted, 0));
+ }
+ @endcode
+
+ @param handler The handler to wrap.
+
+ @param args A list of arguments to bind to the handler. The
+ arguments are forwarded into the returned object.
+*/
+template<class Handler, class... Args>
+#if BOOST_BEAST_DOXYGEN
+implementation_defined
+#else
+detail::bound_handler<
+ typename std::decay<Handler>::type, Args...>
+#endif
+bind_handler(Handler&& handler, Args&&... args)
+{
+#if 0
+ static_assert(is_completion_handler<
+ Handler, void(Args...)>::value,
+ "Handler requirements not met");
+#endif
+ return detail::bound_handler<typename std::decay<
+ Handler>::type, Args...>(std::forward<
+ Handler>(handler), std::forward<Args>(args)...);
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/buffered_read_stream.hpp b/boost/beast/core/buffered_read_stream.hpp
new file mode 100644
index 0000000000..e683a40953
--- /dev/null
+++ b/boost/beast/core/buffered_read_stream.hpp
@@ -0,0 +1,373 @@
+//
+// 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_BUFFERED_READ_STREAM_HPP
+#define BOOST_BEAST_BUFFERED_READ_STREAM_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/multi_buffer.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/asio/io_context.hpp>
+#include <cstdint>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+/** A @b Stream with attached @b DynamicBuffer to buffer reads.
+
+ This wraps a @b Stream implementation so that calls to write are
+ passed through to the underlying stream, while calls to read will
+ first consume the input sequence stored in a @b DynamicBuffer which
+ is part of the object.
+
+ The use-case for this class is different than that of the
+ `boost::asio::buffered_readstream`. It is designed to facilitate
+ the use of `boost::asio::read_until`, and to allow buffers
+ acquired during detection of handshakes to be made transparently
+ available to callers. A hypothetical implementation of the
+ buffered version of `boost::asio::ssl::stream::async_handshake`
+ could make use of this wrapper.
+
+ Uses:
+
+ @li Transparently leave untouched input acquired in calls
+ to `boost::asio::read_until` behind for subsequent callers.
+
+ @li "Preload" a stream with handshake input data acquired
+ from other sources.
+
+ Example:
+ @code
+ // Process the next HTTP header on the stream,
+ // leaving excess bytes behind for the next call.
+ //
+ template<class DynamicBuffer>
+ void process_http_message(
+ buffered_read_stream<DynamicBuffer>& stream)
+ {
+ // Read up to and including the end of the HTTP
+ // header, leaving the sequence in the stream's
+ // buffer. read_until may read past the end of the
+ // headers; the return value will include only the
+ // part up to the end of the delimiter.
+ //
+ std::size_t bytes_transferred =
+ boost::asio::read_until(
+ stream.next_layer(), stream.buffer(), "\r\n\r\n");
+
+ // Use buffers_prefix() to limit the input
+ // sequence to only the data up to and including
+ // the trailing "\r\n\r\n".
+ //
+ auto header_buffers = buffers_prefix(
+ bytes_transferred, stream.buffer().data());
+
+ ...
+
+ // Discard the portion of the input corresponding
+ // to the HTTP headers.
+ //
+ stream.buffer().consume(bytes_transferred);
+
+ // Everything we read from the stream
+ // is part of the content-body.
+ }
+ @endcode
+
+ @tparam Stream The type of stream to wrap.
+
+ @tparam DynamicBuffer The type of stream buffer to use.
+*/
+template<class Stream, class DynamicBuffer>
+class buffered_read_stream
+{
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+
+ template<class Buffers, class Handler>
+ class read_some_op;
+
+ DynamicBuffer buffer_;
+ std::size_t capacity_ = 0;
+ Stream next_layer_;
+
+public:
+ /// The type of the internal buffer
+ using buffer_type = DynamicBuffer;
+
+ /// The type of the next layer.
+ using next_layer_type =
+ typename std::remove_reference<Stream>::type;
+
+ /// The type of the lowest layer.
+ using lowest_layer_type =
+ typename get_lowest_layer<next_layer_type>::type;
+
+ /** Move constructor.
+
+ @note The behavior of move assignment on or from streams
+ with active or pending operations is undefined.
+ */
+ buffered_read_stream(buffered_read_stream&&) = default;
+
+ /** Move assignment.
+
+ @note The behavior of move assignment on or from streams
+ with active or pending operations is undefined.
+ */
+ buffered_read_stream& operator=(buffered_read_stream&&) = default;
+
+ /** Construct the wrapping stream.
+
+ @param args Parameters forwarded to the `Stream` constructor.
+ */
+ template<class... Args>
+ explicit
+ buffered_read_stream(Args&&... args);
+
+ /// Get a reference to the next layer.
+ next_layer_type&
+ next_layer()
+ {
+ return next_layer_;
+ }
+
+ /// Get a const reference to the next layer.
+ next_layer_type const&
+ next_layer() const
+ {
+ return next_layer_;
+ }
+
+ /// Get a reference to the lowest layer.
+ lowest_layer_type&
+ lowest_layer()
+ {
+ return next_layer_.lowest_layer();
+ }
+
+ /// Get a const reference to the lowest layer.
+ lowest_layer_type const&
+ lowest_layer() const
+ {
+ return next_layer_.lowest_layer();
+ }
+
+ /** Get the executor associated with the object.
+
+ This function may be used to obtain the executor object that the stream
+ uses to dispatch handlers for asynchronous operations.
+
+ @return A copy of the executor that stream will use to dispatch handlers.
+
+ @note This function participates in overload resolution only if
+ `NextLayer` has a member function named `get_executor`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ implementation_defined
+#else
+ template<
+ class T = next_layer_type,
+ class = typename std::enable_if<
+ has_get_executor<next_layer_type>::value>::type>
+ auto
+#endif
+ get_executor() noexcept ->
+ decltype(std::declval<T&>().get_executor())
+ {
+ return next_layer_.get_executor();
+ }
+
+ /** Access the internal buffer.
+
+ The internal buffer is returned. It is possible for the
+ caller to break invariants with this function. For example,
+ by causing the internal buffer size to increase beyond
+ the caller defined maximum.
+ */
+ DynamicBuffer&
+ buffer()
+ {
+ return buffer_;
+ }
+
+ /// Access the internal buffer
+ DynamicBuffer const&
+ buffer() const
+ {
+ return buffer_;
+ }
+
+ /** Set the maximum buffer size.
+
+ This changes the maximum size of the internal buffer used
+ to hold read data. No bytes are discarded by this call. If
+ the buffer size is set to zero, no more data will be buffered.
+
+ Thread safety:
+ The caller is responsible for making sure the call is
+ made from the same implicit or explicit strand.
+
+ @param size The number of bytes in the read buffer.
+
+ @note This is a soft limit. If the new maximum size is smaller
+ than the amount of data in the buffer, no bytes are discarded.
+ */
+ void
+ capacity(std::size_t size)
+ {
+ capacity_ = size;
+ }
+
+ /** Read some data from the stream.
+
+ This function is used to read data from the stream.
+ The function call will block until one or more bytes of
+ data has been read successfully, or until an error occurs.
+
+ @param buffers One or more buffers into which the data will be read.
+
+ @return The number of bytes read.
+
+ @throws system_error Thrown on failure.
+ */
+ template<class MutableBufferSequence>
+ std::size_t
+ read_some(MutableBufferSequence const& buffers);
+
+ /** Read some data from the stream.
+
+ This function is used to read data from the stream.
+ The function call will block until one or more bytes of
+ data has been read successfully, or until an error occurs.
+
+ @param buffers One or more buffers into which the data will be read.
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of bytes read, or 0 on error.
+ */
+ template<class MutableBufferSequence>
+ std::size_t
+ read_some(MutableBufferSequence const& buffers,
+ error_code& ec);
+
+ /** Start an asynchronous read.
+
+ This function is used to asynchronously read data from
+ the stream. The function call always returns immediately.
+
+ @param buffers One or more buffers into which the data
+ will be read. Although the buffers object may be copied
+ as necessary, ownership of the underlying memory blocks
+ is retained by the caller, which must guarantee that they
+ remain valid until the handler is called.
+
+ @param handler The handler to be called when the operation
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // number of bytes transferred
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class MutableBufferSequence, class ReadHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+ async_read_some(MutableBufferSequence const& buffers,
+ ReadHandler&& handler);
+
+ /** Write some data to the stream.
+
+ This function is used to write data to the stream.
+ The function call will block until one or more bytes of the
+ data has been written successfully, or until an error occurs.
+
+ @param buffers One or more data buffers to be written to the stream.
+
+ @return The number of bytes written.
+
+ @throws system_error Thrown on failure.
+ */
+ template<class ConstBufferSequence>
+ std::size_t
+ write_some(ConstBufferSequence const& buffers)
+ {
+ static_assert(is_sync_write_stream<next_layer_type>::value,
+ "SyncWriteStream requirements not met");
+ return next_layer_.write_some(buffers);
+ }
+
+ /** Write some data to the stream.
+
+ This function is used to write data to the stream.
+ The function call will block until one or more bytes of the
+ data has been written successfully, or until an error occurs.
+
+ @param buffers One or more data buffers to be written to the stream.
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of bytes written.
+ */
+ template<class ConstBufferSequence>
+ std::size_t
+ write_some(ConstBufferSequence const& buffers,
+ error_code& ec)
+ {
+ static_assert(is_sync_write_stream<next_layer_type>::value,
+ "SyncWriteStream requirements not met");
+ return next_layer_.write_some(buffers, ec);
+ }
+
+ /** Start an asynchronous write.
+
+ This function is used to asynchronously write data from
+ the stream. The function call always returns immediately.
+
+ @param buffers One or more data buffers to be written to
+ the stream. Although the buffers object may be copied as
+ necessary, ownership of the underlying memory blocks is
+ retained by the caller, which must guarantee that they
+ remain valid until the handler is called.
+
+ @param handler The handler to be called when the operation
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // number of bytes transferred
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class ConstBufferSequence, class WriteHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+ async_write_some(ConstBufferSequence const& buffers,
+ WriteHandler&& handler);
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/buffered_read_stream.ipp>
+
+#endif
diff --git a/boost/beast/core/buffers_adapter.hpp b/boost/beast/core/buffers_adapter.hpp
new file mode 100644
index 0000000000..826183ed06
--- /dev/null
+++ b/boost/beast/core/buffers_adapter.hpp
@@ -0,0 +1,164 @@
+//
+// 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_BUFFERS_ADAPTER_HPP
+#define BOOST_BEAST_BUFFERS_ADAPTER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+
+/** Adapts a @b MutableBufferSequence into a @b DynamicBuffer.
+
+ This class wraps a @b MutableBufferSequence to meet the requirements
+ of @b DynamicBuffer. Upon construction the input and output sequences are
+ empty. A copy of the mutable buffer sequence object is stored; however,
+ ownership of the underlying memory is not transferred. The caller is
+ responsible for making sure that referenced memory remains valid
+ for the duration of any operations.
+
+ The size of the mutable buffer sequence determines the maximum
+ number of bytes which may be prepared and committed.
+
+ @tparam MutableBufferSequence The type of mutable buffer sequence to wrap.
+*/
+template<class MutableBufferSequence>
+class buffers_adapter
+{
+ static_assert(boost::asio::is_mutable_buffer_sequence<MutableBufferSequence>::value,
+ "MutableBufferSequence requirements not met");
+
+ using iter_type = typename
+ detail::buffer_sequence_iterator<
+ MutableBufferSequence>::type;
+
+ MutableBufferSequence bs_;
+ iter_type begin_;
+ iter_type out_;
+ iter_type end_;
+ std::size_t max_size_;
+ std::size_t in_pos_ = 0; // offset in *begin_
+ std::size_t in_size_ = 0; // size of input sequence
+ std::size_t out_pos_ = 0; // offset in *out_
+ std::size_t out_end_ = 0; // output end offset
+
+ template<class Deduced>
+ buffers_adapter(Deduced&& other,
+ std::size_t nbegin, std::size_t nout,
+ std::size_t nend)
+ : bs_(std::forward<Deduced>(other).bs_)
+ , begin_(std::next(bs_.begin(), nbegin))
+ , out_(std::next(bs_.begin(), nout))
+ , end_(std::next(bs_.begin(), nend))
+ , max_size_(other.max_size_)
+ , in_pos_(other.in_pos_)
+ , in_size_(other.in_size_)
+ , out_pos_(other.out_pos_)
+ , out_end_(other.out_end_)
+ {
+ }
+
+public:
+#if BOOST_BEAST_DOXYGEN
+ /// The type used to represent the input sequence as a list of buffers.
+ using const_buffers_type = implementation_defined;
+
+ /// The type used to represent the output sequence as a list of buffers.
+ using mutable_buffers_type = implementation_defined;
+
+#else
+ class const_buffers_type;
+
+ class mutable_buffers_type;
+
+#endif
+
+ /// Move constructor.
+ buffers_adapter(buffers_adapter&& other);
+
+ /// Copy constructor.
+ buffers_adapter(buffers_adapter const& other);
+
+ /// Move assignment.
+ buffers_adapter& operator=(buffers_adapter&& other);
+
+ /// Copy assignment.
+ buffers_adapter& operator=(buffers_adapter const&);
+
+ /** Construct a buffers adapter.
+
+ @param buffers The mutable buffer sequence to wrap. A copy of
+ the object will be made, but ownership of the memory is not
+ transferred.
+ */
+ explicit
+ buffers_adapter(MutableBufferSequence const& buffers);
+
+ /// Returns the largest size output sequence possible.
+ std::size_t
+ max_size() const
+ {
+ return max_size_;
+ }
+
+ /// Get the size of the input sequence.
+ std::size_t
+ size() const
+ {
+ return in_size_;
+ }
+
+ /// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
+ std::size_t
+ capacity() const
+ {
+ return max_size_;
+ }
+
+ /** Get a list of buffers that represents the output sequence, with the given size.
+
+ @throws std::length_error if the size would exceed the limit
+ imposed by the underlying mutable buffer sequence.
+
+ @note Buffers representing the input sequence acquired prior to
+ this call remain valid.
+ */
+ mutable_buffers_type
+ prepare(std::size_t n);
+
+ /** Move bytes from the output sequence to the input sequence.
+
+ @note Buffers representing the input sequence acquired prior to
+ this call remain valid.
+ */
+ void
+ commit(std::size_t n);
+
+ /** Get a list of buffers that represents the input sequence.
+
+ @note These buffers remain valid across subsequent calls to `prepare`.
+ */
+ const_buffers_type
+ data() const;
+
+ /// Remove bytes from the input sequence.
+ void
+ consume(std::size_t n);
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/buffers_adapter.ipp>
+
+#endif
diff --git a/boost/beast/core/buffers_cat.hpp b/boost/beast/core/buffers_cat.hpp
new file mode 100644
index 0000000000..1d711b3b30
--- /dev/null
+++ b/boost/beast/core/buffers_cat.hpp
@@ -0,0 +1,119 @@
+//
+// 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_BUFFERS_CAT_HPP
+#define BOOST_BEAST_BUFFERS_CAT_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <tuple>
+
+namespace boost {
+namespace beast {
+
+/** A buffer sequence representing a concatenation of buffer sequences.
+
+ @see @ref buffers_cat
+*/
+template<class... Buffers>
+class buffers_cat_view
+{
+ std::tuple<Buffers...> bn_;
+
+public:
+ /** The type of buffer returned when dereferencing an iterator.
+
+ If every buffer sequence in the view is a @b MutableBufferSequence,
+ then `value_type` will be `boost::asio::mutable_buffer`.
+ Otherwise, `value_type` will be `boost::asio::const_buffer`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using value_type = implementation_defined;
+#else
+ using value_type = typename
+ detail::common_buffers_type<Buffers...>::type;
+#endif
+
+ /// The type of iterator used by the concatenated sequence
+ class const_iterator;
+
+ /// Constructor
+ buffers_cat_view(buffers_cat_view&&) = default;
+
+ /// Assignment
+ buffers_cat_view& operator=(buffers_cat_view&&) = default;
+
+ /// Assignment
+ buffers_cat_view& operator=(buffers_cat_view const&) = default;
+
+ /** Constructor
+
+ @param buffers The list of buffer sequences to concatenate.
+ Copies of the arguments will be made; however, the ownership
+ of memory is not transferred.
+ */
+ explicit
+ buffers_cat_view(Buffers const&... buffers);
+
+ //-----
+
+ /// Required for @b BufferSequence
+ buffers_cat_view(buffers_cat_view const&) = default;
+
+ /// Required for @b BufferSequence
+ const_iterator
+ begin() const;
+
+ /// Required for @b BufferSequence
+ const_iterator
+ end() const;
+};
+
+/** Concatenate 2 or more buffer sequences.
+
+ This function returns a constant or mutable buffer sequence which,
+ when iterated, efficiently concatenates the input buffer sequences.
+ Copies of the arguments passed will be made; however, the returned
+ object does not take ownership of the underlying memory. The
+ application is still responsible for managing the lifetime of the
+ referenced memory.
+
+ @param buffers The list of buffer sequences to concatenate.
+
+ @return A new buffer sequence that represents the concatenation of
+ the input buffer sequences. This buffer sequence will be a
+ @b MutableBufferSequence if each of the passed buffer sequences is
+ also a @b MutableBufferSequence; otherwise the returned buffer
+ sequence will be a @b ConstBufferSequence.
+
+ @see @ref buffers_cat_view
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class... BufferSequence>
+buffers_cat_view<BufferSequence...>
+buffers_cat(BufferSequence const&... buffers)
+#else
+template<class B1, class B2, class... Bn>
+inline
+buffers_cat_view<B1, B2, Bn...>
+buffers_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
+#endif
+{
+ static_assert(
+ detail::is_all_const_buffer_sequence<B1, B2, Bn...>::value,
+ "BufferSequence requirements not met");
+ return buffers_cat_view<B1, B2, Bn...>{b1, b2, bn...};
+}
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/buffers_cat.ipp>
+
+#endif
diff --git a/boost/beast/core/buffers_prefix.hpp b/boost/beast/core/buffers_prefix.hpp
new file mode 100644
index 0000000000..67d58b5c58
--- /dev/null
+++ b/boost/beast/core/buffers_prefix.hpp
@@ -0,0 +1,237 @@
+//
+// 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_BUFFERS_PREFIX_HPP
+#define BOOST_BEAST_BUFFERS_PREFIX_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/in_place_init.hpp>
+#include <boost/asio/buffer.hpp>
+#include <cstdint>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+
+/** A buffer sequence adapter that shortens the sequence size.
+
+ The class adapts a buffer sequence to efficiently represent
+ a shorter subset of the original list of buffers starting
+ with the first byte of the original sequence.
+
+ @tparam BufferSequence The buffer sequence to adapt.
+*/
+template<class BufferSequence>
+class buffers_prefix_view
+{
+ using buffers_type = typename
+ std::decay<BufferSequence>::type;
+
+ using iter_type = typename
+ detail::buffer_sequence_iterator<buffers_type>::type;
+
+ BufferSequence bs_;
+ std::size_t size_;
+ iter_type end_;
+
+ template<class Deduced>
+ buffers_prefix_view(
+ Deduced&& other, std::size_t dist)
+ : bs_(std::forward<Deduced>(other).bs_)
+ , size_(other.size_)
+ , end_(std::next(bs_.begin(), dist))
+ {
+ }
+
+ void
+ setup(std::size_t size);
+
+public:
+ /// The type for each element in the list of buffers.
+ using value_type = typename std::conditional<
+ std::is_convertible<typename
+ std::iterator_traits<iter_type>::value_type,
+ boost::asio::mutable_buffer>::value,
+ boost::asio::mutable_buffer,
+ boost::asio::const_buffer>::type;
+
+#if BOOST_BEAST_DOXYGEN
+ /// A bidirectional iterator type that may be used to read elements.
+ using const_iterator = implementation_defined;
+
+#else
+ class const_iterator;
+
+#endif
+
+ /// Move constructor.
+ buffers_prefix_view(buffers_prefix_view&&);
+
+ /// Copy constructor.
+ buffers_prefix_view(buffers_prefix_view const&);
+
+ /// Move assignment.
+ buffers_prefix_view& operator=(buffers_prefix_view&&);
+
+ /// Copy assignment.
+ buffers_prefix_view& operator=(buffers_prefix_view const&);
+
+ /** Construct a buffer sequence prefix.
+
+ @param size The maximum number of bytes in the prefix.
+ If this is larger than the size of passed, buffers,
+ the resulting sequence will represent the entire
+ input sequence.
+
+ @param buffers The buffer sequence to adapt. A copy of
+ the sequence will be made, but ownership of the underlying
+ memory is not transferred.
+ */
+ buffers_prefix_view(
+ std::size_t size,
+ BufferSequence const& buffers);
+
+ /** Construct a buffer sequence prefix in-place.
+
+ @param size The maximum number of bytes in the prefix.
+ If this is larger than the size of passed, buffers,
+ the resulting sequence will represent the entire
+ input sequence.
+
+ @param args Arguments forwarded to the contained buffers constructor.
+ */
+ template<class... Args>
+ buffers_prefix_view(
+ std::size_t size,
+ boost::in_place_init_t,
+ Args&&... args);
+
+ /// Get a bidirectional iterator to the first element.
+ const_iterator
+ begin() const;
+
+ /// Get a bidirectional iterator to one past the last element.
+ const_iterator
+ end() const;
+};
+
+/** Returns a prefix of a constant buffer.
+
+ The returned buffer points to the same memory as the
+ passed buffer, but with a size that is equal to or less
+ than the size of the original buffer.
+
+ @param size The size of the returned buffer.
+
+ @param buffer The buffer to shorten. The underlying
+ memory is not modified.
+
+ @return A new buffer that points to the first `size`
+ bytes of the original buffer.
+*/
+inline
+boost::asio::const_buffer
+buffers_prefix(std::size_t size,
+ boost::asio::const_buffer buffer)
+{
+ return {buffer.data(),
+ (std::min)(size, buffer.size())};
+}
+
+/** Returns a prefix of a mutable buffer.
+
+ The returned buffer points to the same memory as the
+ passed buffer, but with a size that is equal to or less
+ than the size of the original buffer.
+
+ @param size The size of the returned buffer.
+
+ @param buffer The buffer to shorten. The underlying
+ memory is not modified.
+
+ @return A new buffer that points to the first `size` bytes
+ of the original buffer.
+*/
+inline
+boost::asio::mutable_buffer
+buffers_prefix(std::size_t size,
+ boost::asio::mutable_buffer buffer)
+{
+ return {buffer.data(),
+ (std::min)(size, buffer.size())};
+}
+
+/** Returns a prefix of a buffer sequence.
+
+ This function returns a new buffer sequence which when iterated,
+ presents a shorter subset of the original list of buffers starting
+ with the first byte of the original sequence.
+
+ @param size The maximum number of bytes in the wrapped
+ sequence. If this is larger than the size of passed,
+ buffers, the resulting sequence will represent the
+ entire input sequence.
+
+ @param buffers An instance of @b ConstBufferSequence or
+ @b MutableBufferSequence to adapt. A copy of the sequence
+ will be made, but ownership of the underlying memory is
+ not transferred.
+*/
+template<class BufferSequence>
+#if BOOST_BEAST_DOXYGEN
+buffers_prefix_view<BufferSequence>
+#else
+inline
+typename std::enable_if<
+ ! std::is_same<BufferSequence,
+ boost::asio::const_buffer>::value &&
+ ! std::is_same<BufferSequence,
+ boost::asio::mutable_buffer>::value,
+ buffers_prefix_view<BufferSequence>>::type
+#endif
+buffers_prefix(std::size_t size, BufferSequence const& buffers)
+{
+ static_assert(
+ boost::asio::is_const_buffer_sequence<BufferSequence>::value ||
+ boost::asio::is_mutable_buffer_sequence<BufferSequence>::value,
+ "BufferSequence requirements not met");
+ return buffers_prefix_view<BufferSequence>(size, buffers);
+}
+
+/** Returns the first buffer in a buffer sequence
+
+ This returns the first buffer in the buffer sequence.
+ If the buffer sequence is an empty range, the returned
+ buffer will have a zero buffer size.
+
+ @param buffers The buffer sequence. If the sequence is
+ mutable, the returned buffer sequence will also be mutable.
+ Otherwise, the returned buffer sequence will be constant.
+*/
+template<class BufferSequence>
+typename std::conditional<
+ boost::asio::is_mutable_buffer_sequence<BufferSequence>::value,
+ boost::asio::mutable_buffer,
+ boost::asio::const_buffer>::type
+buffers_front(BufferSequence const& buffers)
+{
+ auto const first =
+ boost::asio::buffer_sequence_begin(buffers);
+ if(first == boost::asio::buffer_sequence_end(buffers))
+ return {};
+ return *first;
+}
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/buffers_prefix.ipp>
+
+#endif
diff --git a/boost/beast/core/buffers_suffix.hpp b/boost/beast/core/buffers_suffix.hpp
new file mode 100644
index 0000000000..81eb5a6ba7
--- /dev/null
+++ b/boost/beast/core/buffers_suffix.hpp
@@ -0,0 +1,161 @@
+//
+// 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_BUFFERS_SUFFIX_HPP
+#define BOOST_BEAST_BUFFERS_SUFFIX_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/detail/in_place_init.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/optional.hpp>
+#include <cstdint>
+#include <iterator>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+/** Adapter to trim the front of a `BufferSequence`.
+
+ This adapter wraps a buffer sequence to create a new sequence
+ which may be incrementally consumed. Bytes consumed are removed
+ from the front of the buffer. The underlying memory is not changed,
+ instead the adapter efficiently iterates through a subset of
+ the buffers wrapped.
+
+ The wrapped buffer is not modified, a copy is made instead.
+ Ownership of the underlying memory is not transferred, the application
+ is still responsible for managing its lifetime.
+
+ @tparam BufferSequence The buffer sequence to wrap.
+
+ @par Example
+
+ This function writes the entire contents of a buffer sequence
+ to the specified stream.
+
+ @code
+ template<class SyncWriteStream, class ConstBufferSequence>
+ void send(SyncWriteStream& stream, ConstBufferSequence const& buffers)
+ {
+ buffers_suffix<ConstBufferSequence> bs{buffers};
+ while(boost::asio::buffer_size(bs) > 0)
+ bs.consume(stream.write_some(bs));
+ }
+ @endcode
+*/
+template<class BufferSequence>
+class buffers_suffix
+{
+ using buffers_type =
+ typename std::decay<BufferSequence>::type;
+
+ using iter_type = typename
+ detail::buffer_sequence_iterator<buffers_type>::type;
+
+ BufferSequence bs_;
+ iter_type begin_;
+ std::size_t skip_ = 0;
+
+ template<class Deduced>
+ buffers_suffix(Deduced&& other, std::size_t dist)
+ : bs_(std::forward<Deduced>(other).bs_)
+ , begin_(std::next(
+ boost::asio::buffer_sequence_begin(bs_),
+ dist))
+ , skip_(other.skip_)
+ {
+ }
+
+public:
+ /** The type for each element in the list of buffers.
+
+ If the buffers in the underlying sequence are convertible to
+ `boost::asio::mutable_buffer`, then this type will be
+ `boost::asio::mutable_buffer`, else this type will be
+ `boost::asio::const_buffer`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using value_type = implementation_defined;
+#else
+ using value_type = typename std::conditional<
+ std::is_convertible<typename
+ std::iterator_traits<iter_type>::value_type,
+ boost::asio::mutable_buffer>::value,
+ boost::asio::mutable_buffer,
+ boost::asio::const_buffer>::type;
+#endif
+
+#if BOOST_BEAST_DOXYGEN
+ /// A bidirectional iterator type that may be used to read elements.
+ using const_iterator = implementation_defined;
+
+#else
+ class const_iterator;
+
+#endif
+
+ /// Constructor
+ buffers_suffix();
+
+ /// Constructor
+ buffers_suffix(buffers_suffix&&);
+
+ /// Constructor
+ buffers_suffix(buffers_suffix const&);
+
+ /** Constructor
+
+ A copy of the buffer sequence is made. Ownership of the
+ underlying memory is not transferred or copied.
+ */
+ explicit
+ buffers_suffix(BufferSequence const& buffers);
+
+ /** Constructor
+
+ This constructs the buffer sequence in-place from
+ a list of arguments.
+
+ @param args Arguments forwarded to the buffers constructor.
+ */
+ template<class... Args>
+ buffers_suffix(boost::in_place_init_t, Args&&... args);
+
+ /// Assignment
+ buffers_suffix& operator=(buffers_suffix&&);
+
+ /// Assignment
+ buffers_suffix& operator=(buffers_suffix const&);
+
+ /// Get a bidirectional iterator to the first element.
+ const_iterator
+ begin() const;
+
+ /// Get a bidirectional iterator to one past the last element.
+ const_iterator
+ end() const;
+
+ /** Remove bytes from the beginning of the sequence.
+
+ @param amount The number of bytes to remove. If this is
+ larger than the number of bytes remaining, all the
+ bytes remaining are removed.
+ */
+ void
+ consume(std::size_t amount);
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/buffers_suffix.ipp>
+
+#endif
diff --git a/boost/beast/core/buffers_to_string.hpp b/boost/beast/core/buffers_to_string.hpp
new file mode 100644
index 0000000000..aae3d3aa57
--- /dev/null
+++ b/boost/beast/core/buffers_to_string.hpp
@@ -0,0 +1,59 @@
+//
+// 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_BUFFERS_TO_STRING_HPP
+#define BOOST_BEAST_BUFFERS_TO_STRING_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <string>
+
+namespace boost {
+namespace beast {
+
+/** Return a string representing the contents of a buffer sequence.
+
+ This function returns a string representing an entire buffer
+ sequence. Nulls and unprintable characters in the buffer
+ sequence are inserted to the resulting string as-is. No
+ character conversions are performed.
+
+ @param buffers The buffer sequence to convert
+
+ @par Example
+
+ This function writes a buffer sequence converted to a string
+ to `std::cout`.
+
+ @code
+ template<class ConstBufferSequence>
+ void print(ConstBufferSequence const& buffers)
+ {
+ std::cout << buffers_to_string(buffers) << std::endl;
+ }
+ @endcode
+*/
+template<class ConstBufferSequence>
+std::string
+buffers_to_string(ConstBufferSequence const& buffers)
+{
+ std::string result;
+ result.reserve(boost::asio::buffer_size(buffers));
+ for(boost::asio::const_buffer buffer :
+ detail::buffers_range(buffers))
+ result.append(reinterpret_cast<
+ char const*>(buffer.data()), buffer.size());
+ return result;
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/allocator.hpp b/boost/beast/core/detail/allocator.hpp
new file mode 100644
index 0000000000..39b2aa5f12
--- /dev/null
+++ b/boost/beast/core/detail/allocator.hpp
@@ -0,0 +1,42 @@
+//
+// 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_DETAIL_ALLOCATOR_HPP
+#define BOOST_BEAST_DETAIL_ALLOCATOR_HPP
+
+#include <boost/config.hpp>
+#if BOOST_NO_CXX11_ALLOCATOR
+#include <boost/container/allocator_traits.hpp>
+#else
+#include <memory>
+#endif
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+// This is a workaround for allocator_traits
+// implementations which falsely claim C++11
+// compatibility.
+
+#if BOOST_NO_CXX11_ALLOCATOR
+template<class Alloc>
+using allocator_traits = boost::container::allocator_traits<Alloc>;
+
+#else
+template<class Alloc>
+using allocator_traits = std::allocator_traits<Alloc>;
+
+#endif
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/base64.hpp b/boost/beast/core/detail/base64.hpp
new file mode 100644
index 0000000000..ece03dfa34
--- /dev/null
+++ b/boost/beast/core/detail/base64.hpp
@@ -0,0 +1,251 @@
+//
+// 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
+//
+
+/*
+ Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
+ Copyright notice:
+
+ base64.cpp and base64.h
+
+ Copyright (C) 2004-2008 René Nyffenegger
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+ René Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+*/
+
+#ifndef BOOST_BEAST_DETAIL_BASE64_HPP
+#define BOOST_BEAST_DETAIL_BASE64_HPP
+
+#include <cctype>
+#include <string>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+namespace base64 {
+
+inline
+char const*
+get_alphabet()
+{
+ static char constexpr tab[] = {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+ };
+ return &tab[0];
+}
+
+inline
+signed char const*
+get_inverse()
+{
+ static signed char constexpr tab[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255
+ };
+ return &tab[0];
+}
+
+
+/// Returns max chars needed to encode a base64 string
+inline
+std::size_t constexpr
+encoded_size(std::size_t n)
+{
+ return 4 * ((n + 2) / 3);
+}
+
+/// Returns max bytes needed to decode a base64 string
+inline
+std::size_t constexpr
+decoded_size(std::size_t n)
+{
+ return n / 4 * 3; // requires n&3==0, smaller
+ //return 3 * n / 4;
+}
+
+/** Encode a series of octets as a padded, base64 string.
+
+ The resulting string will not be null terminated.
+
+ @par Requires
+
+ The memory pointed to by `out` points to valid memory
+ of at least `encoded_size(len)` bytes.
+
+ @return The number of characters written to `out`. This
+ will exclude any null termination.
+*/
+template<class = void>
+std::size_t
+encode(void* dest, void const* src, std::size_t len)
+{
+ char* out = static_cast<char*>(dest);
+ char const* in = static_cast<char const*>(src);
+ auto const tab = base64::get_alphabet();
+
+ for(auto n = len / 3; n--;)
+ {
+ *out++ = tab[ (in[0] & 0xfc) >> 2];
+ *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
+ *out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
+ *out++ = tab[ in[2] & 0x3f];
+ in += 3;
+ }
+
+ switch(len % 3)
+ {
+ case 2:
+ *out++ = tab[ (in[0] & 0xfc) >> 2];
+ *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
+ *out++ = tab[ (in[1] & 0x0f) << 2];
+ *out++ = '=';
+ break;
+
+ case 1:
+ *out++ = tab[ (in[0] & 0xfc) >> 2];
+ *out++ = tab[((in[0] & 0x03) << 4)];
+ *out++ = '=';
+ *out++ = '=';
+ break;
+
+ case 0:
+ break;
+ }
+
+ return out - static_cast<char*>(dest);
+}
+
+/** Decode a padded base64 string into a series of octets.
+
+ @par Requires
+
+ The memory pointed to by `out` points to valid memory
+ of at least `decoded_size(len)` bytes.
+
+ @return The number of octets written to `out`, and
+ the number of characters read from the input string,
+ expressed as a pair.
+*/
+template<class = void>
+std::pair<std::size_t, std::size_t>
+decode(void* dest, char const* src, std::size_t len)
+{
+ char* out = static_cast<char*>(dest);
+ auto in = reinterpret_cast<unsigned char const*>(src);
+ unsigned char c3[3], c4[4];
+ int i = 0;
+ int j = 0;
+
+ auto const inverse = base64::get_inverse();
+
+ while(len-- && *in != '=')
+ {
+ auto const v = inverse[*in];
+ if(v == -1)
+ break;
+ ++in;
+ c4[i] = v;
+ if(++i == 4)
+ {
+ c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
+ c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
+ c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
+
+ for(i = 0; i < 3; i++)
+ *out++ = c3[i];
+ i = 0;
+ }
+ }
+
+ if(i)
+ {
+ c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4);
+ c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
+ c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
+
+ for(j = 0; j < i - 1; j++)
+ *out++ = c3[j];
+ }
+
+ return {out - static_cast<char*>(dest),
+ in - reinterpret_cast<unsigned char const*>(src)};
+}
+
+} // base64
+
+template<class = void>
+std::string
+base64_encode (std::uint8_t const* data,
+ std::size_t len)
+{
+ std::string dest;
+ dest.resize(base64::encoded_size(len));
+ dest.resize(base64::encode(&dest[0], data, len));
+ return dest;
+}
+
+inline
+std::string
+base64_encode(std::string const& s)
+{
+ return base64_encode (reinterpret_cast <
+ std::uint8_t const*> (s.data()), s.size());
+}
+
+template<class = void>
+std::string
+base64_decode(std::string const& data)
+{
+ std::string dest;
+ dest.resize(base64::decoded_size(data.size()));
+ auto const result = base64::decode(
+ &dest[0], data.data(), data.size());
+ dest.resize(result.first);
+ return dest;
+}
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/bind_handler.hpp b/boost/beast/core/detail/bind_handler.hpp
new file mode 100644
index 0000000000..5a9059b8e3
--- /dev/null
+++ b/boost/beast/core/detail/bind_handler.hpp
@@ -0,0 +1,189 @@
+//
+// 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_DETAIL_BIND_HANDLER_HPP
+#define BOOST_BEAST_DETAIL_BIND_HANDLER_HPP
+
+#include <boost/beast/core/detail/integer_sequence.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/core/ignore_unused.hpp>
+#include <functional>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+/* Nullary handler that calls Handler with bound arguments.
+
+ The bound handler provides the same io_context execution
+ guarantees as the original handler.
+*/
+template<class Handler, class... Args>
+class bound_handler
+{
+ // Can't friend partial specializations,
+ // so we just friend the whole thing.
+ template<class T, class Executor>
+ friend struct boost::asio::associated_executor;
+
+ using args_type = std::tuple<
+ typename std::decay<Args>::type...>;
+
+ Handler h_;
+ args_type args_;
+
+ template<class Arg, class Vals>
+ static
+ typename std::enable_if<
+ std::is_placeholder<typename
+ std::decay<Arg>::type>::value == 0,
+ Arg&&>::type
+ extract(Arg&& arg, Vals& vals)
+ {
+ boost::ignore_unused(vals);
+ return arg;
+ }
+
+ template<class Arg, class Vals>
+ static
+ typename std::enable_if<
+ std::is_placeholder<typename
+ std::decay<Arg>::type>::value != 0,
+ typename std::tuple_element<
+ std::is_placeholder<
+ typename std::decay<Arg>::type>::value - 1,
+ Vals>::type&&>::type
+ extract(Arg&&, Vals&& vals)
+ {
+ return std::get<std::is_placeholder<
+ typename std::decay<Arg>::type>::value - 1>(
+ std::forward<Vals>(vals));
+ }
+
+ template<
+ class ArgsTuple,
+ std::size_t... S>
+ static
+ void
+ invoke(
+ Handler& h,
+ ArgsTuple& args,
+ std::tuple<>&&,
+ index_sequence<S...>)
+ {
+ boost::ignore_unused(args);
+ h(std::get<S>(args)...);
+ }
+
+ template<
+ class ArgsTuple,
+ class ValsTuple,
+ std::size_t... S>
+ static
+ void
+ invoke(
+ Handler& h,
+ ArgsTuple& args,
+ ValsTuple&& vals,
+ index_sequence<S...>)
+ {
+ boost::ignore_unused(args);
+ boost::ignore_unused(vals);
+ h(extract(std::get<S>(args),
+ std::forward<ValsTuple>(vals))...);
+ }
+
+public:
+ using result_type = void;
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ bound_handler(bound_handler&&) = default;
+ bound_handler(bound_handler const&) = default;
+
+ template<class DeducedHandler>
+ explicit
+ bound_handler(
+ DeducedHandler&& handler, Args&&... args)
+ : h_(std::forward<DeducedHandler>(handler))
+ , args_(std::forward<Args>(args)...)
+ {
+ }
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ friend
+ bool
+ asio_handler_is_continuation(bound_handler* h)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(std::addressof(h->h_));
+ }
+
+ template<class... Values>
+ void
+ operator()(Values&&... values)
+ {
+ invoke(h_, args_,
+ std::forward_as_tuple(
+ std::forward<Values>(values)...),
+ index_sequence_for<Args...>());
+ }
+
+ template<class... Values>
+ void
+ operator()(Values&&... values) const
+ {
+ invoke(h_, args_,
+ std::forward_as_tuple(
+ std::forward<Values>(values)...),
+ index_sequence_for<Args...>());
+ }
+};
+
+} // detail
+} // beast
+
+namespace asio {
+template<class Handler, class... Args, class Executor>
+struct associated_executor<
+ beast::detail::bound_handler<Handler, Args...>, Executor>
+{
+ using type = typename
+ associated_executor<Handler, Executor>::type;
+
+ static
+ type
+ get(beast::detail::bound_handler<Handler, Args...> const& h,
+ Executor const& ex = Executor()) noexcept
+ {
+ return associated_executor<
+ Handler, Executor>::get(h.h_, ex);
+ }
+};
+} // asio
+
+} // boost
+
+namespace std {
+template<class Handler, class... Args>
+void
+bind(boost::beast::detail::bound_handler<
+ Handler, Args...>, ...) = delete;
+} // std
+
+#endif
diff --git a/boost/beast/core/detail/buffers_ref.hpp b/boost/beast/core/detail/buffers_ref.hpp
new file mode 100644
index 0000000000..e56a9764fd
--- /dev/null
+++ b/boost/beast/core/detail/buffers_ref.hpp
@@ -0,0 +1,67 @@
+//
+// 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_DETAIL_BUFFERS_REF_HPP
+#define BOOST_BEAST_DETAIL_BUFFERS_REF_HPP
+
+#include <boost/beast/core/type_traits.hpp>
+#include <iterator>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+// A very lightweight reference to a buffer sequence
+template<class BufferSequence>
+class buffers_ref
+{
+ BufferSequence const* buffers_;
+
+public:
+ using const_iterator = typename
+ buffer_sequence_iterator<BufferSequence>::type;
+
+ using value_type = typename std::iterator_traits<
+ const_iterator>::value_type;
+
+ buffers_ref(buffers_ref const&) = default;
+ buffers_ref& operator=(buffers_ref const&) = default;
+
+ explicit
+ buffers_ref(BufferSequence const& buffers)
+ : buffers_(std::addressof(buffers))
+ {
+ }
+
+ const_iterator
+ begin() const
+ {
+ return boost::asio::buffer_sequence_begin(*buffers_);
+ }
+
+ const_iterator
+ end() const
+ {
+ return boost::asio::buffer_sequence_end(*buffers_);
+ }
+};
+
+// Return a reference to a buffer sequence
+template<class BufferSequence>
+buffers_ref<BufferSequence>
+make_buffers_ref(BufferSequence const& buffers)
+{
+ return buffers_ref<BufferSequence>(buffers);
+}
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/clamp.hpp b/boost/beast/core/detail/clamp.hpp
new file mode 100644
index 0000000000..95a8fab425
--- /dev/null
+++ b/boost/beast/core/detail/clamp.hpp
@@ -0,0 +1,59 @@
+//
+// 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_CORE_DETAIL_CLAMP_HPP
+#define BOOST_BEAST_CORE_DETAIL_CLAMP_HPP
+
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+template<class UInt>
+static
+std::size_t
+clamp(UInt x)
+{
+ if(x >= (std::numeric_limits<std::size_t>::max)())
+ return (std::numeric_limits<std::size_t>::max)();
+ return static_cast<std::size_t>(x);
+}
+
+template<class UInt>
+static
+std::size_t
+clamp(UInt x, std::size_t limit)
+{
+ if(x >= limit)
+ return limit;
+ return static_cast<std::size_t>(x);
+}
+
+// return `true` if x + y > z, which are unsigned
+template<
+ class U1, class U2, class U3>
+constexpr
+bool
+sum_exceeds(U1 x, U2 y, U3 z)
+{
+ static_assert(
+ std::is_unsigned<U1>::value &&
+ std::is_unsigned<U2>::value &&
+ std::is_unsigned<U3>::value, "");
+ return y > z || x > z - y;
+}
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/config.hpp b/boost/beast/core/detail/config.hpp
new file mode 100644
index 0000000000..48f97e377f
--- /dev/null
+++ b/boost/beast/core/detail/config.hpp
@@ -0,0 +1,57 @@
+//
+// 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_CORE_DETAIL_CONFIG_HPP
+#define BOOST_BEAST_CORE_DETAIL_CONFIG_HPP
+
+#include <boost/config.hpp>
+#include <boost/version.hpp>
+
+// Available to every header
+#include <boost/config.hpp>
+#include <boost/core/ignore_unused.hpp>
+#include <boost/static_assert.hpp>
+
+/*
+ _MSC_VER and _MSC_FULL_VER by version:
+
+ 14.0 (2015) 1900 190023026
+ 14.0 (2015 Update 1) 1900 190023506
+ 14.0 (2015 Update 2) 1900 190023918
+ 14.0 (2015 Update 3) 1900 190024210
+*/
+
+#if defined(BOOST_MSVC)
+# if BOOST_MSVC_FULL_VER < 190024210
+# error Beast requires C++11: Visual Studio 2015 Update 3 or later needed
+# endif
+
+#elif defined(BOOST_GCC)
+# if(BOOST_GCC < 40801)
+# error Beast requires C++11: gcc version 4.8 or later needed
+# endif
+
+#else
+# if \
+ defined(BOOST_NO_CXX11_DECLTYPE) || \
+ defined(BOOST_NO_CXX11_HDR_TUPLE) || \
+ defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) || \
+ defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
+# error Beast requires C++11: a conforming compiler is needed
+# endif
+
+#endif
+
+#if BOOST_VERSION >= 106500 || ! defined(BOOST_GCC) || BOOST_GCC < 70000
+# define BOOST_BEAST_FALLTHROUGH BOOST_FALLTHROUGH
+#else
+# define BOOST_BEAST_FALLTHROUGH __attribute__((fallthrough))
+#endif
+
+#endif
diff --git a/boost/beast/core/detail/cpu_info.hpp b/boost/beast/core/detail/cpu_info.hpp
new file mode 100644
index 0000000000..579589fa8e
--- /dev/null
+++ b/boost/beast/core/detail/cpu_info.hpp
@@ -0,0 +1,99 @@
+//
+// Copyright (c) 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_DETAIL_CPU_INFO_HPP
+#define BOOST_BEAST_DETAIL_CPU_INFO_HPP
+
+#include <boost/config.hpp>
+
+#ifndef BOOST_BEAST_NO_INTRINSICS
+# if defined(BOOST_MSVC) || ((defined(BOOST_GCC) || defined(BOOST_CLANG)) && defined(__SSE4_2__))
+# define BOOST_BEAST_NO_INTRINSICS 0
+# else
+# define BOOST_BEAST_NO_INTRINSICS 1
+# endif
+#endif
+
+#if ! BOOST_BEAST_NO_INTRINSICS
+
+#ifdef BOOST_MSVC
+#include <intrin.h> // __cpuid
+#else
+#include <cpuid.h> // __get_cpuid
+#endif
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+/* Portions from Boost,
+ Copyright Andrey Semashev 2007 - 2015.
+*/
+template<class = void>
+void
+cpuid(
+ std::uint32_t id,
+ std::uint32_t& eax,
+ std::uint32_t& ebx,
+ std::uint32_t& ecx,
+ std::uint32_t& edx)
+{
+#ifdef BOOST_MSVC
+ int regs[4];
+ __cpuid(regs, id);
+ eax = regs[0];
+ ebx = regs[1];
+ ecx = regs[2];
+ edx = regs[3];
+#else
+ __get_cpuid(id, &eax, &ebx, &ecx, &edx);
+#endif
+}
+
+struct cpu_info
+{
+ bool sse42 = false;
+
+ cpu_info();
+};
+
+inline
+cpu_info::
+cpu_info()
+{
+ constexpr std::uint32_t SSE42 = 1 << 20;
+
+ std::uint32_t eax = 0;
+ std::uint32_t ebx = 0;
+ std::uint32_t ecx = 0;
+ std::uint32_t edx = 0;
+
+ cpuid(0, eax, ebx, ecx, edx);
+ if(eax >= 1)
+ {
+ cpuid(1, eax, ebx, ecx, edx);
+ sse42 = (ecx & SSE42) != 0;
+ }
+}
+
+template<class = void>
+cpu_info const&
+get_cpu_info()
+{
+ static cpu_info const ci;
+ return ci;
+}
+
+} // detail
+} // beast
+} // boost
+
+#endif
+
+#endif
diff --git a/boost/beast/core/detail/empty_base_optimization.hpp b/boost/beast/core/detail/empty_base_optimization.hpp
new file mode 100644
index 0000000000..b1e728b674
--- /dev/null
+++ b/boost/beast/core/detail/empty_base_optimization.hpp
@@ -0,0 +1,100 @@
+//
+// 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_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
+#define BOOST_BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
+
+#include <boost/type_traits/is_final.hpp>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+template<class T>
+struct is_empty_base_optimization_derived
+ : std::integral_constant<bool,
+ std::is_empty<T>::value &&
+ ! boost::is_final<T>::value>
+{
+};
+
+template<class T, int UniqueID = 0,
+ bool isDerived =
+ is_empty_base_optimization_derived<T>::value>
+class empty_base_optimization : private T
+{
+public:
+ empty_base_optimization() = default;
+ empty_base_optimization(empty_base_optimization&&) = default;
+ empty_base_optimization(empty_base_optimization const&) = default;
+ empty_base_optimization& operator=(empty_base_optimization&&) = default;
+ empty_base_optimization& operator=(empty_base_optimization const&) = default;
+
+ template<class Arg1, class... ArgN>
+ explicit
+ empty_base_optimization(Arg1&& arg1, ArgN&&... argn)
+ : T(std::forward<Arg1>(arg1),
+ std::forward<ArgN>(argn)...)
+ {
+ }
+
+ T& member() noexcept
+ {
+ return *this;
+ }
+
+ T const& member() const noexcept
+ {
+ return *this;
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<
+ class T,
+ int UniqueID
+>
+class empty_base_optimization <T, UniqueID, false>
+{
+ T t_;
+
+public:
+ empty_base_optimization() = default;
+ empty_base_optimization(empty_base_optimization&&) = default;
+ empty_base_optimization(empty_base_optimization const&) = default;
+ empty_base_optimization& operator=(empty_base_optimization&&) = default;
+ empty_base_optimization& operator=(empty_base_optimization const&) = default;
+
+ template<class Arg1, class... ArgN>
+ explicit
+ empty_base_optimization(Arg1&& arg1, ArgN&&... argn)
+ : t_(std::forward<Arg1>(arg1),
+ std::forward<ArgN>(argn)...)
+ {
+ }
+
+ T& member() noexcept
+ {
+ return t_;
+ }
+
+ T const& member() const noexcept
+ {
+ return t_;
+ }
+};
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/in_place_init.hpp b/boost/beast/core/detail/in_place_init.hpp
new file mode 100644
index 0000000000..d3a33becd5
--- /dev/null
+++ b/boost/beast/core/detail/in_place_init.hpp
@@ -0,0 +1,43 @@
+//
+// 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_DETAIL_IN_PLACE_INIT_HPP
+#define BOOST_BEAST_DETAIL_IN_PLACE_INIT_HPP
+
+#include <boost/version.hpp>
+#include <boost/optional/optional.hpp>
+
+// Provide boost::in_place_init_t and boost::in_place_init
+// for Boost versions earlier than 1.63.0.
+
+#if BOOST_VERSION < 106300
+
+namespace boost {
+
+namespace optional_ns {
+
+// a tag for in-place initialization of contained value
+struct in_place_init_t
+{
+ struct init_tag{};
+ explicit in_place_init_t(init_tag){}
+};
+const in_place_init_t in_place_init ((in_place_init_t::init_tag()));
+
+} // namespace optional_ns
+
+using optional_ns::in_place_init_t;
+using optional_ns::in_place_init;
+
+}
+
+#endif
+
+#endif
+
diff --git a/boost/beast/core/detail/integer_sequence.hpp b/boost/beast/core/detail/integer_sequence.hpp
new file mode 100644
index 0000000000..71664229af
--- /dev/null
+++ b/boost/beast/core/detail/integer_sequence.hpp
@@ -0,0 +1,143 @@
+//
+// 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_DETAIL_INTEGER_SEQUENCE_HPP
+#define BOOST_BEAST_DETAIL_INTEGER_SEQUENCE_HPP
+
+#include <boost/config.hpp>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+template<class T, T... Ints>
+struct integer_sequence
+{
+ using value_type = T;
+ BOOST_STATIC_ASSERT(std::is_integral<T>::value);
+
+ static std::size_t constexpr static_size = sizeof...(Ints);
+
+ static std::size_t constexpr size()
+ {
+ return sizeof...(Ints);
+ }
+};
+
+template<std::size_t... Ints>
+using index_sequence = integer_sequence<std::size_t, Ints...>;
+
+// This workaround is needed for broken sizeof...
+template<class... Args>
+struct sizeof_workaround
+{
+ static std::size_t constexpr size = sizeof... (Args);
+};
+
+#ifdef BOOST_MSVC
+
+// This implementation compiles on real MSVC and clang but not gcc
+
+template<class T, unsigned long long N, class Seq>
+struct make_integer_sequence_unchecked;
+
+template<class T, unsigned long long N, unsigned long long ...Indices>
+struct make_integer_sequence_unchecked<
+ T, N, integer_sequence<T, Indices...>>
+{
+ using type = typename make_integer_sequence_unchecked<
+ T, N-1, integer_sequence<T, N-1, Indices...>>::type;
+};
+
+template<class T, unsigned long long ...Indices>
+struct make_integer_sequence_unchecked<
+ T, 0, integer_sequence<T, Indices...>>
+{
+ using type = integer_sequence<T, Indices...>;
+};
+
+template<class T, T N>
+struct make_integer_sequence_checked
+{
+ BOOST_STATIC_ASSERT(std::is_integral<T>::value);
+ BOOST_STATIC_ASSERT(N >= 0);
+
+ using type = typename make_integer_sequence_unchecked<
+ T, N, integer_sequence<T>>::type;
+};
+
+template<class T, T N>
+using make_integer_sequence =
+ typename make_integer_sequence_checked<T, N>::type;
+
+template<std::size_t N>
+using make_index_sequence = make_integer_sequence<std::size_t, N>;
+
+template<class... Args>
+using index_sequence_for =
+ make_index_sequence<sizeof_workaround<Args...>::size>;
+
+#else
+
+// This implementation compiles on gcc but not MSVC
+
+template<std::size_t... Ints>
+struct index_tuple
+{
+ using next = index_tuple<Ints..., sizeof... (Ints)>;
+
+};
+
+template<std::size_t N>
+struct build_index_tuple
+{
+ using type = typename build_index_tuple<N-1>::type::next;
+};
+
+template<>
+struct build_index_tuple<0>
+{
+ using type = index_tuple<>;
+};
+
+template<class T, T N,
+ class Seq = typename build_index_tuple<N>::type
+>
+struct integer_sequence_helper;
+
+template<class T, T N, std::size_t... Ints>
+struct integer_sequence_helper<T, N, index_tuple<Ints...>>
+{
+ BOOST_STATIC_ASSERT(std::is_integral<T>::value);
+ BOOST_STATIC_ASSERT(N >= 0);
+
+ using type = integer_sequence<T, static_cast<T> (Ints)...>;
+};
+
+template<class T, T N>
+using make_integer_sequence =
+ typename integer_sequence_helper<T, N>::type;
+
+template<std::size_t N>
+using make_index_sequence = make_integer_sequence<std::size_t, N>;
+
+template<class... Args>
+using index_sequence_for =
+ make_index_sequence<sizeof_workaround<Args...>::size>;
+
+#endif
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/ostream.hpp b/boost/beast/core/detail/ostream.hpp
new file mode 100644
index 0000000000..8d6eb94f51
--- /dev/null
+++ b/boost/beast/core/detail/ostream.hpp
@@ -0,0 +1,319 @@
+//
+// 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_DETAIL_OSTREAM_HPP
+#define BOOST_BEAST_DETAIL_OSTREAM_HPP
+
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/read_size.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <memory>
+#include <iosfwd>
+#include <streambuf>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+template<class Buffers>
+class buffers_helper
+{
+ Buffers b_;
+
+public:
+ explicit
+ buffers_helper(Buffers const& b)
+ : b_(b)
+ {
+ }
+
+ template<class B>
+ friend
+ std::ostream&
+ operator<<(std::ostream& os,
+ buffers_helper<B> const& v);
+};
+
+template<class Buffers>
+std::ostream&
+operator<<(std::ostream& os,
+ buffers_helper<Buffers> const& v)
+{
+ for(auto b : buffers_range(v.b_))
+ os.write(
+ reinterpret_cast<char const*>(b.data()),
+ b.size());
+ return os;
+}
+
+//------------------------------------------------------------------------------
+
+struct basic_streambuf_movable_helper :
+ std::basic_streambuf<char, std::char_traits<char>>
+{
+ basic_streambuf_movable_helper(
+ basic_streambuf_movable_helper&&) = default;
+};
+
+using basic_streambuf_movable =
+ std::is_move_constructible<basic_streambuf_movable_helper>;
+
+//------------------------------------------------------------------------------
+
+template<class DynamicBuffer,
+ class CharT, class Traits, bool isMovable>
+class ostream_buffer;
+
+template<class DynamicBuffer, class CharT, class Traits>
+class ostream_buffer
+ <DynamicBuffer, CharT, Traits, true>
+ : public std::basic_streambuf<CharT, Traits>
+{
+ using int_type = typename
+ std::basic_streambuf<CharT, Traits>::int_type;
+
+ using traits_type = typename
+ std::basic_streambuf<CharT, Traits>::traits_type;
+
+ static std::size_t constexpr max_size = 512;
+
+ DynamicBuffer& buf_;
+
+public:
+ ostream_buffer(ostream_buffer&&) = default;
+ ostream_buffer(ostream_buffer const&) = delete;
+
+ ~ostream_buffer() noexcept
+ {
+ sync();
+ }
+
+ explicit
+ ostream_buffer(DynamicBuffer& buf)
+ : buf_(buf)
+ {
+ prepare();
+ }
+
+ int_type
+ overflow(int_type ch) override
+ {
+ if(! Traits::eq_int_type(ch, Traits::eof()))
+ {
+ Traits::assign(*this->pptr(),
+ static_cast<CharT>(ch));
+ flush(1);
+ prepare();
+ return ch;
+ }
+ flush();
+ return traits_type::eof();
+ }
+
+ int
+ sync() override
+ {
+ flush();
+ prepare();
+ return 0;
+ }
+
+private:
+ void
+ prepare()
+ {
+ auto bs = buf_.prepare(
+ read_size_or_throw(buf_, max_size));
+ auto const b = buffers_front(bs);
+ auto const p = reinterpret_cast<CharT*>(b.data());
+ this->setp(p,
+ p + b.size() / sizeof(CharT) - 1);
+ }
+
+ void
+ flush(int extra = 0)
+ {
+ buf_.commit(
+ (this->pptr() - this->pbase() + extra) *
+ sizeof(CharT));
+ }
+};
+
+// This nonsense is all to work around a glitch in libstdc++
+// where std::basic_streambuf copy constructor is private:
+// https://github.com/gcc-mirror/gcc/blob/gcc-4_8-branch/libstdc%2B%2B-v3/include/std/streambuf#L799
+
+template<class DynamicBuffer, class CharT, class Traits>
+class ostream_buffer
+ <DynamicBuffer, CharT, Traits, false>
+ : public std::basic_streambuf<CharT, Traits>
+{
+ using int_type = typename
+ std::basic_streambuf<CharT, Traits>::int_type;
+
+ using traits_type = typename
+ std::basic_streambuf<CharT, Traits>::traits_type;
+
+ static std::size_t constexpr max_size = 512;
+
+ DynamicBuffer& buf_;
+
+public:
+ ostream_buffer(ostream_buffer&&) = delete;
+ ostream_buffer(ostream_buffer const&) = delete;
+
+ ~ostream_buffer() noexcept
+ {
+ sync();
+ }
+
+ explicit
+ ostream_buffer(DynamicBuffer& buf)
+ : buf_(buf)
+ {
+ prepare();
+ }
+
+ int_type
+ overflow(int_type ch) override
+ {
+ if(! Traits::eq_int_type(ch, Traits::eof()))
+ {
+ Traits::assign(*this->pptr(),
+ static_cast<CharT>(ch));
+ flush(1);
+ prepare();
+ return ch;
+ }
+ flush();
+ return traits_type::eof();
+ }
+
+ int
+ sync() override
+ {
+ flush();
+ prepare();
+ return 0;
+ }
+
+private:
+ void
+ prepare()
+ {
+ auto bs = buf_.prepare(
+ read_size_or_throw(buf_, max_size));
+ auto const b = buffers_front(bs);
+ auto const p = reinterpret_cast<CharT*>(b.data());
+ this->setp(p,
+ p + b.size() / sizeof(CharT) - 1);
+ }
+
+ void
+ flush(int extra = 0)
+ {
+ buf_.commit(
+ (this->pptr() - this->pbase() + extra) *
+ sizeof(CharT));
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class DynamicBuffer,
+ class CharT, class Traits, bool isMovable>
+class ostream_helper;
+
+template<class DynamicBuffer, class CharT, class Traits>
+class ostream_helper<
+ DynamicBuffer, CharT, Traits, true>
+ : public std::basic_ostream<CharT, Traits>
+{
+ ostream_buffer<
+ DynamicBuffer, CharT, Traits, true> osb_;
+
+public:
+ explicit
+ ostream_helper(DynamicBuffer& buf);
+
+ ostream_helper(ostream_helper&& other);
+};
+
+template<class DynamicBuffer, class CharT, class Traits>
+ostream_helper<DynamicBuffer, CharT, Traits, true>::
+ostream_helper(DynamicBuffer& buf)
+ : std::basic_ostream<CharT, Traits>(
+ &this->osb_)
+ , osb_(buf)
+{
+}
+
+template<class DynamicBuffer, class CharT, class Traits>
+ostream_helper<DynamicBuffer, CharT, Traits, true>::
+ostream_helper(
+ ostream_helper&& other)
+ : std::basic_ostream<CharT, Traits>(&osb_)
+ , osb_(std::move(other.osb_))
+{
+}
+
+// This work-around is for libstdc++ versions that
+// don't have a movable std::basic_streambuf
+
+template<class T>
+class ostream_helper_base
+{
+protected:
+ std::unique_ptr<T> member;
+
+ ostream_helper_base(
+ ostream_helper_base&&) = default;
+
+ explicit
+ ostream_helper_base(T* t)
+ : member(t)
+ {
+ }
+};
+
+template<class DynamicBuffer, class CharT, class Traits>
+class ostream_helper<
+ DynamicBuffer, CharT, Traits, false>
+ : private ostream_helper_base<ostream_buffer<
+ DynamicBuffer, CharT, Traits, false>>
+ , public std::basic_ostream<CharT, Traits>
+{
+public:
+ explicit
+ ostream_helper(DynamicBuffer& buf)
+ : ostream_helper_base<ostream_buffer<
+ DynamicBuffer, CharT, Traits, false>>(
+ new ostream_buffer<DynamicBuffer,
+ CharT, Traits, false>(buf))
+ , std::basic_ostream<CharT, Traits>(this->member.get())
+ {
+ }
+
+ ostream_helper(ostream_helper&& other)
+ : ostream_helper_base<ostream_buffer<
+ DynamicBuffer, CharT, Traits, false>>(
+ std::move(other))
+ , std::basic_ostream<CharT, Traits>(this->member.get())
+ {
+ }
+};
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/sha1.hpp b/boost/beast/core/detail/sha1.hpp
new file mode 100644
index 0000000000..336da2a0f2
--- /dev/null
+++ b/boost/beast/core/detail/sha1.hpp
@@ -0,0 +1,313 @@
+//
+// 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_DETAIL_SHA1_HPP
+#define BOOST_BEAST_DETAIL_SHA1_HPP
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+
+// Based on https://github.com/vog/sha1
+/*
+ Original authors:
+ Steve Reid (Original C Code)
+ Bruce Guenter (Small changes to fit into bglibs)
+ Volker Grabsch (Translation to simpler C++ Code)
+ Eugene Hopkinson (Safety improvements)
+ Vincent Falco (beast adaptation)
+*/
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+namespace sha1 {
+
+static std::size_t constexpr BLOCK_INTS = 16;
+static std::size_t constexpr BLOCK_BYTES = 64;
+static std::size_t constexpr DIGEST_BYTES = 20;
+
+inline
+std::uint32_t
+rol(std::uint32_t value, std::size_t bits)
+{
+ return (value << bits) | (value >> (32 - bits));
+}
+
+inline
+std::uint32_t
+blk(std::uint32_t block[BLOCK_INTS], std::size_t i)
+{
+ return rol(
+ block[(i+13)&15] ^ block[(i+8)&15] ^
+ block[(i+2)&15] ^ block[i], 1);
+}
+
+inline
+void
+R0(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
+ std::uint32_t &w, std::uint32_t x, std::uint32_t y,
+ std::uint32_t &z, std::size_t i)
+{
+ z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
+ w = rol(w, 30);
+}
+
+
+inline
+void
+R1(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
+ std::uint32_t &w, std::uint32_t x, std::uint32_t y,
+ std::uint32_t &z, std::size_t i)
+{
+ block[i] = blk(block, i);
+ z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
+ w = rol(w, 30);
+}
+
+inline
+void
+R2(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
+ std::uint32_t &w, std::uint32_t x, std::uint32_t y,
+ std::uint32_t &z, std::size_t i)
+{
+ block[i] = blk(block, i);
+ z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5);
+ w = rol(w, 30);
+}
+
+inline
+void
+R3(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
+ std::uint32_t &w, std::uint32_t x, std::uint32_t y,
+ std::uint32_t &z, std::size_t i)
+{
+ block[i] = blk(block, i);
+ z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5);
+ w = rol(w, 30);
+}
+
+inline
+void
+R4(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
+ std::uint32_t &w, std::uint32_t x, std::uint32_t y,
+ std::uint32_t &z, std::size_t i)
+{
+ block[i] = blk(block, i);
+ z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5);
+ w = rol(w, 30);
+}
+
+inline
+void
+make_block(std::uint8_t const* p,
+ std::uint32_t block[BLOCK_INTS])
+{
+ for(std::size_t i = 0; i < BLOCK_INTS; i++)
+ block[i] =
+ (static_cast<std::uint32_t>(p[4*i+3])) |
+ (static_cast<std::uint32_t>(p[4*i+2]))<< 8 |
+ (static_cast<std::uint32_t>(p[4*i+1]))<<16 |
+ (static_cast<std::uint32_t>(p[4*i+0]))<<24;
+}
+
+template<class = void>
+void
+transform(
+ std::uint32_t digest[], std::uint32_t block[BLOCK_INTS])
+{
+ std::uint32_t a = digest[0];
+ std::uint32_t b = digest[1];
+ std::uint32_t c = digest[2];
+ std::uint32_t d = digest[3];
+ std::uint32_t e = digest[4];
+
+ R0(block, a, b, c, d, e, 0);
+ R0(block, e, a, b, c, d, 1);
+ R0(block, d, e, a, b, c, 2);
+ R0(block, c, d, e, a, b, 3);
+ R0(block, b, c, d, e, a, 4);
+ R0(block, a, b, c, d, e, 5);
+ R0(block, e, a, b, c, d, 6);
+ R0(block, d, e, a, b, c, 7);
+ R0(block, c, d, e, a, b, 8);
+ R0(block, b, c, d, e, a, 9);
+ R0(block, a, b, c, d, e, 10);
+ R0(block, e, a, b, c, d, 11);
+ R0(block, d, e, a, b, c, 12);
+ R0(block, c, d, e, a, b, 13);
+ R0(block, b, c, d, e, a, 14);
+ R0(block, a, b, c, d, e, 15);
+ R1(block, e, a, b, c, d, 0);
+ R1(block, d, e, a, b, c, 1);
+ R1(block, c, d, e, a, b, 2);
+ R1(block, b, c, d, e, a, 3);
+ R2(block, a, b, c, d, e, 4);
+ R2(block, e, a, b, c, d, 5);
+ R2(block, d, e, a, b, c, 6);
+ R2(block, c, d, e, a, b, 7);
+ R2(block, b, c, d, e, a, 8);
+ R2(block, a, b, c, d, e, 9);
+ R2(block, e, a, b, c, d, 10);
+ R2(block, d, e, a, b, c, 11);
+ R2(block, c, d, e, a, b, 12);
+ R2(block, b, c, d, e, a, 13);
+ R2(block, a, b, c, d, e, 14);
+ R2(block, e, a, b, c, d, 15);
+ R2(block, d, e, a, b, c, 0);
+ R2(block, c, d, e, a, b, 1);
+ R2(block, b, c, d, e, a, 2);
+ R2(block, a, b, c, d, e, 3);
+ R2(block, e, a, b, c, d, 4);
+ R2(block, d, e, a, b, c, 5);
+ R2(block, c, d, e, a, b, 6);
+ R2(block, b, c, d, e, a, 7);
+ R3(block, a, b, c, d, e, 8);
+ R3(block, e, a, b, c, d, 9);
+ R3(block, d, e, a, b, c, 10);
+ R3(block, c, d, e, a, b, 11);
+ R3(block, b, c, d, e, a, 12);
+ R3(block, a, b, c, d, e, 13);
+ R3(block, e, a, b, c, d, 14);
+ R3(block, d, e, a, b, c, 15);
+ R3(block, c, d, e, a, b, 0);
+ R3(block, b, c, d, e, a, 1);
+ R3(block, a, b, c, d, e, 2);
+ R3(block, e, a, b, c, d, 3);
+ R3(block, d, e, a, b, c, 4);
+ R3(block, c, d, e, a, b, 5);
+ R3(block, b, c, d, e, a, 6);
+ R3(block, a, b, c, d, e, 7);
+ R3(block, e, a, b, c, d, 8);
+ R3(block, d, e, a, b, c, 9);
+ R3(block, c, d, e, a, b, 10);
+ R3(block, b, c, d, e, a, 11);
+ R4(block, a, b, c, d, e, 12);
+ R4(block, e, a, b, c, d, 13);
+ R4(block, d, e, a, b, c, 14);
+ R4(block, c, d, e, a, b, 15);
+ R4(block, b, c, d, e, a, 0);
+ R4(block, a, b, c, d, e, 1);
+ R4(block, e, a, b, c, d, 2);
+ R4(block, d, e, a, b, c, 3);
+ R4(block, c, d, e, a, b, 4);
+ R4(block, b, c, d, e, a, 5);
+ R4(block, a, b, c, d, e, 6);
+ R4(block, e, a, b, c, d, 7);
+ R4(block, d, e, a, b, c, 8);
+ R4(block, c, d, e, a, b, 9);
+ R4(block, b, c, d, e, a, 10);
+ R4(block, a, b, c, d, e, 11);
+ R4(block, e, a, b, c, d, 12);
+ R4(block, d, e, a, b, c, 13);
+ R4(block, c, d, e, a, b, 14);
+ R4(block, b, c, d, e, a, 15);
+
+ digest[0] += a;
+ digest[1] += b;
+ digest[2] += c;
+ digest[3] += d;
+ digest[4] += e;
+}
+
+} // sha1
+
+struct sha1_context
+{
+ static unsigned int constexpr block_size = sha1::BLOCK_BYTES;
+ static unsigned int constexpr digest_size = 20;
+
+ std::size_t buflen;
+ std::size_t blocks;
+ std::uint32_t digest[5];
+ std::uint8_t buf[block_size];
+};
+
+template<class = void>
+void
+init(sha1_context& ctx) noexcept
+{
+ ctx.buflen = 0;
+ ctx.blocks = 0;
+ ctx.digest[0] = 0x67452301;
+ ctx.digest[1] = 0xefcdab89;
+ ctx.digest[2] = 0x98badcfe;
+ ctx.digest[3] = 0x10325476;
+ ctx.digest[4] = 0xc3d2e1f0;
+}
+
+template<class = void>
+void
+update(sha1_context& ctx,
+ void const* message, std::size_t size) noexcept
+{
+ auto p = reinterpret_cast<
+ std::uint8_t const*>(message);
+ for(;;)
+ {
+ auto const n = (std::min)(
+ size, sizeof(ctx.buf) - ctx.buflen);
+ std::memcpy(ctx.buf + ctx.buflen, p, n);
+ ctx.buflen += n;
+ if(ctx.buflen != 64)
+ return;
+ p += n;
+ size -= n;
+ ctx.buflen = 0;
+ std::uint32_t block[sha1::BLOCK_INTS];
+ sha1::make_block(ctx.buf, block);
+ sha1::transform(ctx.digest, block);
+ ++ctx.blocks;
+ }
+}
+
+template<class = void>
+void
+finish(sha1_context& ctx, void* digest) noexcept
+{
+ using sha1::BLOCK_INTS;
+ using sha1::BLOCK_BYTES;
+
+ std::uint64_t total_bits =
+ (ctx.blocks*64 + ctx.buflen) * 8;
+ // pad
+ ctx.buf[ctx.buflen++] = 0x80;
+ auto const buflen = ctx.buflen;
+ while(ctx.buflen < 64)
+ ctx.buf[ctx.buflen++] = 0x00;
+ std::uint32_t block[BLOCK_INTS];
+ sha1::make_block(ctx.buf, block);
+ if(buflen > BLOCK_BYTES - 8)
+ {
+ sha1::transform(ctx.digest, block);
+ for(size_t i = 0; i < BLOCK_INTS - 2; i++)
+ block[i] = 0;
+ }
+
+ /* Append total_bits, split this uint64_t into two uint32_t */
+ block[BLOCK_INTS - 1] = total_bits & 0xffffffff;
+ block[BLOCK_INTS - 2] = (total_bits >> 32);
+ sha1::transform(ctx.digest, block);
+ for(std::size_t i = 0; i < sha1::DIGEST_BYTES/4; i++)
+ {
+ std::uint8_t* d =
+ reinterpret_cast<std::uint8_t*>(digest) + 4 * i;
+ d[3] = ctx.digest[i] & 0xff;
+ d[2] = (ctx.digest[i] >> 8) & 0xff;
+ d[1] = (ctx.digest[i] >> 16) & 0xff;
+ d[0] = (ctx.digest[i] >> 24) & 0xff;
+ }
+}
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/static_ostream.hpp b/boost/beast/core/detail/static_ostream.hpp
new file mode 100644
index 0000000000..cb5a00b3da
--- /dev/null
+++ b/boost/beast/core/detail/static_ostream.hpp
@@ -0,0 +1,142 @@
+//
+// 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_DETAIL_STATIC_OSTREAM_HPP
+#define BOOST_BEAST_DETAIL_STATIC_OSTREAM_HPP
+
+#include <locale>
+#include <ostream>
+#include <streambuf>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+// http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
+
+class static_ostream_buffer
+ : public std::basic_streambuf<char>
+{
+ using CharT = char;
+ using Traits = std::char_traits<CharT>;
+ using int_type = typename
+ std::basic_streambuf<CharT, Traits>::int_type;
+ using traits_type = typename
+ std::basic_streambuf<CharT, Traits>::traits_type;
+
+ char* data_;
+ std::size_t size_;
+ std::size_t len_ = 0;
+ std::string s_;
+
+public:
+ static_ostream_buffer(static_ostream_buffer&&) = delete;
+ static_ostream_buffer(static_ostream_buffer const&) = delete;
+
+ static_ostream_buffer(char* data, std::size_t size)
+ : data_(data)
+ , size_(size)
+ {
+ this->setp(data_, data_ + size - 1);
+ }
+
+ ~static_ostream_buffer() noexcept
+ {
+ }
+
+ string_view
+ str() const
+ {
+ if(! s_.empty())
+ return {s_.data(), len_};
+ return {data_, len_};
+ }
+
+ int_type
+ overflow(int_type ch) override
+ {
+ if(! Traits::eq_int_type(ch, Traits::eof()))
+ {
+ Traits::assign(*this->pptr(),
+ static_cast<CharT>(ch));
+ flush(1);
+ prepare();
+ return ch;
+ }
+ flush();
+ return traits_type::eof();
+ }
+
+ int
+ sync() override
+ {
+ flush();
+ prepare();
+ return 0;
+ }
+
+private:
+ void
+ prepare()
+ {
+ static auto const growth_factor = 1.5;
+
+ if(len_ < size_ - 1)
+ {
+ this->setp(
+ data_ + len_, data_ + size_ - 2);
+ return;
+ }
+ if(s_.empty())
+ {
+ s_.resize(static_cast<std::size_t>(
+ growth_factor * len_));
+ Traits::copy(&s_[0], data_, len_);
+ }
+ else
+ {
+ s_.resize(static_cast<std::size_t>(
+ growth_factor * len_));
+ }
+ this->setp(&s_[len_], &s_[len_] +
+ s_.size() - len_ - 1);
+ }
+
+ void
+ flush(int extra = 0)
+ {
+ len_ += static_cast<std::size_t>(
+ this->pptr() - this->pbase() + extra);
+ }
+};
+
+class static_ostream : public std::basic_ostream<char>
+{
+ static_ostream_buffer osb_;
+
+public:
+ static_ostream(char* data, std::size_t size)
+ : std::basic_ostream<char>(&this->osb_)
+ , osb_(data, size)
+ {
+ imbue(std::locale::classic());
+ }
+
+ string_view
+ str() const
+ {
+ return osb_.str();
+ }
+};
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/static_string.hpp b/boost/beast/core/detail/static_string.hpp
new file mode 100644
index 0000000000..a5384591ff
--- /dev/null
+++ b/boost/beast/core/detail/static_string.hpp
@@ -0,0 +1,135 @@
+//
+// 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_DETAIL_STATIC_STRING_HPP
+#define BOOST_BEAST_DETAIL_STATIC_STRING_HPP
+
+#include <boost/beast/core/string.hpp>
+#include <boost/assert.hpp>
+#include <iterator>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+// Because k-ballo said so
+template<class T>
+using is_input_iterator =
+ std::integral_constant<bool,
+ ! std::is_integral<T>::value>;
+
+template<class CharT, class Traits>
+int
+lexicographical_compare(
+ CharT const* s1, std::size_t n1,
+ CharT const* s2, std::size_t n2)
+{
+ if(n1 < n2)
+ return Traits::compare(
+ s1, s2, n1) <= 0 ? -1 : 1;
+ if(n1 > n2)
+ return Traits::compare(
+ s1, s2, n2) >= 0 ? 1 : -1;
+ return Traits::compare(s1, s2, n1);
+}
+
+template<class CharT, class Traits>
+inline
+int
+lexicographical_compare(
+ basic_string_view<CharT, Traits> s1,
+ CharT const* s2, std::size_t n2)
+{
+ return lexicographical_compare<CharT, Traits>(
+ s1.data(), s1.size(), s2, n2);
+}
+
+template<class CharT, class Traits>
+inline
+int
+lexicographical_compare(
+ basic_string_view<CharT, Traits> s1,
+ basic_string_view<CharT, Traits> s2)
+{
+ return lexicographical_compare<CharT, Traits>(
+ s1.data(), s1.size(), s2.data(), s2.size());
+}
+
+// Maximum number of characters in the decimal
+// representation of a binary number. This includes
+// the potential minus sign.
+//
+inline
+std::size_t constexpr
+max_digits(std::size_t bytes)
+{
+ return static_cast<std::size_t>(
+ bytes * 2.41) + 1 + 1;
+}
+
+template<class CharT, class Integer, class Traits>
+CharT*
+raw_to_string(
+ CharT* buf, Integer x, std::true_type)
+{
+ if(x == 0)
+ {
+ Traits::assign(*--buf, '0');
+ return buf;
+ }
+ if(x < 0)
+ {
+ x = -x;
+ for(;x > 0; x /= 10)
+ Traits::assign(*--buf ,
+ "0123456789"[x % 10]);
+ Traits::assign(*--buf, '-');
+ return buf;
+ }
+ for(;x > 0; x /= 10)
+ Traits::assign(*--buf ,
+ "0123456789"[x % 10]);
+ return buf;
+}
+
+template<class CharT, class Integer, class Traits>
+CharT*
+raw_to_string(
+ CharT* buf, Integer x, std::false_type)
+{
+ if(x == 0)
+ {
+ *--buf = '0';
+ return buf;
+ }
+ for(;x > 0; x /= 10)
+ Traits::assign(*--buf ,
+ "0123456789"[x % 10]);
+ return buf;
+}
+
+template<
+ class CharT,
+ class Integer,
+ class Traits = std::char_traits<CharT>>
+CharT*
+raw_to_string(CharT* last, std::size_t size, Integer i)
+{
+ boost::ignore_unused(size);
+ BOOST_ASSERT(size >= max_digits(sizeof(Integer)));
+ return raw_to_string<CharT, Integer, Traits>(
+ last, i, std::is_signed<Integer>{});
+}
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/type_traits.hpp b/boost/beast/core/detail/type_traits.hpp
new file mode 100644
index 0000000000..06fbda291d
--- /dev/null
+++ b/boost/beast/core/detail/type_traits.hpp
@@ -0,0 +1,353 @@
+//
+// 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_DETAIL_TYPE_TRAITS_HPP
+#define BOOST_BEAST_DETAIL_TYPE_TRAITS_HPP
+
+#include <boost/beast/core/error.hpp>
+#include <boost/asio/buffer.hpp>
+#include <iterator>
+#include <tuple>
+#include <type_traits>
+#include <string>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+//
+// utilities
+//
+
+template<class... Ts>
+struct make_void
+{
+ using type = void;
+};
+
+template<class... Ts>
+using void_t = typename make_void<Ts...>::type;
+
+template<class T>
+inline
+void
+accept_rv(T){}
+
+template<class U>
+std::size_t constexpr
+max_sizeof()
+{
+ return sizeof(U);
+}
+
+template<class U0, class U1, class... Us>
+std::size_t constexpr
+max_sizeof()
+{
+ return
+ max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
+ max_sizeof<U0>() : max_sizeof<U1, Us...>();
+}
+
+template<class U>
+std::size_t constexpr
+max_alignof()
+{
+ return alignof(U);
+}
+
+template<class U0, class U1, class... Us>
+std::size_t constexpr
+max_alignof()
+{
+ return
+ max_alignof<U0>() > max_alignof<U1, Us...>() ?
+ max_alignof<U0>() : max_alignof<U1, Us...>();
+}
+
+template<unsigned N, class T, class... Tn>
+struct repeat_tuple_impl
+{
+ using type = typename repeat_tuple_impl<
+ N - 1, T, T, Tn...>::type;
+};
+
+template<class T, class... Tn>
+struct repeat_tuple_impl<0, T, Tn...>
+{
+ using type = std::tuple<T, Tn...>;
+};
+
+template<unsigned N, class T>
+struct repeat_tuple
+{
+ using type =
+ typename repeat_tuple_impl<N-1, T>::type;
+};
+
+template<class T>
+struct repeat_tuple<0, T>
+{
+ using type = std::tuple<>;
+};
+
+template<class R, class C, class ...A>
+auto
+is_invocable_test(C&& c, int, A&& ...a)
+ -> decltype(std::is_convertible<
+ decltype(c(std::forward<A>(a)...)), R>::value ||
+ std::is_same<R, void>::value,
+ std::true_type());
+
+template<class R, class C, class ...A>
+std::false_type
+is_invocable_test(C&& c, long, A&& ...a);
+
+/** Metafunction returns `true` if F callable as R(A...)
+
+ Example:
+
+ @code
+ is_invocable<T, void(std::string)>
+ @endcode
+*/
+/** @{ */
+template<class C, class F>
+struct is_invocable : std::false_type
+{
+};
+
+template<class C, class R, class ...A>
+struct is_invocable<C, R(A...)>
+ : decltype(is_invocable_test<R>(
+ std::declval<C>(), 1, std::declval<A>()...))
+{
+};
+/** @} */
+
+// for span
+template<class T, class E, class = void>
+struct is_contiguous_container: std::false_type {};
+
+template<class T, class E>
+struct is_contiguous_container<T, E, void_t<
+ decltype(
+ std::declval<std::size_t&>() = std::declval<T const&>().size(),
+ std::declval<E*&>() = std::declval<T&>().data(),
+ (void)0),
+ typename std::enable_if<
+ std::is_same<
+ typename std::remove_cv<E>::type,
+ typename std::remove_cv<
+ typename std::remove_pointer<
+ decltype(std::declval<T&>().data())
+ >::type
+ >::type
+ >::value
+ >::type>>: std::true_type
+{};
+
+template<class...>
+struct unwidest_unsigned;
+
+template<class U0>
+struct unwidest_unsigned<U0>
+{
+ using type = U0;
+};
+
+template<class U0, class... UN>
+struct unwidest_unsigned<U0, UN...>
+{
+ BOOST_STATIC_ASSERT(std::is_unsigned<U0>::value);
+ using type = typename std::conditional<
+ (sizeof(U0) < sizeof(typename unwidest_unsigned<UN...>::type)),
+ U0, typename unwidest_unsigned<UN...>::type>::type;
+};
+
+template<class...>
+struct widest_unsigned;
+
+template<class U0>
+struct widest_unsigned<U0>
+{
+ using type = U0;
+};
+
+template<class U0, class... UN>
+struct widest_unsigned<U0, UN...>
+{
+ BOOST_STATIC_ASSERT(std::is_unsigned<U0>::value);
+ using type = typename std::conditional<
+ (sizeof(U0) > sizeof(typename widest_unsigned<UN...>::type)),
+ U0, typename widest_unsigned<UN...>::type>::type;
+};
+
+template<class U>
+inline
+constexpr
+U
+min_all(U u)
+{
+ BOOST_STATIC_ASSERT(std::is_unsigned<U>::value);
+ return u;
+}
+
+template<class U0, class U1, class... UN>
+inline
+constexpr
+typename unwidest_unsigned<U0, U1, UN...>::type
+min_all(U0 u0, U1 u1, UN... un)
+{
+ using type =
+ typename unwidest_unsigned<U0, U1, UN...>::type;
+ return u0 < u1 ?
+ static_cast<type>(min_all(u0, un...)) :
+ static_cast<type>(min_all(u1, un...));
+}
+
+template<class U>
+inline
+constexpr
+U
+max_all(U u)
+{
+ BOOST_STATIC_ASSERT(std::is_unsigned<U>::value);
+ return u;
+}
+
+template<class U0, class U1, class... UN>
+inline
+constexpr
+typename widest_unsigned<U0, U1, UN...>::type
+max_all(U0 u0, U1 u1, UN... un)
+{
+ return u0 > u1? max_all(u0, un...) : max_all(u1, un...);
+}
+
+//------------------------------------------------------------------------------
+
+//
+// buffer concepts
+//
+
+// Types that meet the requirements,
+// for use with std::declval only.
+template<class BufferType>
+struct BufferSequence
+{
+ using value_type = BufferType;
+ using const_iterator = BufferType const*;
+ ~BufferSequence();
+ BufferSequence(BufferSequence const&) = default;
+ const_iterator begin() const noexcept;
+ const_iterator end() const noexcept;
+};
+using ConstBufferSequence =
+ BufferSequence<boost::asio::const_buffer>;
+using MutableBufferSequence =
+ BufferSequence<boost::asio::mutable_buffer>;
+
+template<class B1, class... Bn>
+struct is_all_const_buffer_sequence
+ : std::integral_constant<bool,
+ boost::asio::is_const_buffer_sequence<B1>::value &&
+ is_all_const_buffer_sequence<Bn...>::value>
+{
+};
+
+template<class B>
+struct is_all_const_buffer_sequence<B>
+ : boost::asio::is_const_buffer_sequence<B>
+{
+};
+
+template<class... Bn>
+struct common_buffers_type
+{
+ using type = typename std::conditional<
+ std::is_convertible<std::tuple<Bn...>,
+ typename repeat_tuple<sizeof...(Bn),
+ boost::asio::mutable_buffer>::type>::value,
+ boost::asio::mutable_buffer,
+ boost::asio::const_buffer>::type;
+};
+
+template<class B>
+struct buffer_sequence_iterator
+{
+ using type = decltype(
+ boost::asio::buffer_sequence_begin(
+ std::declval<B const&>()));
+};
+
+// Types that meet the requirements,
+// for use with std::declval only.
+struct StreamHandler
+{
+ StreamHandler(StreamHandler const&) = default;
+ void operator()(error_code ec, std::size_t);
+};
+using ReadHandler = StreamHandler;
+using WriteHandler = StreamHandler;
+
+template<class Buffers>
+class buffers_range_adapter
+{
+ Buffers const& b_;
+
+public:
+ using value_type = typename std::conditional<
+ std::is_convertible<typename std::iterator_traits<
+ typename buffer_sequence_iterator<Buffers>::type>::value_type,
+ boost::asio::const_buffer>::value,
+ boost::asio::const_buffer,
+ boost::asio::mutable_buffer>::type;
+
+ /* VFALCO This isn't right, because range-for will pick up the iterator's
+ value_type which might not be const_buffer or mutable_buffer. We
+ need to declare our own iterator wrapper that converts the underlying
+ iterator's value_type to const_buffer or mutable_buffer so that
+ range-for sees one of those types.
+ */
+ using const_iterator = typename
+ buffer_sequence_iterator<Buffers>::type;
+
+ explicit
+ buffers_range_adapter(Buffers const& b)
+ : b_(b)
+ {
+ }
+
+ const_iterator
+ begin() const noexcept
+ {
+ return boost::asio::buffer_sequence_begin(b_);
+ }
+
+ const_iterator
+ end() const noexcept
+ {
+ return boost::asio::buffer_sequence_end(b_);
+ }
+};
+
+template<class Buffers>
+buffers_range_adapter<Buffers>
+buffers_range(Buffers const& buffers)
+{
+ return buffers_range_adapter<Buffers>{buffers};
+}
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/variant.hpp b/boost/beast/core/detail/variant.hpp
new file mode 100644
index 0000000000..cba6ba63c0
--- /dev/null
+++ b/boost/beast/core/detail/variant.hpp
@@ -0,0 +1,195 @@
+//
+// 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_DETAIL_VARIANT_HPP
+#define BOOST_BEAST_DETAIL_VARIANT_HPP
+
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/assert.hpp>
+#include <cstddef>
+#include <tuple>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+// This simple variant gets the job done without
+// causing too much trouble with template depth:
+//
+// * Always allows an empty state I==0
+// * emplace() and get() support 1-based indexes only
+// * Basic exception guarantee
+// * Max 255 types
+//
+template<class... TN>
+class variant
+{
+ typename std::aligned_storage<
+ max_sizeof<TN...>(),
+ max_alignof<TN...>()
+ >::type buf_;
+ unsigned char i_ = 0;
+
+ template<std::size_t I>
+ using type = typename std::tuple_element<
+ I , std::tuple<TN...>>::type;
+
+ template<std::size_t I>
+ using C = std::integral_constant<std::size_t, I>;
+
+public:
+ variant() = default;
+
+ ~variant()
+ {
+ if(i_)
+ destroy(C<0>{});
+ }
+
+ // 0 = empty
+ unsigned char
+ index() const
+ {
+ return i_;
+ }
+
+ // moved-from object becomes empty
+ variant(variant&& other)
+ {
+ i_ = other.move(&buf_, C<0>{});
+ }
+
+ variant(variant const& other)
+ {
+ i_ = other.copy(&buf_, C<0>{});
+ }
+
+ // moved-from object becomes empty
+ variant& operator=(variant&& other)
+ {
+ if(i_ != 0)
+ destroy(C<0>{});
+ i_ = other.move(&buf_, C<0>{});
+ return *this;
+ }
+
+ variant& operator=(variant const& other)
+ {
+ if(i_ != 0)
+ destroy(C<0>{});
+ i_ = other.copy(&buf_, C<0>{});
+ return *this;
+ }
+
+ template<std::size_t I, class... Args>
+ void
+ emplace(Args&&... args)
+ {
+ if(i_ != 0)
+ destroy(C<0>{});
+ new(&buf_) type<I-1>(
+ std::forward<Args>(args)...);
+ i_ = I;
+ }
+
+ template<std::size_t I>
+ type<I-1>&
+ get()
+ {
+ BOOST_ASSERT(i_ == I);
+ return *reinterpret_cast<
+ type<I-1>*>(&buf_);
+ }
+
+ template<std::size_t I>
+ type<I-1> const&
+ get() const
+ {
+ BOOST_ASSERT(i_ == I);
+ return *reinterpret_cast<
+ type<I-1> const*>(&buf_);
+ }
+
+ void
+ reset()
+ {
+ if(i_ == 0)
+ return;
+ destroy(C<0>{});
+ }
+
+private:
+ void
+ destroy(C<sizeof...(TN)>)
+ {
+ return;
+ }
+
+ template<std::size_t I>
+ void
+ destroy(C<I>)
+ {
+ if(i_ == I+1)
+ {
+ using T = type<I>;
+ get<I+1>().~T();
+ i_ = 0;
+ return;
+ }
+ destroy(C<I+1>{});
+ }
+
+ unsigned char
+ move(void*, C<sizeof...(TN)>)
+ {
+ return 0;
+ }
+
+ template<std::size_t I>
+ unsigned char
+ move(void* dest, C<I>)
+ {
+ if(i_ == I+1)
+ {
+ using T = type<I>;
+ new(dest) T{std::move(get<I+1>())};
+ get<I+1>().~T();
+ i_ = 0;
+ return I+1;
+ }
+ return move(dest, C<I+1>{});
+ }
+
+ unsigned char
+ copy(void*, C<sizeof...(TN)>) const
+ {
+ return 0;
+ }
+
+ template<std::size_t I>
+ unsigned char
+ copy(void* dest, C<I>) const
+ {
+ if(i_ == I+1)
+ {
+ using T = type<I>;
+ auto const& t = get<I+1>();
+ new(dest) T{t};
+ return I+1;
+ }
+ return copy(dest, C<I+1>{});
+ }
+};
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/detail/varint.hpp b/boost/beast/core/detail/varint.hpp
new file mode 100644
index 0000000000..37f0b9d1c1
--- /dev/null
+++ b/boost/beast/core/detail/varint.hpp
@@ -0,0 +1,79 @@
+//
+// Copyright (c) 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_DETAIL_VARINT_HPP
+#define BOOST_BEAST_DETAIL_VARINT_HPP
+
+#include <boost/static_assert.hpp>
+#include <cstdlib>
+#include <iterator>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace detail {
+
+// https://developers.google.com/protocol-buffers/docs/encoding#varints
+
+inline
+std::size_t
+varint_size(std::size_t value)
+{
+ std::size_t n = 1;
+ while(value > 127)
+ {
+ ++n;
+ value /= 128;
+ }
+ return n;
+}
+
+template<class FwdIt>
+std::size_t
+varint_read(FwdIt& first)
+{
+ using value_type = typename
+ std::iterator_traits<FwdIt>::value_type;
+ BOOST_STATIC_ASSERT(
+ std::is_integral<value_type>::value &&
+ sizeof(value_type) == 1);
+ std::size_t value = 0;
+ std::size_t factor = 1;
+ while((*first & 0x80) != 0)
+ {
+ value += (*first++ & 0x7f) * factor;
+ factor *= 128;
+ }
+ value += *first++ * factor;
+ return value;
+}
+
+template<class FwdIt>
+void
+varint_write(FwdIt& first, std::size_t value)
+{
+ using value_type = typename
+ std::iterator_traits<FwdIt>::value_type;
+ BOOST_STATIC_ASSERT(
+ std::is_integral<value_type>::value &&
+ sizeof(value_type) == 1);
+ while(value > 127)
+ {
+ *first++ = static_cast<value_type>(
+ 0x80 | value);
+ value /= 128;
+ }
+ *first++ = static_cast<value_type>(value);
+}
+
+} // detail
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/error.hpp b/boost/beast/core/error.hpp
new file mode 100644
index 0000000000..f034f7a404
--- /dev/null
+++ b/boost/beast/core/error.hpp
@@ -0,0 +1,58 @@
+//
+// 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_ERROR_HPP
+#define BOOST_BEAST_ERROR_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/system/system_error.hpp>
+
+namespace boost {
+namespace beast {
+
+/// The type of error code used by the library
+using error_code = boost::system::error_code;
+
+/// The type of system error thrown by the library
+using system_error = boost::system::system_error;
+
+/// The type of error category used by the library
+using error_category = boost::system::error_category;
+
+/// A function to return the generic error category used by the library
+#if BOOST_BEAST_DOXYGEN
+error_category const&
+generic_category();
+#else
+using boost::system::generic_category;
+#endif
+
+/// A function to return the system error category used by the library
+#if BOOST_BEAST_DOXYGEN
+error_category const&
+system_category();
+#else
+using boost::system::system_category;
+#endif
+
+/// The type of error condition used by the library
+using error_condition = boost::system::error_condition;
+
+/// The set of constants used for cross-platform error codes
+#if BOOST_BEAST_DOXYGEN
+enum errc{};
+#else
+namespace errc = boost::system::errc;
+#endif
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/file.hpp b/boost/beast/core/file.hpp
new file mode 100644
index 0000000000..09c8e82081
--- /dev/null
+++ b/boost/beast/core/file.hpp
@@ -0,0 +1,45 @@
+//
+// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_FILE_HPP
+#define BOOST_BEAST_CORE_FILE_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/file_base.hpp>
+#include <boost/beast/core/file_posix.hpp>
+#include <boost/beast/core/file_stdio.hpp>
+#include <boost/beast/core/file_win32.hpp>
+#include <boost/config.hpp>
+
+namespace boost {
+namespace beast {
+
+/** An implementation of File.
+
+ This alias is set to the best available implementation
+ of @b File given the platform and build settings.
+*/
+#if BOOST_BEAST_DOXYGEN
+struct file : file_stdio
+{
+};
+#else
+#if BOOST_BEAST_USE_WIN32_FILE
+using file = file_win32;
+#elif BOOST_BEAST_USE_POSIX_FILE
+using file = file_posix;
+#else
+using file = file_stdio;
+#endif
+#endif
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/file_base.hpp b/boost/beast/core/file_base.hpp
new file mode 100644
index 0000000000..c2b3c53a53
--- /dev/null
+++ b/boost/beast/core/file_base.hpp
@@ -0,0 +1,89 @@
+//
+// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_FILE_BASE_HPP
+#define BOOST_BEAST_CORE_FILE_BASE_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/string.hpp>
+
+namespace boost {
+namespace beast {
+
+/** File open modes
+
+ These modes are used when opening files using
+ instances of the @b File concept.
+
+ @see file_stdio
+*/
+enum class file_mode
+{
+ /// Random reading
+ read,
+
+ /// Sequential reading
+ scan,
+
+ /** Random writing to a new or truncated file
+
+ @li If the file does not exist, it is created.
+
+ @li If the file exists, it is truncated to
+ zero size upon opening.
+ */
+ write,
+
+ /** Random writing to new file only
+
+ If the file exists, an error is generated.
+ */
+ write_new,
+
+ /** Random writing to existing file
+
+ If the file does not exist, an error is generated.
+ */
+ write_existing,
+
+ /** Appending to a new or truncated file
+
+ The current file position shall be set to the end of
+ the file prior to each write.
+
+ @li If the file does not exist, it is created.
+
+ @li If the file exists, it is truncated to
+ zero size upon opening.
+ */
+ append,
+
+ /** Appending to a new file only
+
+ The current file position shall be set to the end of
+ the file prior to each write.
+
+ If the file exists, an error is generated.
+ */
+ append_new,
+
+ /** Appending to an existing file
+
+ The current file position shall be set to the end of
+ the file prior to each write.
+
+ If the file does not exist, an error is generated.
+ */
+ append_existing
+};
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/file_posix.hpp b/boost/beast/core/file_posix.hpp
new file mode 100644
index 0000000000..65bac2065d
--- /dev/null
+++ b/boost/beast/core/file_posix.hpp
@@ -0,0 +1,175 @@
+//
+// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_FILE_POSIX_HPP
+#define BOOST_BEAST_CORE_FILE_POSIX_HPP
+
+#include <boost/config.hpp>
+
+#if ! defined(BOOST_BEAST_NO_POSIX_FILE)
+# if ! defined(__APPLE__) && ! defined(__linux__)
+# define BOOST_BEAST_NO_POSIX_FILE
+# endif
+#endif
+
+#if ! defined(BOOST_BEAST_USE_POSIX_FILE)
+# if ! defined(BOOST_BEAST_NO_POSIX_FILE)
+# define BOOST_BEAST_USE_POSIX_FILE 1
+# else
+# define BOOST_BEAST_USE_POSIX_FILE 0
+# endif
+#endif
+
+#if BOOST_BEAST_USE_POSIX_FILE
+
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/file_base.hpp>
+#include <cstdint>
+
+namespace boost {
+namespace beast {
+
+/** An implementation of File for POSIX systems.
+
+ This class implements a @b File using POSIX interfaces.
+*/
+class file_posix
+{
+ int fd_ = -1;
+
+public:
+ /** The type of the underlying file handle.
+
+ This is platform-specific.
+ */
+ using native_handle_type = int;
+
+ /** Destructor
+
+ If the file is open it is first closed.
+ */
+ ~file_posix();
+
+ /** Constructor
+
+ There is no open file initially.
+ */
+ file_posix() = default;
+
+ /** Constructor
+
+ The moved-from object behaves as if default constructed.
+ */
+ file_posix(file_posix&& other);
+
+ /** Assignment
+
+ The moved-from object behaves as if default constructed.
+ */
+ file_posix& operator=(file_posix&& other);
+
+ /// Returns the native handle associated with the file.
+ native_handle_type
+ native_handle() const
+ {
+ return fd_;
+ }
+
+ /** Set the native handle associated with the file.
+
+ If the file is open it is first closed.
+
+ @param fd The native file handle to assign.
+ */
+ void
+ native_handle(native_handle_type fd);
+
+ /// Returns `true` if the file is open
+ bool
+ is_open() const
+ {
+ return fd_ != -1;
+ }
+
+ /** Close the file if open
+
+ @param ec Set to the error, if any occurred.
+ */
+ void
+ close(error_code& ec);
+
+ /** Open a file at the given path with the specified mode
+
+ @param path The utf-8 encoded path to the file
+
+ @param mode The file mode to use
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ open(char const* path, file_mode mode, error_code& ec);
+
+ /** Return the size of the open file
+
+ @param ec Set to the error, if any occurred
+
+ @return The size in bytes
+ */
+ std::uint64_t
+ size(error_code& ec) const;
+
+ /** Return the current position in the open file
+
+ @param ec Set to the error, if any occurred
+
+ @return The offset in bytes from the beginning of the file
+ */
+ std::uint64_t
+ pos(error_code& ec) const;
+
+ /** Adjust the current position in the open file
+
+ @param offset The offset in bytes from the beginning of the file
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ seek(std::uint64_t offset, error_code& ec);
+
+ /** Read from the open file
+
+ @param buffer The buffer for storing the result of the read
+
+ @param n The number of bytes to read
+
+ @param ec Set to the error, if any occurred
+ */
+ std::size_t
+ read(void* buffer, std::size_t n, error_code& ec) const;
+
+ /** Write to the open file
+
+ @param buffer The buffer holding the data to write
+
+ @param n The number of bytes to write
+
+ @param ec Set to the error, if any occurred
+ */
+ std::size_t
+ write(void const* buffer, std::size_t n, error_code& ec);
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/file_posix.ipp>
+
+#endif
+
+#endif
diff --git a/boost/beast/core/file_stdio.hpp b/boost/beast/core/file_stdio.hpp
new file mode 100644
index 0000000000..09ca0c42de
--- /dev/null
+++ b/boost/beast/core/file_stdio.hpp
@@ -0,0 +1,158 @@
+//
+// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_FILE_STDIO_HPP
+#define BOOST_BEAST_CORE_FILE_STDIO_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/file_base.hpp>
+#include <cstdio>
+#include <cstdint>
+
+namespace boost {
+namespace beast {
+
+/** An implementation of File which uses cstdio.
+
+ This class implements a file using the interfaces present
+ in the C++ Standard Library, in `<stdio>`.
+*/
+class file_stdio
+{
+ FILE* f_ = nullptr;
+
+public:
+ /** The type of the underlying file handle.
+
+ This is platform-specific.
+ */
+ using native_handle_type = FILE*;
+
+ /** Destructor
+
+ If the file is open it is first closed.
+ */
+ ~file_stdio();
+
+ /** Constructor
+
+ There is no open file initially.
+ */
+ file_stdio() = default;
+
+ /** Constructor
+
+ The moved-from object behaves as if default constructed.
+ */
+ file_stdio(file_stdio&& other);
+
+ /** Assignment
+
+ The moved-from object behaves as if default constructed.
+ */
+ file_stdio& operator=(file_stdio&& other);
+
+ /// Returns the native handle associated with the file.
+ FILE*
+ native_handle() const
+ {
+ return f_;
+ }
+
+ /** Set the native handle associated with the file.
+
+ If the file is open it is first closed.
+
+ @param f The native file handle to assign.
+ */
+ void
+ native_handle(FILE* f);
+
+ /// Returns `true` if the file is open
+ bool
+ is_open() const
+ {
+ return f_ != nullptr;
+ }
+
+ /** Close the file if open
+
+ @param ec Set to the error, if any occurred.
+ */
+ void
+ close(error_code& ec);
+
+ /** Open a file at the given path with the specified mode
+
+ @param path The utf-8 encoded path to the file
+
+ @param mode The file mode to use
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ open(char const* path, file_mode mode, error_code& ec);
+
+ /** Return the size of the open file
+
+ @param ec Set to the error, if any occurred
+
+ @return The size in bytes
+ */
+ std::uint64_t
+ size(error_code& ec) const;
+
+ /** Return the current position in the open file
+
+ @param ec Set to the error, if any occurred
+
+ @return The offset in bytes from the beginning of the file
+ */
+ std::uint64_t
+ pos(error_code& ec) const;
+
+ /** Adjust the current position in the open file
+
+ @param offset The offset in bytes from the beginning of the file
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ seek(std::uint64_t offset, error_code& ec);
+
+ /** Read from the open file
+
+ @param buffer The buffer for storing the result of the read
+
+ @param n The number of bytes to read
+
+ @param ec Set to the error, if any occurred
+ */
+ std::size_t
+ read(void* buffer, std::size_t n, error_code& ec) const;
+
+ /** Write to the open file
+
+ @param buffer The buffer holding the data to write
+
+ @param n The number of bytes to write
+
+ @param ec Set to the error, if any occurred
+ */
+ std::size_t
+ write(void const* buffer, std::size_t n, error_code& ec);
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/file_stdio.ipp>
+
+#endif
diff --git a/boost/beast/core/file_win32.hpp b/boost/beast/core/file_win32.hpp
new file mode 100644
index 0000000000..3e9a18abaa
--- /dev/null
+++ b/boost/beast/core/file_win32.hpp
@@ -0,0 +1,177 @@
+//
+// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_FILE_WIN32_HPP
+#define BOOST_BEAST_CORE_FILE_WIN32_HPP
+
+#include <boost/config.hpp>
+
+#if ! defined(BOOST_BEAST_USE_WIN32_FILE)
+# ifdef BOOST_MSVC
+# define BOOST_BEAST_USE_WIN32_FILE 1
+# else
+# define BOOST_BEAST_USE_WIN32_FILE 0
+# endif
+#endif
+
+#if BOOST_BEAST_USE_WIN32_FILE
+
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/file_base.hpp>
+#include <boost/winapi/basic_types.hpp>
+#include <boost/winapi/handles.hpp>
+#include <cstdio>
+#include <cstdint>
+
+namespace boost {
+namespace beast {
+
+/** An implementation of File for Win32.
+
+ This class implements a @b File using Win32 native interfaces.
+*/
+class file_win32
+{
+ boost::winapi::HANDLE_ h_ =
+ boost::winapi::INVALID_HANDLE_VALUE_;
+
+public:
+ /** The type of the underlying file handle.
+
+ This is platform-specific.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using native_handle_type = HANDLE;
+#else
+ using native_handle_type = boost::winapi::HANDLE_;
+#endif
+
+ /** Destructor
+
+ If the file is open it is first closed.
+ */
+ ~file_win32();
+
+ /** Constructor
+
+ There is no open file initially.
+ */
+ file_win32() = default;
+
+ /** Constructor
+
+ The moved-from object behaves as if default constructed.
+ */
+ file_win32(file_win32&& other);
+
+ /** Assignment
+
+ The moved-from object behaves as if default constructed.
+ */
+ file_win32& operator=(file_win32&& other);
+
+ /// Returns the native handle associated with the file.
+ native_handle_type
+ native_handle()
+ {
+ return h_;
+ }
+
+ /** Set the native handle associated with the file.
+
+ If the file is open it is first closed.
+
+ @param h The native file handle to assign.
+ */
+ void
+ native_handle(native_handle_type h);
+
+ /// Returns `true` if the file is open
+ bool
+ is_open() const
+ {
+ return h_ != boost::winapi::INVALID_HANDLE_VALUE_;
+ }
+
+ /** Close the file if open
+
+ @param ec Set to the error, if any occurred.
+ */
+ void
+ close(error_code& ec);
+
+ /** Open a file at the given path with the specified mode
+
+ @param path The utf-8 encoded path to the file
+
+ @param mode The file mode to use
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ open(char const* path, file_mode mode, error_code& ec);
+
+ /** Return the size of the open file
+
+ @param ec Set to the error, if any occurred
+
+ @return The size in bytes
+ */
+ std::uint64_t
+ size(error_code& ec) const;
+
+ /** Return the current position in the open file
+
+ @param ec Set to the error, if any occurred
+
+ @return The offset in bytes from the beginning of the file
+ */
+ std::uint64_t
+ pos(error_code& ec);
+
+ /** Adjust the current position in the open file
+
+ @param offset The offset in bytes from the beginning of the file
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ seek(std::uint64_t offset, error_code& ec);
+
+ /** Read from the open file
+
+ @param buffer The buffer for storing the result of the read
+
+ @param n The number of bytes to read
+
+ @param ec Set to the error, if any occurred
+ */
+ std::size_t
+ read(void* buffer, std::size_t n, error_code& ec);
+
+ /** Write to the open file
+
+ @param buffer The buffer holding the data to write
+
+ @param n The number of bytes to write
+
+ @param ec Set to the error, if any occurred
+ */
+ std::size_t
+ write(void const* buffer, std::size_t n, error_code& ec);
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/file_win32.ipp>
+
+#endif
+
+#endif
diff --git a/boost/beast/core/flat_buffer.hpp b/boost/beast/core/flat_buffer.hpp
new file mode 100644
index 0000000000..0ccd16bc64
--- /dev/null
+++ b/boost/beast/core/flat_buffer.hpp
@@ -0,0 +1,342 @@
+//
+// 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_FLAT_BUFFER_HPP
+#define BOOST_BEAST_FLAT_BUFFER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/detail/allocator.hpp>
+#include <boost/beast/core/detail/empty_base_optimization.hpp>
+#include <boost/asio/buffer.hpp>
+#include <limits>
+#include <memory>
+
+namespace boost {
+namespace beast {
+
+/** A linear dynamic buffer.
+
+ Objects of this type meet the requirements of @b DynamicBuffer
+ and offer additional invariants:
+
+ @li Buffer sequences returned by @ref data and @ref prepare
+ will always be of length one.
+
+ @li A configurable maximum buffer size may be set upon
+ construction. Attempts to exceed the buffer size will throw
+ `std::length_error`.
+
+ Upon construction, a maximum size for the buffer may be
+ specified. If this limit is exceeded, the `std::length_error`
+ exception will be thrown.
+
+ @note This class is designed for use with algorithms that
+ take dynamic buffers as parameters, and are optimized
+ for the case where the input sequence or output sequence
+ is stored in a single contiguous buffer.
+*/
+template<class Allocator>
+class basic_flat_buffer
+#if ! BOOST_BEAST_DOXYGEN
+ : private detail::empty_base_optimization<
+ typename detail::allocator_traits<Allocator>::
+ template rebind_alloc<char>>
+#endif
+{
+ enum
+ {
+ min_size = 512
+ };
+
+ template<class OtherAlloc>
+ friend class basic_flat_buffer;
+
+ using base_alloc_type = typename
+ detail::allocator_traits<Allocator>::
+ template rebind_alloc<char>;
+
+ using alloc_traits =
+ detail::allocator_traits<base_alloc_type>;
+
+ static
+ inline
+ std::size_t
+ dist(char const* first, char const* last)
+ {
+ return static_cast<std::size_t>(last - first);
+ }
+
+ char* begin_;
+ char* in_;
+ char* out_;
+ char* last_;
+ char* end_;
+ std::size_t max_;
+
+public:
+ /// The type of allocator used.
+ using allocator_type = Allocator;
+
+ /// The type used to represent the input sequence as a list of buffers.
+ using const_buffers_type = boost::asio::mutable_buffer;
+
+ /// The type used to represent the output sequence as a list of buffers.
+ using mutable_buffers_type = boost::asio::mutable_buffer;
+
+ /// Destructor
+ ~basic_flat_buffer();
+
+ /** Constructor
+
+ Upon construction, capacity will be zero.
+ */
+ basic_flat_buffer();
+
+ /** Constructor
+
+ Upon construction, capacity will be zero.
+
+ @param limit The setting for @ref max_size.
+ */
+ explicit
+ basic_flat_buffer(std::size_t limit);
+
+ /** Constructor
+
+ Upon construction, capacity will be zero.
+
+ @param alloc The allocator to construct with.
+ */
+ explicit
+ basic_flat_buffer(Allocator const& alloc);
+
+ /** Constructor
+
+ Upon construction, capacity will be zero.
+
+ @param limit The setting for @ref max_size.
+
+ @param alloc The allocator to use.
+ */
+ basic_flat_buffer(
+ std::size_t limit, Allocator const& alloc);
+
+ /** Constructor
+
+ After the move, `*this` will have an empty output sequence.
+
+ @param other The object to move from. After the move,
+ The object's state will be as if constructed using
+ its current allocator and limit.
+ */
+ basic_flat_buffer(basic_flat_buffer&& other);
+
+ /** Constructor
+
+ After the move, `*this` will have an empty output sequence.
+
+ @param other The object to move from. After the move,
+ The object's state will be as if constructed using
+ its current allocator and limit.
+
+ @param alloc The allocator to use.
+ */
+ basic_flat_buffer(
+ basic_flat_buffer&& other, Allocator const& alloc);
+
+ /** Constructor
+
+ @param other The object to copy from.
+ */
+ basic_flat_buffer(basic_flat_buffer const& other);
+
+ /** Constructor
+
+ @param other The object to copy from.
+
+ @param alloc The allocator to use.
+ */
+ basic_flat_buffer(basic_flat_buffer const& other,
+ Allocator const& alloc);
+
+ /** Constructor
+
+ @param other The object to copy from.
+ */
+ template<class OtherAlloc>
+ basic_flat_buffer(
+ basic_flat_buffer<OtherAlloc> const& other);
+
+ /** Constructor
+
+ @param other The object to copy from.
+
+ @param alloc The allocator to use.
+ */
+ template<class OtherAlloc>
+ basic_flat_buffer(
+ basic_flat_buffer<OtherAlloc> const& other,
+ Allocator const& alloc);
+
+ /** Assignment
+
+ After the move, `*this` will have an empty output sequence.
+
+ @param other The object to move from. After the move,
+ the object's state will be as if constructed using
+ its current allocator and limit.
+ */
+ basic_flat_buffer&
+ operator=(basic_flat_buffer&& other);
+
+ /** Assignment
+
+ After the copy, `*this` will have an empty output sequence.
+
+ @param other The object to copy from.
+ */
+ basic_flat_buffer&
+ operator=(basic_flat_buffer const& other);
+
+ /** Copy assignment
+
+ After the copy, `*this` will have an empty output sequence.
+
+ @param other The object to copy from.
+ */
+ template<class OtherAlloc>
+ basic_flat_buffer&
+ operator=(basic_flat_buffer<OtherAlloc> const& other);
+
+ /// Returns a copy of the associated allocator.
+ allocator_type
+ get_allocator() const
+ {
+ return this->member();
+ }
+
+ /// Returns the size of the input sequence.
+ std::size_t
+ size() const
+ {
+ return dist(in_, out_);
+ }
+
+ /// Return the maximum sum of the input and output sequence sizes.
+ std::size_t
+ max_size() const
+ {
+ return max_;
+ }
+
+ /// Return the maximum sum of input and output sizes that can be held without an allocation.
+ std::size_t
+ capacity() const
+ {
+ return dist(begin_, end_);
+ }
+
+ /// Get a list of buffers that represent the input sequence.
+ const_buffers_type
+ data() const
+ {
+ return {in_, dist(in_, out_)};
+ }
+
+ /** Get a list of buffers that represent the output sequence, with the given size.
+
+ @throws std::length_error if `size() + n` exceeds `max_size()`.
+
+ @note All previous buffers sequences obtained from
+ calls to @ref data or @ref prepare are invalidated.
+ */
+ mutable_buffers_type
+ prepare(std::size_t n);
+
+ /** Move bytes from the output sequence to the input sequence.
+
+ @param n The number of bytes to move. If this is larger than
+ the number of bytes in the output sequences, then the entire
+ output sequences is moved.
+
+ @note All previous buffers sequences obtained from
+ calls to @ref data or @ref prepare are invalidated.
+ */
+ void
+ commit(std::size_t n)
+ {
+ out_ += (std::min)(n, dist(out_, last_));
+ }
+
+ /** Remove bytes from the input sequence.
+
+ If `n` is greater than the number of bytes in the input
+ sequence, all bytes in the input sequence are removed.
+
+ @note All previous buffers sequences obtained from
+ calls to @ref data or @ref prepare are invalidated.
+ */
+ void
+ consume(std::size_t n);
+
+ /** Reallocate the buffer to fit the input sequence.
+
+ @note All previous buffers sequences obtained from
+ calls to @ref data or @ref prepare are invalidated.
+ */
+ void
+ shrink_to_fit();
+
+ /// Exchange two flat buffers
+ template<class Alloc>
+ friend
+ void
+ swap(
+ basic_flat_buffer<Alloc>& lhs,
+ basic_flat_buffer<Alloc>& rhs);
+
+private:
+ void
+ reset();
+
+ template<class DynamicBuffer>
+ void
+ copy_from(DynamicBuffer const& other);
+
+ void
+ move_assign(basic_flat_buffer&, std::true_type);
+
+ void
+ move_assign(basic_flat_buffer&, std::false_type);
+
+ void
+ copy_assign(basic_flat_buffer const&, std::true_type);
+
+ void
+ copy_assign(basic_flat_buffer const&, std::false_type);
+
+ void
+ swap(basic_flat_buffer&);
+
+ void
+ swap(basic_flat_buffer&, std::true_type);
+
+ void
+ swap(basic_flat_buffer&, std::false_type);
+};
+
+using flat_buffer =
+ basic_flat_buffer<std::allocator<char>>;
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/flat_buffer.ipp>
+
+#endif
diff --git a/boost/beast/core/flat_static_buffer.hpp b/boost/beast/core/flat_static_buffer.hpp
new file mode 100644
index 0000000000..141ae552c1
--- /dev/null
+++ b/boost/beast/core/flat_static_buffer.hpp
@@ -0,0 +1,254 @@
+//
+// 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_FLAT_STATIC_BUFFER_HPP
+#define BOOST_BEAST_FLAT_STATIC_BUFFER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/buffer.hpp>
+#include <algorithm>
+#include <cstddef>
+#include <cstring>
+
+namespace boost {
+namespace beast {
+
+/** A flat @b DynamicBuffer with a fixed size internal buffer.
+
+ Buffer sequences returned by @ref data and @ref prepare
+ will always be of length one.
+ Ownership of the underlying storage belongs to the derived class.
+
+ @note Variables are usually declared using the template class
+ @ref flat_static_buffer; however, to reduce the number of instantiations
+ of template functions receiving static stream buffer arguments in a
+ deduced context, the signature of the receiving function should use
+ @ref flat_static_buffer_base.
+
+ When used with @ref flat_static_buffer this implements a dynamic
+ buffer using no memory allocations.
+
+ @see @ref flat_static_buffer
+*/
+class flat_static_buffer_base
+{
+ char* begin_;
+ char* in_;
+ char* out_;
+ char* last_;
+ char* end_;
+
+ flat_static_buffer_base(flat_static_buffer_base const& other) = delete;
+ flat_static_buffer_base& operator=(flat_static_buffer_base const&) = delete;
+
+public:
+ /** The type used to represent the input sequence as a list of buffers.
+
+ This buffer sequence is guaranteed to have length 1.
+ */
+ using const_buffers_type = boost::asio::mutable_buffer;
+
+ /** The type used to represent the output sequence as a list of buffers.
+
+ This buffer sequence is guaranteed to have length 1.
+ */
+ using mutable_buffers_type = boost::asio::mutable_buffer;
+
+ /** Constructor
+
+ This creates a dynamic buffer using the provided storage area.
+
+ @param p A pointer to valid storage of at least `n` bytes.
+
+ @param n The number of valid bytes pointed to by `p`.
+ */
+ flat_static_buffer_base(void* p, std::size_t n)
+ {
+ reset_impl(p, n);
+ }
+
+ /// Return the size of the input sequence.
+ std::size_t
+ size() const
+ {
+ return out_ - in_;
+ }
+
+ /// Return the maximum sum of the input and output sequence sizes.
+ std::size_t
+ max_size() const
+ {
+ return dist(begin_, end_);
+ }
+
+ /// Return the maximum sum of input and output sizes that can be held without an allocation.
+ std::size_t
+ capacity() const
+ {
+ return max_size();
+ }
+
+ /** Get a list of buffers that represent the input sequence.
+
+ @note These buffers remain valid across subsequent calls to `prepare`.
+ */
+ const_buffers_type
+ data() const;
+
+ /// Set the input and output sequences to size 0
+ void
+ reset();
+
+ /** Get a list of buffers that represent the output sequence, with the given size.
+
+ @throws std::length_error if the size would exceed the limit
+ imposed by the underlying mutable buffer sequence.
+
+ @note Buffers representing the input sequence acquired prior to
+ this call remain valid.
+ */
+ mutable_buffers_type
+ prepare(std::size_t n);
+
+ /** Move bytes from the output sequence to the input sequence.
+
+ @note Buffers representing the input sequence acquired prior to
+ this call remain valid.
+ */
+ void
+ commit(std::size_t n)
+ {
+ out_ += (std::min<std::size_t>)(n, last_ - out_);
+ }
+
+ /// Remove bytes from the input sequence.
+ void
+ consume(std::size_t n)
+ {
+ consume_impl(n);
+ }
+
+protected:
+ /** Constructor
+
+ The buffer will be in an undefined state. It is necessary
+ for the derived class to call @ref reset with a pointer
+ and size in order to initialize the object.
+ */
+ flat_static_buffer_base() = default;
+
+ /** Reset the pointed-to buffer.
+
+ This function resets the internal state to the buffer provided.
+ All input and output sequences are invalidated. This function
+ allows the derived class to construct its members before
+ initializing the static buffer.
+
+ @param p A pointer to valid storage of at least `n` bytes.
+
+ @param n The number of valid bytes pointed to by `p`.
+ */
+ void
+ reset(void* p, std::size_t n);
+
+private:
+ static
+ inline
+ std::size_t
+ dist(char const* first, char const* last)
+ {
+ return static_cast<std::size_t>(last - first);
+ }
+
+ template<class = void>
+ void
+ reset_impl();
+
+ template<class = void>
+ void
+ reset_impl(void* p, std::size_t n);
+
+ template<class = void>
+ mutable_buffers_type
+ prepare_impl(std::size_t n);
+
+ template<class = void>
+ void
+ consume_impl(std::size_t n);
+};
+
+//------------------------------------------------------------------------------
+
+/** A @b DynamicBuffer with a fixed size internal buffer.
+
+ Buffer sequences returned by @ref data and @ref prepare
+ will always be of length one.
+ This implements a dynamic buffer using no memory allocations.
+
+ @tparam N The number of bytes in the internal buffer.
+
+ @note To reduce the number of template instantiations when passing
+ objects of this type in a deduced context, the signature of the
+ receiving function should use @ref flat_static_buffer_base instead.
+
+ @see @ref flat_static_buffer_base
+*/
+template<std::size_t N>
+class flat_static_buffer : public flat_static_buffer_base
+{
+ char buf_[N];
+
+public:
+ /// Constructor
+ flat_static_buffer(flat_static_buffer const&);
+
+ /// Constructor
+ flat_static_buffer()
+ : flat_static_buffer_base(buf_, N)
+ {
+ }
+
+ /// Assignment
+ flat_static_buffer& operator=(flat_static_buffer const&);
+
+ /// Returns the @ref flat_static_buffer_base portion of this object
+ flat_static_buffer_base&
+ base()
+ {
+ return *this;
+ }
+
+ /// Returns the @ref flat_static_buffer_base portion of this object
+ flat_static_buffer_base const&
+ base() const
+ {
+ return *this;
+ }
+
+ /// Return the maximum sum of the input and output sequence sizes.
+ std::size_t constexpr
+ max_size() const
+ {
+ return N;
+ }
+
+ /// Return the maximum sum of input and output sizes that can be held without an allocation.
+ std::size_t constexpr
+ capacity() const
+ {
+ return N;
+ }
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/flat_static_buffer.ipp>
+
+#endif
diff --git a/boost/beast/core/handler_ptr.hpp b/boost/beast/core/handler_ptr.hpp
new file mode 100644
index 0000000000..f08a815c16
--- /dev/null
+++ b/boost/beast/core/handler_ptr.hpp
@@ -0,0 +1,213 @@
+//
+// 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_HANDLER_PTR_HPP
+#define BOOST_BEAST_HANDLER_PTR_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <atomic>
+#include <cstdint>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+/** A smart pointer container with associated completion handler.
+
+ This is a smart pointer that retains shared ownership of an
+ object through a pointer. Memory is managed using the allocation
+ and deallocation functions associated with a completion handler,
+ which is also stored in the object. The managed object is
+ destroyed and its memory deallocated when one of the following
+ happens:
+
+ @li The function @ref invoke is called.
+
+ @li The function @ref release_handler is called.
+
+ @li The last remaining container owning the object is destroyed.
+
+ Objects of this type are used in the implementation of
+ composed operations. Typically the composed operation's shared
+ state is managed by the @ref handler_ptr and an allocator
+ associated with the final handler is used to create the managed
+ object.
+
+ @note The reference count is stored using a 16 bit unsigned
+ integer. Making more than 2^16 copies of one object results
+ in undefined behavior.
+
+ @tparam T The type of the owned object.
+
+ @tparam Handler The type of the completion handler.
+*/
+template<class T, class Handler>
+class handler_ptr
+{
+ struct P
+ {
+ T* t;
+ std::atomic<std::uint16_t> n;
+
+ // There's no way to put the handler anywhere else
+ // without exposing ourselves to race conditions
+ // and all sorts of ugliness.
+ // See:
+ // https://github.com/boostorg/beast/issues/215
+ Handler handler;
+
+ template<class DeducedHandler, class... Args>
+ P(DeducedHandler&& handler, Args&&... args);
+ };
+
+ P* p_;
+
+public:
+ /// The type of element this object stores
+ using element_type = T;
+
+ /// The type of handler this object stores
+ using handler_type = Handler;
+
+ /// Copy assignment (disallowed).
+ handler_ptr& operator=(handler_ptr const&) = delete;
+
+ /** Destructs the owned object if no more @ref handler_ptr link to it.
+
+ If `*this` owns an object and it is the last @ref handler_ptr
+ owning it, the object is destroyed and the memory deallocated
+ using the associated deallocator.
+ */
+ ~handler_ptr();
+
+ /** Move constructor.
+
+ When this call returns, the moved-from container
+ will have no owned object.
+ */
+ handler_ptr(handler_ptr&& other);
+
+ /// Copy constructor
+ handler_ptr(handler_ptr const& other);
+
+ /** Construct a new @ref handler_ptr
+
+ This creates a new @ref handler_ptr with an owned object
+ of type `T`. The allocator associated with the handler will
+ be used to allocate memory for the owned object. The constructor
+ for the owned object will be called thusly:
+
+ @code
+ T(handler, std::forward<Args>(args)...)
+ @endcode
+
+ @param handler The handler to associate with the owned
+ object. The argument will be moved.
+
+ @param args Optional arguments forwarded to
+ the owned object's constructor.
+ */
+ template<class... Args>
+ handler_ptr(Handler&& handler, Args&&... args);
+
+ /** Construct a new @ref handler_ptr
+
+ This creates a new @ref handler_ptr with an owned object
+ of type `T`. The allocator associated with the handler will
+ be used to allocate memory for the owned object. The constructor
+ for the owned object will be called thusly:
+
+ @code
+ T(handler, std::forward<Args>(args)...)
+ @endcode
+
+ @param handler The handler to associate with the owned
+ object. The argument will be copied.
+
+ @param args Optional arguments forwarded to
+ the owned object's constructor.
+ */
+ template<class... Args>
+ handler_ptr(Handler const& handler, Args&&... args);
+
+ /// Returns a reference to the handler
+ handler_type&
+ handler() const
+ {
+ return p_->handler;
+ }
+
+ /// Returns `true` if `*this` owns an object.
+ explicit
+ operator bool() const
+ {
+ return p_ && p_->t;
+ }
+
+ /** Returns a pointer to the owned object.
+
+ If `*this` owns an object, a pointer to the
+ object is returned, else `nullptr` is returned.
+ */
+ T*
+ get() const
+ {
+ return p_ ? p_->t : nullptr;
+ }
+
+ /// Return a reference to the owned object.
+ T&
+ operator*() const
+ {
+ return *p_->t;
+ }
+
+ /// Return a pointer to the owned object.
+ T*
+ operator->() const
+ {
+ return p_->t;
+ }
+
+ /** Release ownership of the handler
+
+ If `*this` owns an object, it is first destroyed.
+
+ @return The released handler.
+ */
+ handler_type
+ release_handler();
+
+ /** Invoke the handler in the owned object.
+
+ This function invokes the handler in the owned object
+ with a forwarded argument list. Before the invocation,
+ the owned object is destroyed, satisfying the
+ deallocation-before-invocation Asio guarantee. All
+ instances of @ref handler_ptr which refer to the
+ same owned object will be reset, including this instance.
+
+ @note Care must be taken when the arguments are themselves
+ stored in the owned object. Such arguments must first be
+ moved to the stack or elsewhere, and then passed, or else
+ undefined behavior will result.
+ */
+ template<class... Args>
+ void
+ invoke(Args&&... args);
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/handler_ptr.ipp>
+
+#endif
diff --git a/boost/beast/core/impl/buffered_read_stream.ipp b/boost/beast/core/impl/buffered_read_stream.ipp
new file mode 100644
index 0000000000..9e9fa53065
--- /dev/null
+++ b/boost/beast/core/impl/buffered_read_stream.ipp
@@ -0,0 +1,248 @@
+//
+// 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_IMPL_BUFFERED_READ_STREAM_IPP
+#define BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_IPP
+
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/read_size.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/throw_exception.hpp>
+
+namespace boost {
+namespace beast {
+
+template<class Stream, class DynamicBuffer>
+template<class MutableBufferSequence, class Handler>
+class buffered_read_stream<
+ Stream, DynamicBuffer>::read_some_op
+{
+ int step_ = 0;
+ buffered_read_stream& s_;
+ MutableBufferSequence b_;
+ Handler h_;
+
+public:
+ read_some_op(read_some_op&&) = default;
+ read_some_op(read_some_op const&) = default;
+
+ template<class DeducedHandler, class... Args>
+ read_some_op(DeducedHandler&& h,
+ buffered_read_stream& s,
+ MutableBufferSequence const& b)
+ : s_(s)
+ , b_(b)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type =
+ boost::asio::associated_executor_t<Handler, decltype(
+ std::declval<buffered_read_stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()(error_code const& ec,
+ std::size_t bytes_transferred);
+
+ friend
+ bool asio_handler_is_continuation(read_some_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<class Stream, class DynamicBuffer>
+template<class MutableBufferSequence, class Handler>
+void
+buffered_read_stream<Stream, DynamicBuffer>::
+read_some_op<MutableBufferSequence, Handler>::operator()(
+ error_code const& ec, std::size_t bytes_transferred)
+{
+ switch(step_)
+ {
+ case 0:
+ if(s_.buffer_.size() == 0)
+ {
+ if(s_.capacity_ == 0)
+ {
+ // read (unbuffered)
+ step_ = 1;
+ return s_.next_layer_.async_read_some(
+ b_, std::move(*this));
+ }
+
+ // read
+ step_ = 2;
+ return s_.next_layer_.async_read_some(
+ s_.buffer_.prepare(read_size(
+ s_.buffer_, s_.capacity_)),
+ std::move(*this));
+
+ }
+ step_ = 3;
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+
+ case 1:
+ // upcall
+ break;
+
+ case 2:
+ s_.buffer_.commit(bytes_transferred);
+ BOOST_BEAST_FALLTHROUGH;
+
+ case 3:
+ bytes_transferred =
+ boost::asio::buffer_copy(b_, s_.buffer_.data());
+ s_.buffer_.consume(bytes_transferred);
+ break;
+ }
+ h_(ec, bytes_transferred);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Stream, class DynamicBuffer>
+template<class... Args>
+buffered_read_stream<Stream, DynamicBuffer>::
+buffered_read_stream(Args&&... args)
+ : next_layer_(std::forward<Args>(args)...)
+{
+}
+
+template<class Stream, class DynamicBuffer>
+template<class ConstBufferSequence, class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+buffered_read_stream<Stream, DynamicBuffer>::
+async_write_some(
+ ConstBufferSequence const& buffers,
+ WriteHandler&& handler)
+{
+ static_assert(is_async_write_stream<next_layer_type>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ static_assert(is_completion_handler<WriteHandler,
+ void(error_code, std::size_t)>::value,
+ "WriteHandler requirements not met");
+ return next_layer_.async_write_some(buffers,
+ std::forward<WriteHandler>(handler));
+}
+
+template<class Stream, class DynamicBuffer>
+template<class MutableBufferSequence>
+std::size_t
+buffered_read_stream<Stream, DynamicBuffer>::
+read_some(
+ MutableBufferSequence const& buffers)
+{
+ static_assert(is_sync_read_stream<next_layer_type>::value,
+ "SyncReadStream requirements not met");
+ static_assert(boost::asio::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence requirements not met");
+ error_code ec;
+ auto n = read_some(buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return n;
+}
+
+template<class Stream, class DynamicBuffer>
+template<class MutableBufferSequence>
+std::size_t
+buffered_read_stream<Stream, DynamicBuffer>::
+read_some(MutableBufferSequence const& buffers,
+ error_code& ec)
+{
+ static_assert(is_sync_read_stream<next_layer_type>::value,
+ "SyncReadStream requirements not met");
+ static_assert(boost::asio::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence requirements not met");
+ using boost::asio::buffer_size;
+ using boost::asio::buffer_copy;
+ if(buffer_.size() == 0)
+ {
+ if(capacity_ == 0)
+ return next_layer_.read_some(buffers, ec);
+ buffer_.commit(next_layer_.read_some(
+ buffer_.prepare(read_size(buffer_,
+ capacity_)), ec));
+ if(ec)
+ return 0;
+ }
+ else
+ {
+ ec.assign(0, ec.category());
+ }
+ auto bytes_transferred =
+ buffer_copy(buffers, buffer_.data());
+ buffer_.consume(bytes_transferred);
+ return bytes_transferred;
+}
+
+template<class Stream, class DynamicBuffer>
+template<class MutableBufferSequence, class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+buffered_read_stream<Stream, DynamicBuffer>::
+async_read_some(
+ MutableBufferSequence const& buffers,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<next_layer_type>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(boost::asio::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence requirements not met");
+ if(buffer_.size() == 0 && capacity_ == 0)
+ return next_layer_.async_read_some(buffers,
+ std::forward<ReadHandler>(handler));
+ boost::asio::async_completion<ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ read_some_op<MutableBufferSequence, BOOST_ASIO_HANDLER_TYPE(
+ ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, *this, buffers}(
+ error_code{}, 0);
+ return init.result.get();
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/buffers_adapter.ipp b/boost/beast/core/impl/buffers_adapter.ipp
new file mode 100644
index 0000000000..e16d2a9b32
--- /dev/null
+++ b/boost/beast/core/impl/buffers_adapter.ipp
@@ -0,0 +1,517 @@
+//
+// 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_IMPL_BUFFERS_ADAPTER_IPP
+#define BOOST_BEAST_IMPL_BUFFERS_ADAPTER_IPP
+
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <cstring>
+#include <iterator>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+
+template<class MutableBufferSequence>
+class buffers_adapter<MutableBufferSequence>::
+ const_buffers_type
+{
+ buffers_adapter const* ba_;
+
+public:
+ using value_type = boost::asio::mutable_buffer;
+
+ class const_iterator;
+
+ const_buffers_type() = delete;
+ const_buffers_type(
+ const_buffers_type const&) = default;
+ const_buffers_type& operator=(
+ const_buffers_type const&) = default;
+
+ const_iterator
+ begin() const;
+
+ const_iterator
+ end() const;
+
+private:
+ friend class buffers_adapter;
+
+ const_buffers_type(buffers_adapter const& ba)
+ : ba_(&ba)
+ {
+ }
+};
+
+template<class MutableBufferSequence>
+class buffers_adapter<MutableBufferSequence>::
+ const_buffers_type::const_iterator
+{
+ iter_type it_;
+ buffers_adapter const* ba_ = nullptr;
+
+public:
+ using value_type = boost::asio::const_buffer;
+ using pointer = value_type const*;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ const_iterator() = default;
+ const_iterator(const_iterator&& other) = default;
+ const_iterator(const_iterator const& other) = default;
+ const_iterator& operator=(const_iterator&& other) = default;
+ const_iterator& operator=(const_iterator const& other) = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return ba_ == other.ba_ &&
+ it_ == other.it_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ value_type const b = *it_;
+ return value_type{b.data(),
+ (ba_->out_ == boost::asio::buffer_sequence_end(ba_->bs_) ||
+ it_ != ba_->out_) ? b.size() : ba_->out_pos_} +
+ (it_ == ba_->begin_ ? ba_->in_pos_ : 0);
+ }
+
+ pointer
+ operator->() const = delete;
+
+ const_iterator&
+ operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ const_iterator&
+ operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ const_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ --(*this);
+ return temp;
+ }
+
+private:
+ friend class const_buffers_type;
+
+ const_iterator(buffers_adapter const& ba,
+ iter_type iter)
+ : it_(iter)
+ , ba_(&ba)
+ {
+ }
+};
+
+template<class MutableBufferSequence>
+inline
+auto
+buffers_adapter<MutableBufferSequence>::const_buffers_type::begin() const ->
+ const_iterator
+{
+ return const_iterator{*ba_, ba_->begin_};
+}
+
+template<class MutableBufferSequence>
+inline
+auto
+buffers_adapter<MutableBufferSequence>::const_buffers_type::end() const ->
+ const_iterator
+{
+ return const_iterator{*ba_, ba_->out_ ==
+ ba_->end_ ? ba_->end_ : std::next(ba_->out_)};
+}
+
+//------------------------------------------------------------------------------
+
+template<class MutableBufferSequence>
+class buffers_adapter<MutableBufferSequence>::
+mutable_buffers_type
+{
+ buffers_adapter const* ba_;
+
+public:
+ using value_type = boost::asio::mutable_buffer;
+
+ class const_iterator;
+
+ mutable_buffers_type() = delete;
+ mutable_buffers_type(
+ mutable_buffers_type const&) = default;
+ mutable_buffers_type& operator=(
+ mutable_buffers_type const&) = default;
+
+ const_iterator
+ begin() const;
+
+ const_iterator
+ end() const;
+
+private:
+ friend class buffers_adapter;
+
+ mutable_buffers_type(
+ buffers_adapter const& ba)
+ : ba_(&ba)
+ {
+ }
+};
+
+template<class MutableBufferSequence>
+class buffers_adapter<MutableBufferSequence>::
+mutable_buffers_type::const_iterator
+{
+ iter_type it_;
+ buffers_adapter const* ba_ = nullptr;
+
+public:
+ using value_type = boost::asio::mutable_buffer;
+ using pointer = value_type const*;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ const_iterator() = default;
+ const_iterator(const_iterator&& other) = default;
+ const_iterator(const_iterator const& other) = default;
+ const_iterator& operator=(const_iterator&& other) = default;
+ const_iterator& operator=(const_iterator const& other) = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return ba_ == other.ba_ &&
+ it_ == other.it_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ value_type const b = *it_;
+ return value_type{b.data(),
+ it_ == std::prev(ba_->end_) ?
+ ba_->out_end_ : b.size()} +
+ (it_ == ba_->out_ ? ba_->out_pos_ : 0);
+ }
+
+ pointer
+ operator->() const = delete;
+
+ const_iterator&
+ operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ const_iterator&
+ operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ const_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ --(*this);
+ return temp;
+ }
+
+private:
+ friend class mutable_buffers_type;
+
+ const_iterator(buffers_adapter const& ba,
+ iter_type iter)
+ : it_(iter)
+ , ba_(&ba)
+ {
+ }
+};
+
+template<class MutableBufferSequence>
+inline
+auto
+buffers_adapter<MutableBufferSequence>::
+mutable_buffers_type::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{*ba_, ba_->out_};
+}
+
+template<class MutableBufferSequence>
+inline
+auto
+buffers_adapter<MutableBufferSequence>::
+mutable_buffers_type::
+end() const ->
+ const_iterator
+{
+ return const_iterator{*ba_, ba_->end_};
+}
+
+//------------------------------------------------------------------------------
+
+template<class MutableBufferSequence>
+buffers_adapter<MutableBufferSequence>::buffers_adapter(
+ buffers_adapter&& other)
+ : buffers_adapter(std::move(other),
+ std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.begin_),
+ std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.out_),
+ std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.end_))
+{
+}
+
+template<class MutableBufferSequence>
+buffers_adapter<MutableBufferSequence>::buffers_adapter(
+ buffers_adapter const& other)
+ : buffers_adapter(other,
+ std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.begin_),
+ std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.out_),
+ std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.end_))
+{
+}
+
+template<class MutableBufferSequence>
+auto
+buffers_adapter<MutableBufferSequence>::operator=(
+ buffers_adapter&& other) -> buffers_adapter&
+{
+ auto const nbegin = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.begin_);
+ auto const nout = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.out_);
+ auto const nend = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.end_);
+ bs_ = std::move(other.bs_);
+ begin_ = std::next(boost::asio::buffer_sequence_begin(bs_), nbegin);
+ out_ = std::next(boost::asio::buffer_sequence_begin(bs_), nout);
+ end_ = std::next(boost::asio::buffer_sequence_begin(bs_), nend);
+ max_size_ = other.max_size_;
+ in_pos_ = other.in_pos_;
+ in_size_ = other.in_size_;
+ out_pos_ = other.out_pos_;
+ out_end_ = other.out_end_;
+ return *this;
+}
+
+template<class MutableBufferSequence>
+auto
+buffers_adapter<MutableBufferSequence>::operator=(
+ buffers_adapter const& other) -> buffers_adapter&
+{
+ auto const nbegin = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.begin_);
+ auto const nout = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.out_);
+ auto const nend = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.end_);
+ bs_ = other.bs_;
+ begin_ = std::next(boost::asio::buffer_sequence_begin(bs_), nbegin);
+ out_ = std::next(boost::asio::buffer_sequence_begin(bs_), nout);
+ end_ = std::next(boost::asio::buffer_sequence_begin(bs_), nend);
+ max_size_ = other.max_size_;
+ in_pos_ = other.in_pos_;
+ in_size_ = other.in_size_;
+ out_pos_ = other.out_pos_;
+ out_end_ = other.out_end_;
+ return *this;
+}
+
+template<class MutableBufferSequence>
+buffers_adapter<MutableBufferSequence>::buffers_adapter(
+ MutableBufferSequence const& bs)
+ : bs_(bs)
+ , begin_(boost::asio::buffer_sequence_begin(bs_))
+ , out_ (boost::asio::buffer_sequence_begin(bs_))
+ , end_ (boost::asio::buffer_sequence_begin(bs_))
+ , max_size_(boost::asio::buffer_size(bs_))
+{
+}
+
+template<class MutableBufferSequence>
+auto
+buffers_adapter<MutableBufferSequence>::prepare(std::size_t n) ->
+ mutable_buffers_type
+{
+ using boost::asio::buffer_size;
+ end_ = out_;
+ if(end_ != boost::asio::buffer_sequence_end(bs_))
+ {
+ auto size = buffer_size(*end_) - out_pos_;
+ if(n > size)
+ {
+ n -= size;
+ while(++end_ !=
+ boost::asio::buffer_sequence_end(bs_))
+ {
+ size = buffer_size(*end_);
+ if(n < size)
+ {
+ out_end_ = n;
+ n = 0;
+ ++end_;
+ break;
+ }
+ n -= size;
+ out_end_ = size;
+ }
+ }
+ else
+ {
+ ++end_;
+ out_end_ = out_pos_ + n;
+ n = 0;
+ }
+ }
+ if(n > 0)
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "buffer overflow"});
+ return mutable_buffers_type{*this};
+}
+
+template<class MutableBufferSequence>
+void
+buffers_adapter<MutableBufferSequence>::commit(std::size_t n)
+{
+ using boost::asio::buffer_size;
+ if(out_ == end_)
+ return;
+ auto const last = std::prev(end_);
+ while(out_ != last)
+ {
+ auto const avail =
+ buffer_size(*out_) - out_pos_;
+ if(n < avail)
+ {
+ out_pos_ += n;
+ in_size_ += n;
+ max_size_ -= n;
+ return;
+ }
+ ++out_;
+ n -= avail;
+ out_pos_ = 0;
+ in_size_ += avail;
+ max_size_ -= avail;
+ }
+
+ n = (std::min)(n, out_end_ - out_pos_);
+ out_pos_ += n;
+ in_size_ += n;
+ max_size_ -= n;
+ if(out_pos_ == buffer_size(*out_))
+ {
+ ++out_;
+ out_pos_ = 0;
+ out_end_ = 0;
+ }
+}
+
+template<class MutableBufferSequence>
+inline
+auto
+buffers_adapter<MutableBufferSequence>::data() const ->
+ const_buffers_type
+{
+ return const_buffers_type{*this};
+}
+
+template<class MutableBufferSequence>
+void
+buffers_adapter<MutableBufferSequence>::consume(std::size_t n)
+{
+ using boost::asio::buffer_size;
+ while(begin_ != out_)
+ {
+ auto const avail =
+ buffer_size(*begin_) - in_pos_;
+ if(n < avail)
+ {
+ in_size_ -= n;
+ in_pos_ += n;
+ return;
+ }
+ n -= avail;
+ in_size_ -= avail;
+ in_pos_ = 0;
+ ++begin_;
+ }
+ auto const avail = out_pos_ - in_pos_;
+ if(n < avail)
+ {
+ in_size_ -= n;
+ in_pos_ += n;
+ }
+ else
+ {
+ in_size_ -= avail;
+ in_pos_ = out_pos_;
+ }
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/buffers_cat.ipp b/boost/beast/core/impl/buffers_cat.ipp
new file mode 100644
index 0000000000..d92d55233a
--- /dev/null
+++ b/boost/beast/core/impl/buffers_cat.ipp
@@ -0,0 +1,503 @@
+//
+// 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_IMPL_BUFFERS_CAT_IPP
+#define BOOST_BEAST_IMPL_BUFFERS_CAT_IPP
+
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/throw_exception.hpp>
+#include <array>
+#include <cstdint>
+#include <iterator>
+#include <new>
+#include <stdexcept>
+#include <tuple>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+template<class... Bn>
+class buffers_cat_view<Bn...>::const_iterator
+{
+ std::size_t n_;
+ std::tuple<Bn...> const* bn_;
+ std::array<char, detail::max_sizeof<
+ typename detail::buffer_sequence_iterator<Bn>::type...>()> buf_;
+
+ friend class buffers_cat_view<Bn...>;
+
+ template<std::size_t I>
+ using C = std::integral_constant<std::size_t, I>;
+
+ template<std::size_t I>
+ using iter_t = typename detail::buffer_sequence_iterator<
+ typename std::tuple_element<I, std::tuple<Bn...>>::type>::type;
+
+ template<std::size_t I>
+ iter_t<I>&
+ iter()
+ {
+ // type-pun
+ return *reinterpret_cast<
+ iter_t<I>*>(static_cast<void*>(buf_.data()));
+ }
+
+ template<std::size_t I>
+ iter_t<I> const&
+ iter() const
+ {
+ // type-pun
+ return *reinterpret_cast<
+ iter_t<I> const*>(static_cast<
+ void const*>(buf_.data()));
+ }
+
+public:
+ using value_type = typename
+ detail::common_buffers_type<Bn...>::type;
+ using pointer = value_type const*;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ ~const_iterator();
+ const_iterator();
+ const_iterator(const_iterator&& other);
+ const_iterator(const_iterator const& other);
+ const_iterator& operator=(const_iterator&& other);
+ const_iterator& operator=(const_iterator const& other);
+
+ bool
+ operator==(const_iterator const& other) const;
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return ! (*this == other);
+ }
+
+ reference
+ operator*() const;
+
+ pointer
+ operator->() const = delete;
+
+ const_iterator&
+ operator++();
+
+ const_iterator
+ operator++(int);
+
+ const_iterator&
+ operator--();
+
+ const_iterator
+ operator--(int);
+
+private:
+ const_iterator(
+ std::tuple<Bn...> const& bn, bool at_end);
+
+ void
+ construct(C<sizeof...(Bn)> const&)
+ {
+ auto constexpr I = sizeof...(Bn);
+ n_ = I;
+ }
+
+ template<std::size_t I>
+ void
+ construct(C<I> const&)
+ {
+ if(boost::asio::buffer_size(
+ std::get<I>(*bn_)) != 0)
+ {
+ n_ = I;
+ new(&buf_[0]) iter_t<I>{
+ boost::asio::buffer_sequence_begin(
+ std::get<I>(*bn_))};
+ return;
+ }
+ construct(C<I+1>{});
+ }
+
+ void
+ rconstruct(C<0> const&)
+ {
+ auto constexpr I = 0;
+ if(boost::asio::buffer_size(
+ std::get<I>(*bn_)) != 0)
+ {
+ n_ = I;
+ new(&buf_[0]) iter_t<I>{
+ boost::asio::buffer_sequence_end(
+ std::get<I>(*bn_))};
+ return;
+ }
+ BOOST_THROW_EXCEPTION(std::logic_error{
+ "invalid iterator"});
+ }
+
+ template<std::size_t I>
+ void
+ rconstruct(C<I> const&)
+ {
+ if(boost::asio::buffer_size(
+ std::get<I>(*bn_)) != 0)
+ {
+ n_ = I;
+ new(&buf_[0]) iter_t<I>{
+ boost::asio::buffer_sequence_end(
+ std::get<I>(*bn_))};
+ return;
+ }
+ rconstruct(C<I-1>{});
+ }
+
+ void
+ destroy(C<sizeof...(Bn)> const&)
+ {
+ return;
+ }
+
+ template<std::size_t I>
+ void
+ destroy(C<I> const&)
+ {
+ if(n_ == I)
+ {
+ using Iter = iter_t<I>;
+ iter<I>().~Iter();
+ return;
+ }
+ destroy(C<I+1>{});
+ }
+
+ void
+ move(const_iterator&&,
+ C<sizeof...(Bn)> const&)
+ {
+ }
+
+ template<std::size_t I>
+ void
+ move(const_iterator&& other,
+ C<I> const&)
+ {
+ if(n_ == I)
+ {
+ new(&buf_[0]) iter_t<I>{
+ std::move(other.iter<I>())};
+ return;
+ }
+ move(std::move(other), C<I+1>{});
+ }
+
+ void
+ copy(const_iterator const&,
+ C<sizeof...(Bn)> const&)
+ {
+ }
+
+ template<std::size_t I>
+ void
+ copy(const_iterator const& other,
+ C<I> const&)
+ {
+ if(n_ == I)
+ {
+ new(&buf_[0]) iter_t<I>{
+ other.iter<I>()};
+ return;
+ }
+ copy(other, C<I+1>{});
+ }
+
+ bool
+ equal(const_iterator const&,
+ C<sizeof...(Bn)> const&) const
+ {
+ return true;
+ }
+
+ template<std::size_t I>
+ bool
+ equal(const_iterator const& other,
+ C<I> const&) const
+ {
+ if(n_ == I)
+ return iter<I>() == other.iter<I>();
+ return equal(other, C<I+1>{});
+ }
+
+ [[noreturn]]
+ reference
+ dereference(C<sizeof...(Bn)> const&) const
+ {
+ BOOST_THROW_EXCEPTION(std::logic_error{
+ "invalid iterator"});
+ }
+
+ template<std::size_t I>
+ reference
+ dereference(C<I> const&) const
+ {
+ if(n_ == I)
+ return *iter<I>();
+ return dereference(C<I+1>{});
+ }
+
+ [[noreturn]]
+ void
+ increment(C<sizeof...(Bn)> const&)
+ {
+ BOOST_THROW_EXCEPTION(std::logic_error{
+ "invalid iterator"});
+ }
+
+ template<std::size_t I>
+ void
+ increment(C<I> const&)
+ {
+ if(n_ == I)
+ {
+ if(++iter<I>() !=
+ boost::asio::buffer_sequence_end(
+ std::get<I>(*bn_)))
+ return;
+ using Iter = iter_t<I>;
+ iter<I>().~Iter();
+ return construct(C<I+1>{});
+ }
+ increment(C<I+1>{});
+ }
+
+ void
+ decrement(C<sizeof...(Bn)> const&)
+ {
+ auto constexpr I = sizeof...(Bn);
+ if(n_ == I)
+ rconstruct(C<I-1>{});
+ decrement(C<I-1>{});
+ }
+
+ template<std::size_t I>
+ void
+ decrement(C<I> const&)
+ {
+ if(n_ == I)
+ {
+ if(iter<I>() !=
+ boost::asio::buffer_sequence_begin(
+ std::get<I>(*bn_)))
+ {
+ --iter<I>();
+ return;
+ }
+ --n_;
+ using Iter = iter_t<I>;
+ iter<I>().~Iter();
+ rconstruct(C<I-1>{});
+ }
+ decrement(C<I-1>{});
+ }
+
+ void
+ decrement(C<0> const&)
+ {
+ auto constexpr I = 0;
+ if(iter<I>() !=
+ boost::asio::buffer_sequence_begin(
+ std::get<I>(*bn_)))
+ {
+ --iter<I>();
+ return;
+ }
+ BOOST_THROW_EXCEPTION(std::logic_error{
+ "invalid iterator"});
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class... Bn>
+buffers_cat_view<Bn...>::
+const_iterator::~const_iterator()
+{
+ destroy(C<0>{});
+}
+
+template<class... Bn>
+buffers_cat_view<Bn...>::
+const_iterator::const_iterator()
+ : n_(sizeof...(Bn))
+ , bn_(nullptr)
+{
+}
+
+template<class... Bn>
+buffers_cat_view<Bn...>::
+const_iterator::const_iterator(
+ std::tuple<Bn...> const& bn, bool at_end)
+ : bn_(&bn)
+{
+ if(at_end)
+ n_ = sizeof...(Bn);
+ else
+ construct(C<0>{});
+}
+
+template<class... Bn>
+buffers_cat_view<Bn...>::
+const_iterator::const_iterator(const_iterator&& other)
+ : n_(other.n_)
+ , bn_(other.bn_)
+{
+ move(std::move(other), C<0>{});
+}
+
+template<class... Bn>
+buffers_cat_view<Bn...>::
+const_iterator::const_iterator(const_iterator const& other)
+ : n_(other.n_)
+ , bn_(other.bn_)
+{
+ copy(other, C<0>{});
+}
+
+template<class... Bn>
+auto
+buffers_cat_view<Bn...>::
+const_iterator::operator=(const_iterator&& other) ->
+ const_iterator&
+{
+ if(&other == this)
+ return *this;
+ destroy(C<0>{});
+ n_ = other.n_;
+ bn_ = other.bn_;
+ // VFALCO What about exceptions?
+ move(std::move(other), C<0>{});
+ return *this;
+}
+
+template<class... Bn>
+auto
+buffers_cat_view<Bn...>::
+const_iterator::operator=(const_iterator const& other) ->
+const_iterator&
+{
+ if(&other == this)
+ return *this;
+ destroy(C<0>{});
+ n_ = other.n_;
+ bn_ = other.bn_;
+ // VFALCO What about exceptions?
+ copy(other, C<0>{});
+ return *this;
+}
+
+template<class... Bn>
+bool
+buffers_cat_view<Bn...>::
+const_iterator::operator==(const_iterator const& other) const
+{
+ if(bn_ != other.bn_)
+ return false;
+ if(n_ != other.n_)
+ return false;
+ return equal(other, C<0>{});
+}
+
+template<class... Bn>
+auto
+buffers_cat_view<Bn...>::
+const_iterator::operator*() const ->
+ reference
+{
+ return dereference(C<0>{});
+}
+
+template<class... Bn>
+auto
+buffers_cat_view<Bn...>::
+const_iterator::operator++() ->
+ const_iterator&
+{
+ increment(C<0>{});
+ return *this;
+}
+
+template<class... Bn>
+auto
+buffers_cat_view<Bn...>::
+const_iterator::operator++(int) ->
+ const_iterator
+{
+ auto temp = *this;
+ ++(*this);
+ return temp;
+}
+
+template<class... Bn>
+auto
+buffers_cat_view<Bn...>::
+const_iterator::operator--() ->
+ const_iterator&
+{
+ decrement(C<sizeof...(Bn)>{});
+ return *this;
+}
+
+template<class... Bn>
+auto
+buffers_cat_view<Bn...>::
+const_iterator::operator--(int) ->
+ const_iterator
+{
+ auto temp = *this;
+ --(*this);
+ return temp;
+}
+
+//------------------------------------------------------------------------------
+
+template<class... Bn>
+buffers_cat_view<Bn...>::
+buffers_cat_view(Bn const&... bn)
+ : bn_(bn...)
+{
+}
+
+
+template<class... Bn>
+inline
+auto
+buffers_cat_view<Bn...>::begin() const ->
+ const_iterator
+{
+ return const_iterator{bn_, false};
+}
+
+template<class... Bn>
+inline
+auto
+buffers_cat_view<Bn...>::end() const ->
+ const_iterator
+{
+ return const_iterator{bn_, true};
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/buffers_prefix.ipp b/boost/beast/core/impl/buffers_prefix.ipp
new file mode 100644
index 0000000000..0bb92cc3ae
--- /dev/null
+++ b/boost/beast/core/impl/buffers_prefix.ipp
@@ -0,0 +1,258 @@
+//
+// 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_IMPL_BUFFERS_PREFIX_IPP
+#define BOOST_BEAST_IMPL_BUFFERS_PREFIX_IPP
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+namespace detail {
+
+inline
+boost::asio::const_buffer
+buffers_prefix(std::size_t size,
+ boost::asio::const_buffer buffer)
+{
+ return {buffer.data(),
+ (std::min)(size, buffer.size())};
+}
+
+inline
+boost::asio::mutable_buffer
+buffers_prefix(std::size_t size,
+ boost::asio::mutable_buffer buffer)
+{
+ return {buffer.data(),
+ (std::min)(size, buffer.size())};
+}
+
+} // detail
+
+template<class BufferSequence>
+class buffers_prefix_view<BufferSequence>::const_iterator
+{
+ friend class buffers_prefix_view<BufferSequence>;
+
+ buffers_prefix_view const* b_ = nullptr;
+ std::size_t remain_;
+ iter_type it_;
+
+public:
+ using value_type = typename std::conditional<
+ std::is_convertible<typename
+ std::iterator_traits<iter_type>::value_type,
+ boost::asio::mutable_buffer>::value,
+ boost::asio::mutable_buffer,
+ boost::asio::const_buffer>::type;
+ using pointer = value_type const*;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ const_iterator() = default;
+ const_iterator(const_iterator&& other) = default;
+ const_iterator(const_iterator const& other) = default;
+ const_iterator& operator=(const_iterator&& other) = default;
+ const_iterator& operator=(const_iterator const& other) = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return b_ == other.b_ && it_ == other.it_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return detail::buffers_prefix(remain_, *it_);
+ }
+
+ pointer
+ operator->() const = delete;
+
+ const_iterator&
+ operator++()
+ {
+ remain_ -= boost::asio::buffer_size(*it_++);
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ remain_ -= boost::asio::buffer_size(*it_++);
+ return temp;
+ }
+
+ const_iterator&
+ operator--()
+ {
+ remain_ += boost::asio::buffer_size(*--it_);
+ return *this;
+ }
+
+ const_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ remain_ += boost::asio::buffer_size(*--it_);
+ return temp;
+ }
+
+private:
+ const_iterator(buffers_prefix_view const& b,
+ std::true_type)
+ : b_(&b)
+ , remain_(0)
+ , it_(b_->end_)
+ {
+ }
+
+ const_iterator(buffers_prefix_view const& b,
+ std::false_type)
+ : b_(&b)
+ , remain_(b_->size_)
+ , it_(boost::asio::buffer_sequence_begin(b_->bs_))
+ {
+ }
+};
+
+template<class BufferSequence>
+void
+buffers_prefix_view<BufferSequence>::
+setup(std::size_t size)
+{
+ size_ = 0;
+ end_ = boost::asio::buffer_sequence_begin(bs_);
+ auto const last = bs_.end();
+ while(end_ != last)
+ {
+ auto const len =
+ boost::asio::buffer_size(*end_++);
+ if(len >= size)
+ {
+ size_ += size;
+ break;
+ }
+ size -= len;
+ size_ += len;
+ }
+}
+
+template<class BufferSequence>
+buffers_prefix_view<BufferSequence>::
+buffers_prefix_view(buffers_prefix_view&& other)
+ : buffers_prefix_view(std::move(other),
+ std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.end_))
+{
+}
+
+template<class BufferSequence>
+buffers_prefix_view<BufferSequence>::
+buffers_prefix_view(buffers_prefix_view const& other)
+ : buffers_prefix_view(other,
+ std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.end_))
+{
+}
+
+template<class BufferSequence>
+auto
+buffers_prefix_view<BufferSequence>::
+operator=(buffers_prefix_view&& other) ->
+ buffers_prefix_view&
+{
+ auto const dist = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.end_);
+ bs_ = std::move(other.bs_);
+ size_ = other.size_;
+ end_ = std::next(
+ boost::asio::buffer_sequence_begin(bs_),
+ dist);
+ return *this;
+}
+
+template<class BufferSequence>
+auto
+buffers_prefix_view<BufferSequence>::
+operator=(buffers_prefix_view const& other) ->
+ buffers_prefix_view&
+{
+ auto const dist = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.end_);
+ bs_ = other.bs_;
+ size_ = other.size_;
+ end_ = std::next(
+ boost::asio::buffer_sequence_begin(bs_),
+ dist);
+ return *this;
+}
+
+template<class BufferSequence>
+buffers_prefix_view<BufferSequence>::
+buffers_prefix_view(std::size_t size,
+ BufferSequence const& bs)
+ : bs_(bs)
+{
+ setup(size);
+}
+
+template<class BufferSequence>
+template<class... Args>
+buffers_prefix_view<BufferSequence>::
+buffers_prefix_view(std::size_t size,
+ boost::in_place_init_t, Args&&... args)
+ : bs_(std::forward<Args>(args)...)
+{
+ setup(size);
+}
+
+template<class BufferSequence>
+inline
+auto
+buffers_prefix_view<BufferSequence>::begin() const ->
+ const_iterator
+{
+ return const_iterator{*this, std::false_type{}};
+}
+
+template<class BufferSequence>
+inline
+auto
+buffers_prefix_view<BufferSequence>::end() const ->
+ const_iterator
+{
+ return const_iterator{*this, std::true_type{}};
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/buffers_suffix.ipp b/boost/beast/core/impl/buffers_suffix.ipp
new file mode 100644
index 0000000000..6356d63e0c
--- /dev/null
+++ b/boost/beast/core/impl/buffers_suffix.ipp
@@ -0,0 +1,249 @@
+//
+// 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_IMPL_BUFFERS_SUFFIX_IPP
+#define BOOST_BEAST_IMPL_BUFFERS_SUFFIX_IPP
+
+#include <boost/beast/core/type_traits.hpp>
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+template<class Buffers>
+class buffers_suffix<Buffers>::const_iterator
+{
+ friend class buffers_suffix<Buffers>;
+
+ using iter_type = typename
+ detail::buffer_sequence_iterator<Buffers>::type;
+
+ iter_type it_;
+ buffers_suffix const* b_ = nullptr;
+
+public:
+ using value_type = typename std::conditional<
+ std::is_convertible<typename
+ std::iterator_traits<iter_type>::value_type,
+ boost::asio::mutable_buffer>::value,
+ boost::asio::mutable_buffer,
+ boost::asio::const_buffer>::type;
+ using pointer = value_type const*;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ const_iterator() = default;
+ const_iterator(const_iterator&& other) = default;
+ const_iterator(const_iterator const& other) = default;
+ const_iterator& operator=(const_iterator&& other) = default;
+ const_iterator& operator=(const_iterator const& other) = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return b_ == other.b_ && it_ == other.it_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return it_ == b_->begin_
+ ? value_type{*it_} + b_->skip_
+ : *it_;
+ }
+
+ pointer
+ operator->() const = delete;
+
+ const_iterator&
+ operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ const_iterator&
+ operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ const_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ --(*this);
+ return temp;
+ }
+
+private:
+ const_iterator(buffers_suffix const& b,
+ iter_type it)
+ : it_(it)
+ , b_(&b)
+ {
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class Buffers>
+buffers_suffix<Buffers>::
+buffers_suffix()
+ : begin_(boost::asio::buffer_sequence_begin(bs_))
+{
+}
+
+template<class Buffers>
+buffers_suffix<Buffers>::
+buffers_suffix(buffers_suffix&& other)
+ : buffers_suffix(std::move(other),
+ std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(
+ other.bs_), other.begin_))
+{
+}
+
+template<class Buffers>
+buffers_suffix<Buffers>::
+buffers_suffix(buffers_suffix const& other)
+ : buffers_suffix(other,
+ std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(
+ other.bs_), other.begin_))
+{
+}
+
+template<class Buffers>
+buffers_suffix<Buffers>::
+buffers_suffix(Buffers const& bs)
+ : bs_(bs)
+ , begin_(boost::asio::buffer_sequence_begin(bs_))
+{
+ static_assert(
+ boost::asio::is_const_buffer_sequence<Buffers>::value||
+ boost::asio::is_mutable_buffer_sequence<Buffers>::value,
+ "BufferSequence requirements not met");
+}
+
+template<class Buffers>
+template<class... Args>
+buffers_suffix<Buffers>::
+buffers_suffix(boost::in_place_init_t, Args&&... args)
+ : bs_(std::forward<Args>(args)...)
+ , begin_(boost::asio::buffer_sequence_begin(bs_))
+{
+ static_assert(sizeof...(Args) > 0,
+ "Missing constructor arguments");
+ static_assert(
+ std::is_constructible<Buffers, Args...>::value,
+ "Buffers not constructible from arguments");
+}
+
+template<class Buffers>
+auto
+buffers_suffix<Buffers>::
+operator=(buffers_suffix&& other) ->
+ buffers_suffix&
+{
+ auto const dist = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.begin_);
+ bs_ = std::move(other.bs_);
+ begin_ = std::next(
+ boost::asio::buffer_sequence_begin(bs_),
+ dist);
+ skip_ = other.skip_;
+ return *this;
+}
+
+template<class Buffers>
+auto
+buffers_suffix<Buffers>::
+operator=(buffers_suffix const& other) ->
+ buffers_suffix&
+{
+ auto const dist = std::distance<iter_type>(
+ boost::asio::buffer_sequence_begin(other.bs_),
+ other.begin_);
+ bs_ = other.bs_;
+ begin_ = std::next(
+ boost::asio::buffer_sequence_begin(bs_), dist);
+ skip_ = other.skip_;
+ return *this;
+}
+
+template<class Buffers>
+inline
+auto
+buffers_suffix<Buffers>::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{*this, begin_};
+}
+
+template<class Buffers>
+inline
+auto
+buffers_suffix<Buffers>::
+end() const ->
+ const_iterator
+{
+ return const_iterator{*this,
+ boost::asio::buffer_sequence_end(bs_)};
+}
+
+template<class Buffers>
+void
+buffers_suffix<Buffers>::
+consume(std::size_t amount)
+{
+ using boost::asio::buffer_size;
+ auto const end =
+ boost::asio::buffer_sequence_end(bs_);
+ for(;amount > 0 && begin_ != end; ++begin_)
+ {
+ auto const len =
+ buffer_size(*begin_) - skip_;
+ if(amount < len)
+ {
+ skip_ += amount;
+ break;
+ }
+ amount -= len;
+ skip_ = 0;
+ }
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/file_posix.ipp b/boost/beast/core/impl/file_posix.ipp
new file mode 100644
index 0000000000..96ccdc3bbe
--- /dev/null
+++ b/boost/beast/core/impl/file_posix.ipp
@@ -0,0 +1,349 @@
+//
+// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP
+#define BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP
+
+#if ! defined(BOOST_BEAST_NO_POSIX_FADVISE)
+# if defined(__APPLE__) || (defined(ANDROID) && (__ANDROID_API__ < 21))
+# define BOOST_BEAST_NO_POSIX_FADVISE
+# endif
+#endif
+
+#if ! defined(BOOST_BEAST_USE_POSIX_FADVISE)
+# if ! defined(BOOST_BEAST_NO_POSIX_FADVISE)
+# define BOOST_BEAST_USE_POSIX_FADVISE 1
+# else
+# define BOOST_BEAST_USE_POSIX_FADVISE 0
+# endif
+#endif
+
+#include <limits>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+
+namespace boost {
+namespace beast {
+
+namespace detail {
+
+inline
+int
+file_posix_close(int fd)
+{
+ for(;;)
+ {
+ if(! ::close(fd))
+ break;
+ int const ev = errno;
+ if(errno != EINTR)
+ return ev;
+ }
+ return 0;
+}
+
+} // detail
+
+inline
+file_posix::
+~file_posix()
+{
+ if(fd_ != -1)
+ detail::file_posix_close(fd_);
+}
+
+inline
+file_posix::
+file_posix(file_posix&& other)
+ : fd_(other.fd_)
+{
+ other.fd_ = -1;
+}
+
+inline
+file_posix&
+file_posix::
+operator=(file_posix&& other)
+{
+ if(&other == this)
+ return *this;
+ if(fd_ != -1)
+ detail::file_posix_close(fd_);
+ fd_ = other.fd_;
+ other.fd_ = -1;
+ return *this;
+}
+
+inline
+void
+file_posix::
+native_handle(native_handle_type fd)
+{
+ if(fd_ != -1)
+ detail::file_posix_close(fd_);
+ fd_ = fd;
+}
+
+inline
+void
+file_posix::
+close(error_code& ec)
+{
+ if(fd_ != -1)
+ {
+ auto const ev =
+ detail::file_posix_close(fd_);
+ if(ev)
+ ec.assign(ev, generic_category());
+ else
+ ec.assign(0, ec.category());
+ fd_ = -1;
+ }
+ else
+ {
+ ec.assign(0, ec.category());
+ }
+}
+
+inline
+void
+file_posix::
+open(char const* path, file_mode mode, error_code& ec)
+{
+ if(fd_ != -1)
+ {
+ auto const ev =
+ detail::file_posix_close(fd_);
+ if(ev)
+ ec.assign(ev, generic_category());
+ else
+ ec.assign(0, ec.category());
+ fd_ = -1;
+ }
+ int f = 0;
+#if BOOST_BEAST_USE_POSIX_FADVISE
+ int advise = 0;
+#endif
+ switch(mode)
+ {
+ default:
+ case file_mode::read:
+ f = O_RDONLY;
+ #if BOOST_BEAST_USE_POSIX_FADVISE
+ advise = POSIX_FADV_RANDOM;
+ #endif
+ break;
+ case file_mode::scan:
+ f = O_RDONLY;
+ #if BOOST_BEAST_USE_POSIX_FADVISE
+ advise = POSIX_FADV_SEQUENTIAL;
+ #endif
+ break;
+
+ case file_mode::write:
+ f = O_RDWR | O_CREAT | O_TRUNC;
+ #if BOOST_BEAST_USE_POSIX_FADVISE
+ advise = POSIX_FADV_RANDOM;
+ #endif
+ break;
+
+ case file_mode::write_new:
+ f = O_RDWR | O_CREAT | O_EXCL;
+ #if BOOST_BEAST_USE_POSIX_FADVISE
+ advise = POSIX_FADV_RANDOM;
+ #endif
+ break;
+
+ case file_mode::write_existing:
+ f = O_RDWR | O_EXCL;
+ #if BOOST_BEAST_USE_POSIX_FADVISE
+ advise = POSIX_FADV_RANDOM;
+ #endif
+ break;
+
+ case file_mode::append:
+ f = O_RDWR | O_CREAT | O_TRUNC;
+ #if BOOST_BEAST_USE_POSIX_FADVISE
+ advise = POSIX_FADV_SEQUENTIAL;
+ #endif
+ break;
+
+ case file_mode::append_new:
+ f = O_RDWR | O_CREAT | O_EXCL;
+ #if BOOST_BEAST_USE_POSIX_FADVISE
+ advise = POSIX_FADV_SEQUENTIAL;
+ #endif
+ break;
+
+ case file_mode::append_existing:
+ f = O_RDWR | O_EXCL;
+ #if BOOST_BEAST_USE_POSIX_FADVISE
+ advise = POSIX_FADV_SEQUENTIAL;
+ #endif
+ break;
+ }
+ for(;;)
+ {
+ fd_ = ::open(path, f, 0644);
+ if(fd_ != -1)
+ break;
+ auto const ev = errno;
+ if(ev != EINTR)
+ {
+ ec.assign(ev, generic_category());
+ return;
+ }
+ }
+#if BOOST_BEAST_USE_POSIX_FADVISE
+ if(::posix_fadvise(fd_, 0, 0, advise))
+ {
+ auto const ev = errno;
+ detail::file_posix_close(fd_);
+ fd_ = -1;
+ ec.assign(ev, generic_category());
+ return;
+ }
+#endif
+ ec.assign(0, ec.category());
+}
+
+inline
+std::uint64_t
+file_posix::
+size(error_code& ec) const
+{
+ if(fd_ == -1)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ struct stat st;
+ if(::fstat(fd_, &st) != 0)
+ {
+ ec.assign(errno, generic_category());
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ return st.st_size;
+}
+
+inline
+std::uint64_t
+file_posix::
+pos(error_code& ec) const
+{
+ if(fd_ == -1)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ auto const result = ::lseek(fd_, 0, SEEK_CUR);
+ if(result == (off_t)-1)
+ {
+ ec.assign(errno, generic_category());
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ return result;
+}
+
+inline
+void
+file_posix::
+seek(std::uint64_t offset, error_code& ec)
+{
+ if(fd_ == -1)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return;
+ }
+ auto const result = ::lseek(fd_, offset, SEEK_SET);
+ if(result == static_cast<off_t>(-1))
+ {
+ ec.assign(errno, generic_category());
+ return;
+ }
+ ec.assign(0, ec.category());
+}
+
+inline
+std::size_t
+file_posix::
+read(void* buffer, std::size_t n, error_code& ec) const
+{
+ if(fd_ == -1)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ std::size_t nread = 0;
+ while(n > 0)
+ {
+ auto const amount = static_cast<ssize_t>((std::min)(
+ n, static_cast<std::size_t>(SSIZE_MAX)));
+ auto const result = ::read(fd_, buffer, amount);
+ if(result == -1)
+ {
+ auto const ev = errno;
+ if(ev == EINTR)
+ continue;
+ ec.assign(ev, generic_category());
+ return nread;
+ }
+ if(result == 0)
+ {
+ // short read
+ return nread;
+ }
+ n -= result;
+ nread += result;
+ buffer = reinterpret_cast<char*>(buffer) + result;
+ }
+ return nread;
+}
+
+inline
+std::size_t
+file_posix::
+write(void const* buffer, std::size_t n, error_code& ec)
+{
+ if(fd_ == -1)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ std::size_t nwritten = 0;
+ while(n > 0)
+ {
+ auto const amount = static_cast<ssize_t>((std::min)(
+ n, static_cast<std::size_t>(SSIZE_MAX)));
+ auto const result = ::write(fd_, buffer, amount);
+ if(result == -1)
+ {
+ auto const ev = errno;
+ if(ev == EINTR)
+ continue;
+ ec.assign(ev, generic_category());
+ return nwritten;
+ }
+ n -= result;
+ nwritten += result;
+ buffer = reinterpret_cast<char const*>(buffer) + result;
+ }
+ return nwritten;
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/file_stdio.ipp b/boost/beast/core/impl/file_stdio.ipp
new file mode 100644
index 0000000000..ca5cb0cbde
--- /dev/null
+++ b/boost/beast/core/impl/file_stdio.ipp
@@ -0,0 +1,239 @@
+//
+// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP
+#define BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP
+
+#include <limits>
+
+namespace boost {
+namespace beast {
+
+inline
+file_stdio::
+~file_stdio()
+{
+ if(f_)
+ fclose(f_);
+}
+
+inline
+file_stdio::
+file_stdio(file_stdio&& other)
+ : f_(other.f_)
+{
+ other.f_ = nullptr;
+}
+
+inline
+file_stdio&
+file_stdio::
+operator=(file_stdio&& other)
+{
+ if(&other == this)
+ return *this;
+ if(f_)
+ fclose(f_);
+ f_ = other.f_;
+ other.f_ = nullptr;
+ return *this;
+}
+
+inline
+void
+file_stdio::
+native_handle(FILE* f)
+{
+ if(f_)
+ fclose(f_);
+ f_ = f;
+}
+
+inline
+void
+file_stdio::
+close(error_code& ec)
+{
+ if(f_)
+ {
+ int failed = fclose(f_);
+ f_ = nullptr;
+ if(failed)
+ {
+ ec.assign(errno, generic_category());
+ return;
+ }
+ }
+ ec.assign(0, ec.category());
+}
+
+inline
+void
+file_stdio::
+open(char const* path, file_mode mode, error_code& ec)
+{
+ if(f_)
+ {
+ fclose(f_);
+ f_ = nullptr;
+ }
+ char const* s;
+ switch(mode)
+ {
+ default:
+ case file_mode::read: s = "rb"; break;
+ case file_mode::scan: s = "rb"; break;
+ case file_mode::write: s = "wb"; break;
+ case file_mode::write_new: s = "wbx"; break;
+ case file_mode::write_existing: s = "wb"; break;
+ case file_mode::append: s = "ab"; break;
+ case file_mode::append_new: s = "abx"; break;
+ case file_mode::append_existing: s = "ab"; break;
+ }
+#if BOOST_MSVC
+ auto const ev = fopen_s(&f_, path, s);
+ if(ev)
+ {
+ f_ = nullptr;
+ ec.assign(ev, generic_category());
+ return;
+ }
+#else
+ f_ = std::fopen(path, s);
+ if(! f_)
+ {
+ ec.assign(errno, generic_category());
+ return;
+ }
+#endif
+ ec.assign(0, ec.category());
+}
+
+inline
+std::uint64_t
+file_stdio::
+size(error_code& ec) const
+{
+ if(! f_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ long pos = std::ftell(f_);
+ if(pos == -1L)
+ {
+ ec.assign(errno, generic_category());
+ return 0;
+ }
+ int result = std::fseek(f_, 0, SEEK_END);
+ if(result != 0)
+ {
+ ec.assign(errno, generic_category());
+ return 0;
+ }
+ long size = std::ftell(f_);
+ if(size == -1L)
+ {
+ ec.assign(errno, generic_category());
+ std::fseek(f_, pos, SEEK_SET);
+ return 0;
+ }
+ result = std::fseek(f_, pos, SEEK_SET);
+ if(result != 0)
+ ec.assign(errno, generic_category());
+ else
+ ec.assign(0, ec.category());
+ return size;
+}
+
+inline
+std::uint64_t
+file_stdio::
+pos(error_code& ec) const
+{
+ if(! f_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ long pos = std::ftell(f_);
+ if(pos == -1L)
+ {
+ ec.assign(errno, generic_category());
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ return pos;
+}
+
+inline
+void
+file_stdio::
+seek(std::uint64_t offset, error_code& ec)
+{
+ if(! f_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return;
+ }
+ if(offset > (std::numeric_limits<long>::max)())
+ {
+ ec = make_error_code(errc::invalid_seek);
+ return;
+ }
+ int result = std::fseek(f_,
+ static_cast<long>(offset), SEEK_SET);
+ if(result != 0)
+ ec.assign(errno, generic_category());
+ else
+ ec.assign(0, ec.category());
+}
+
+inline
+std::size_t
+file_stdio::
+read(void* buffer, std::size_t n, error_code& ec) const
+{
+ if(! f_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ auto nread = std::fread(buffer, 1, n, f_);
+ if(std::ferror(f_))
+ {
+ ec.assign(errno, generic_category());
+ return 0;
+ }
+ return nread;
+}
+
+inline
+std::size_t
+file_stdio::
+write(void const* buffer, std::size_t n, error_code& ec)
+{
+ if(! f_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ auto nwritten = std::fwrite(buffer, 1, n, f_);
+ if(std::ferror(f_))
+ {
+ ec.assign(errno, generic_category());
+ return 0;
+ }
+ return nwritten;
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/file_win32.ipp b/boost/beast/core/impl/file_win32.ipp
new file mode 100644
index 0000000000..de4abae41f
--- /dev/null
+++ b/boost/beast/core/impl/file_win32.ipp
@@ -0,0 +1,364 @@
+//
+// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP
+#define BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP
+
+#include <boost/winapi/access_rights.hpp>
+#include <boost/winapi/error_codes.hpp>
+#include <boost/winapi/file_management.hpp>
+#include <boost/winapi/get_last_error.hpp>
+#include <limits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+namespace detail {
+
+// VFALCO Can't seem to get boost/detail/winapi to work with
+// this so use the non-Ex version for now.
+inline
+boost::winapi::BOOL_
+set_file_pointer_ex(
+ boost::winapi::HANDLE_ hFile,
+ boost::winapi::LARGE_INTEGER_ lpDistanceToMove,
+ boost::winapi::PLARGE_INTEGER_ lpNewFilePointer,
+ boost::winapi::DWORD_ dwMoveMethod)
+{
+ auto dwHighPart = lpDistanceToMove.u.HighPart;
+ auto dwLowPart = boost::winapi::SetFilePointer(
+ hFile,
+ lpDistanceToMove.u.LowPart,
+ &dwHighPart,
+ dwMoveMethod);
+ if(dwLowPart == boost::winapi::INVALID_SET_FILE_POINTER_)
+ return 0;
+ if(lpNewFilePointer)
+ {
+ lpNewFilePointer->u.LowPart = dwLowPart;
+ lpNewFilePointer->u.HighPart = dwHighPart;
+ }
+ return 1;
+}
+
+} // detail
+
+inline
+file_win32::
+~file_win32()
+{
+ if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
+ boost::winapi::CloseHandle(h_);
+}
+
+inline
+file_win32::
+file_win32(file_win32&& other)
+ : h_(other.h_)
+{
+ other.h_ = boost::winapi::INVALID_HANDLE_VALUE_;
+}
+
+inline
+file_win32&
+file_win32::
+operator=(file_win32&& other)
+{
+ if(&other == this)
+ return *this;
+ if(h_)
+ boost::winapi::CloseHandle(h_);
+ h_ = other.h_;
+ other.h_ = boost::winapi::INVALID_HANDLE_VALUE_;
+ return *this;
+}
+
+inline
+void
+file_win32::
+native_handle(native_handle_type h)
+{
+ if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
+ boost::winapi::CloseHandle(h_);
+ h_ = h;
+}
+
+inline
+void
+file_win32::
+close(error_code& ec)
+{
+ if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
+ {
+ if(! boost::winapi::CloseHandle(h_))
+ ec.assign(boost::winapi::GetLastError(),
+ system_category());
+ else
+ ec.assign(0, ec.category());
+ h_ = boost::winapi::INVALID_HANDLE_VALUE_;
+ }
+ else
+ {
+ ec.assign(0, ec.category());
+ }
+}
+
+inline
+void
+file_win32::
+open(char const* path, file_mode mode, error_code& ec)
+{
+ if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
+ {
+ boost::winapi::CloseHandle(h_);
+ h_ = boost::winapi::INVALID_HANDLE_VALUE_;
+ }
+ boost::winapi::DWORD_ share_mode = 0;
+ boost::winapi::DWORD_ desired_access = 0;
+ boost::winapi::DWORD_ creation_disposition = 0;
+ boost::winapi::DWORD_ flags_and_attributes = 0;
+/*
+ | When the file...
+ This argument: | Exists Does not exist
+ -------------------------+------------------------------------------------------
+ CREATE_ALWAYS | Truncates Creates
+ CREATE_NEW +-----------+ Fails Creates
+ OPEN_ALWAYS ===| does this |===> Opens Creates
+ OPEN_EXISTING +-----------+ Opens Fails
+ TRUNCATE_EXISTING | Truncates Fails
+*/
+ switch(mode)
+ {
+ default:
+ case file_mode::read:
+ desired_access = boost::winapi::GENERIC_READ_;
+ share_mode = boost::winapi::FILE_SHARE_READ_;
+ creation_disposition = boost::winapi::OPEN_EXISTING_;
+ flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
+ break;
+
+ case file_mode::scan:
+ desired_access = boost::winapi::GENERIC_READ_;
+ share_mode = boost::winapi::FILE_SHARE_READ_;
+ creation_disposition = boost::winapi::OPEN_EXISTING_;
+ flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
+ break;
+
+ case file_mode::write:
+ desired_access = boost::winapi::GENERIC_READ_ |
+ boost::winapi::GENERIC_WRITE_;
+ creation_disposition = boost::winapi::CREATE_ALWAYS_;
+ flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
+ break;
+
+ case file_mode::write_new:
+ desired_access = boost::winapi::GENERIC_READ_ |
+ boost::winapi::GENERIC_WRITE_;
+ creation_disposition = boost::winapi::CREATE_NEW_;
+ flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
+ break;
+
+ case file_mode::write_existing:
+ desired_access = boost::winapi::GENERIC_READ_ |
+ boost::winapi::GENERIC_WRITE_;
+ creation_disposition = boost::winapi::OPEN_EXISTING_;
+ flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
+ break;
+
+ case file_mode::append:
+ desired_access = boost::winapi::GENERIC_READ_ |
+ boost::winapi::GENERIC_WRITE_;
+
+ creation_disposition = boost::winapi::CREATE_ALWAYS_;
+ flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
+ break;
+
+ case file_mode::append_new:
+ desired_access = boost::winapi::GENERIC_READ_ |
+ boost::winapi::GENERIC_WRITE_;
+ creation_disposition = boost::winapi::CREATE_NEW_;
+ flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
+ break;
+
+ case file_mode::append_existing:
+ desired_access = boost::winapi::GENERIC_READ_ |
+ boost::winapi::GENERIC_WRITE_;
+ creation_disposition = boost::winapi::OPEN_EXISTING_;
+ flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
+ break;
+ }
+ h_ = ::CreateFileA(
+ path,
+ desired_access,
+ share_mode,
+ NULL,
+ creation_disposition,
+ flags_and_attributes,
+ NULL);
+ if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
+ ec.assign(boost::winapi::GetLastError(),
+ system_category());
+ else
+ ec.assign(0, ec.category());
+}
+
+inline
+std::uint64_t
+file_win32::
+size(error_code& ec) const
+{
+ if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ boost::winapi::LARGE_INTEGER_ fileSize;
+ if(! boost::winapi::GetFileSizeEx(h_, &fileSize))
+ {
+ ec.assign(boost::winapi::GetLastError(),
+ system_category());
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ return fileSize.QuadPart;
+}
+
+inline
+std::uint64_t
+file_win32::
+pos(error_code& ec)
+{
+ if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ boost::winapi::LARGE_INTEGER_ in;
+ boost::winapi::LARGE_INTEGER_ out;
+ in.QuadPart = 0;
+ if(! detail::set_file_pointer_ex(h_, in, &out,
+ boost::winapi::FILE_CURRENT_))
+ {
+ ec.assign(boost::winapi::GetLastError(),
+ system_category());
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ return out.QuadPart;
+}
+
+inline
+void
+file_win32::
+seek(std::uint64_t offset, error_code& ec)
+{
+ if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return;
+ }
+ boost::winapi::LARGE_INTEGER_ in;
+ in.QuadPart = offset;
+ if(! detail::set_file_pointer_ex(h_, in, 0,
+ boost::winapi::FILE_BEGIN_))
+ {
+ ec.assign(boost::winapi::GetLastError(),
+ system_category());
+ return;
+ }
+ ec.assign(0, ec.category());
+}
+
+inline
+std::size_t
+file_win32::
+read(void* buffer, std::size_t n, error_code& ec)
+{
+ if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ std::size_t nread = 0;
+ while(n > 0)
+ {
+ boost::winapi::DWORD_ amount;
+ if(n > (std::numeric_limits<
+ boost::winapi::DWORD_>::max)())
+ amount = (std::numeric_limits<
+ boost::winapi::DWORD_>::max)();
+ else
+ amount = static_cast<
+ boost::winapi::DWORD_>(n);
+ boost::winapi::DWORD_ bytesRead;
+ if(! ::ReadFile(h_, buffer, amount, &bytesRead, 0))
+ {
+ auto const dwError = ::GetLastError();
+ if(dwError != boost::winapi::ERROR_HANDLE_EOF_)
+ ec.assign(::GetLastError(), system_category());
+ else
+ ec.assign(0, ec.category());
+ return nread;
+ }
+ if(bytesRead == 0)
+ return nread;
+ n -= bytesRead;
+ nread += bytesRead;
+ buffer = reinterpret_cast<char*>(buffer) + bytesRead;
+ }
+ ec.assign(0, ec.category());
+ return nread;
+}
+
+inline
+std::size_t
+file_win32::
+write(void const* buffer, std::size_t n, error_code& ec)
+{
+ if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
+ {
+ ec.assign(errc::invalid_argument, generic_category());
+ return 0;
+ }
+ std::size_t nwritten = 0;
+ while(n > 0)
+ {
+ boost::winapi::DWORD_ amount;
+ if(n > (std::numeric_limits<
+ boost::winapi::DWORD_>::max)())
+ amount = (std::numeric_limits<
+ boost::winapi::DWORD_>::max)();
+ else
+ amount = static_cast<
+ boost::winapi::DWORD_>(n);
+ boost::winapi::DWORD_ bytesWritten;
+ if(! ::WriteFile(h_, buffer, amount, &bytesWritten, 0))
+ {
+ auto const dwError = ::GetLastError();
+ if(dwError != boost::winapi::ERROR_HANDLE_EOF_)
+ ec.assign(::GetLastError(), system_category());
+ else
+ ec.assign(0, ec.category());
+ return nwritten;
+ }
+ if(bytesWritten == 0)
+ return nwritten;
+ n -= bytesWritten;
+ nwritten += bytesWritten;
+ buffer = reinterpret_cast<char const*>(buffer) + bytesWritten;
+ }
+ ec.assign(0, ec.category());
+ return nwritten;
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/flat_buffer.ipp b/boost/beast/core/impl/flat_buffer.ipp
new file mode 100644
index 0000000000..ffca651cc9
--- /dev/null
+++ b/boost/beast/core/impl/flat_buffer.ipp
@@ -0,0 +1,475 @@
+//
+// 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_IMPL_FLAT_BUFFER_HPP
+#define BOOST_BEAST_IMPL_FLAT_BUFFER_HPP
+
+#include <boost/assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+
+/* Memory is laid out thusly:
+
+ begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
+*/
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+~basic_flat_buffer()
+{
+ if(begin_)
+ alloc_traits::deallocate(
+ this->member(), begin_, dist(begin_, end_));
+}
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer()
+ : begin_(nullptr)
+ , in_(nullptr)
+ , out_(nullptr)
+ , last_(nullptr)
+ , end_(nullptr)
+ , max_((std::numeric_limits<std::size_t>::max)())
+{
+}
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(std::size_t limit)
+ : begin_(nullptr)
+ , in_(nullptr)
+ , out_(nullptr)
+ , last_(nullptr)
+ , end_(nullptr)
+ , max_(limit)
+{
+}
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(Allocator const& alloc)
+ : detail::empty_base_optimization<base_alloc_type>(alloc)
+ , begin_(nullptr)
+ , in_(nullptr)
+ , out_(nullptr)
+ , last_(nullptr)
+ , end_(nullptr)
+ , max_((std::numeric_limits<std::size_t>::max)())
+{
+}
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(std::size_t limit, Allocator const& alloc)
+ : detail::empty_base_optimization<base_alloc_type>(alloc)
+ , begin_(nullptr)
+ , in_(nullptr)
+ , out_(nullptr)
+ , last_(nullptr)
+ , end_(nullptr)
+ , max_(limit)
+{
+}
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(basic_flat_buffer&& other)
+ : detail::empty_base_optimization<base_alloc_type>(
+ std::move(other.member()))
+ , begin_(other.begin_)
+ , in_(other.in_)
+ , out_(other.out_)
+ , last_(out_)
+ , end_(other.end_)
+ , max_(other.max_)
+{
+ other.begin_ = nullptr;
+ other.in_ = nullptr;
+ other.out_ = nullptr;
+ other.last_ = nullptr;
+ other.end_ = nullptr;
+}
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(basic_flat_buffer&& other,
+ Allocator const& alloc)
+ : detail::empty_base_optimization<base_alloc_type>(alloc)
+{
+ if(this->member() != other.member())
+ {
+ begin_ = nullptr;
+ in_ = nullptr;
+ out_ = nullptr;
+ last_ = nullptr;
+ end_ = nullptr;
+ max_ = other.max_;
+ copy_from(other);
+ other.reset();
+ }
+ else
+ {
+ begin_ = other.begin_;
+ in_ = other.in_;
+ out_ = other.out_;
+ last_ = out_;
+ end_ = other.end_;
+ max_ = other.max_;
+ other.begin_ = nullptr;
+ other.in_ = nullptr;
+ other.out_ = nullptr;
+ other.last_ = nullptr;
+ other.end_ = nullptr;
+ }
+}
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(basic_flat_buffer const& other)
+ : detail::empty_base_optimization<base_alloc_type>(
+ alloc_traits::select_on_container_copy_construction(
+ other.member()))
+ , begin_(nullptr)
+ , in_(nullptr)
+ , out_(nullptr)
+ , last_(nullptr)
+ , end_(nullptr)
+ , max_(other.max_)
+{
+ copy_from(other);
+}
+
+template<class Allocator>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(basic_flat_buffer const& other,
+ Allocator const& alloc)
+ : detail::empty_base_optimization<base_alloc_type>(alloc)
+ , begin_(nullptr)
+ , in_(nullptr)
+ , out_(nullptr)
+ , last_(nullptr)
+ , end_(nullptr)
+ , max_(other.max_)
+{
+ copy_from(other);
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(
+ basic_flat_buffer<OtherAlloc> const& other)
+ : begin_(nullptr)
+ , in_(nullptr)
+ , out_(nullptr)
+ , last_(nullptr)
+ , end_(nullptr)
+ , max_(other.max_)
+{
+ copy_from(other);
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+basic_flat_buffer<Allocator>::
+basic_flat_buffer(basic_flat_buffer<OtherAlloc> const& other,
+ Allocator const& alloc)
+ : detail::empty_base_optimization<base_alloc_type>(alloc)
+ , begin_(nullptr)
+ , in_(nullptr)
+ , out_(nullptr)
+ , last_(nullptr)
+ , end_(nullptr)
+ , max_(other.max_)
+{
+ copy_from(other);
+}
+
+template<class Allocator>
+auto
+basic_flat_buffer<Allocator>::
+operator=(basic_flat_buffer&& other) ->
+ basic_flat_buffer&
+{
+ if(this != &other)
+ move_assign(other, std::integral_constant<bool,
+ alloc_traits::propagate_on_container_move_assignment::value>{});
+ return *this;
+}
+
+template<class Allocator>
+auto
+basic_flat_buffer<Allocator>::
+operator=(basic_flat_buffer const& other) ->
+ basic_flat_buffer&
+{
+ if(this != &other)
+ copy_assign(other, std::integral_constant<bool,
+ alloc_traits::propagate_on_container_copy_assignment::value>{});
+ return *this;
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+auto
+basic_flat_buffer<Allocator>::
+operator=(basic_flat_buffer<OtherAlloc> const& other) ->
+ basic_flat_buffer&
+{
+ reset();
+ max_ = other.max_;
+ copy_from(other);
+ return *this;
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+auto
+basic_flat_buffer<Allocator>::
+prepare(std::size_t n) ->
+ mutable_buffers_type
+{
+ if(n <= dist(out_, end_))
+ {
+ // existing capacity is sufficient
+ last_ = out_ + n;
+ return{out_, n};
+ }
+ auto const len = size();
+ if(n <= capacity() - len)
+ {
+ // after a memmove,
+ // existing capacity is sufficient
+ if(len > 0)
+ std::memmove(begin_, in_, len);
+ in_ = begin_;
+ out_ = in_ + len;
+ last_ = out_ + n;
+ return {out_, n};
+ }
+ // enforce maximum capacity
+ if(n > max_ - len)
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "basic_flat_buffer overflow"});
+ // allocate a new buffer
+ auto const new_size = (std::min<std::size_t>)(
+ max_,
+ (std::max<std::size_t>)(2 * len, len + n));
+ auto const p = alloc_traits::allocate(
+ this->member(), new_size);
+ if(begin_)
+ {
+ BOOST_ASSERT(p);
+ BOOST_ASSERT(in_);
+ std::memcpy(p, in_, len);
+ alloc_traits::deallocate(
+ this->member(), begin_, capacity());
+ }
+ begin_ = p;
+ in_ = begin_;
+ out_ = in_ + len;
+ last_ = out_ + n;
+ end_ = begin_ + new_size;
+ return {out_, n};
+}
+
+template<class Allocator>
+void
+basic_flat_buffer<Allocator>::
+consume(std::size_t n)
+{
+ if(n >= dist(in_, out_))
+ {
+ in_ = begin_;
+ out_ = begin_;
+ return;
+ }
+ in_ += n;
+}
+
+template<class Allocator>
+void
+basic_flat_buffer<Allocator>::
+shrink_to_fit()
+{
+ auto const len = size();
+ if(len == capacity())
+ return;
+ char* p;
+ if(len > 0)
+ {
+ BOOST_ASSERT(begin_);
+ BOOST_ASSERT(in_);
+ p = alloc_traits::allocate(
+ this->member(), len);
+ std::memcpy(p, in_, len);
+ }
+ else
+ {
+ p = nullptr;
+ }
+ alloc_traits::deallocate(
+ this->member(), begin_, dist(begin_, end_));
+ begin_ = p;
+ in_ = begin_;
+ out_ = begin_ + len;
+ last_ = out_;
+ end_ = out_;
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+inline
+void
+basic_flat_buffer<Allocator>::
+reset()
+{
+ consume(size());
+ shrink_to_fit();
+}
+
+template<class Allocator>
+template<class DynamicBuffer>
+inline
+void
+basic_flat_buffer<Allocator>::
+copy_from(DynamicBuffer const& buffer)
+{
+ if(buffer.size() == 0)
+ return;
+ using boost::asio::buffer_copy;
+ commit(buffer_copy(
+ prepare(buffer.size()), buffer.data()));
+}
+
+template<class Allocator>
+inline
+void
+basic_flat_buffer<Allocator>::
+move_assign(basic_flat_buffer& other, std::true_type)
+{
+ reset();
+ this->member() = std::move(other.member());
+ begin_ = other.begin_;
+ in_ = other.in_;
+ out_ = other.out_;
+ last_ = out_;
+ end_ = other.end_;
+ max_ = other.max_;
+ other.begin_ = nullptr;
+ other.in_ = nullptr;
+ other.out_ = nullptr;
+ other.last_ = nullptr;
+ other.end_ = nullptr;
+}
+
+template<class Allocator>
+inline
+void
+basic_flat_buffer<Allocator>::
+move_assign(basic_flat_buffer& other, std::false_type)
+{
+ reset();
+ if(this->member() != other.member())
+ {
+ copy_from(other);
+ other.reset();
+ }
+ else
+ {
+ move_assign(other, std::true_type{});
+ }
+}
+
+template<class Allocator>
+inline
+void
+basic_flat_buffer<Allocator>::
+copy_assign(basic_flat_buffer const& other, std::true_type)
+{
+ reset();
+ max_ = other.max_;
+ this->member() = other.member();
+ copy_from(other);
+}
+
+template<class Allocator>
+inline
+void
+basic_flat_buffer<Allocator>::
+copy_assign(basic_flat_buffer const& other, std::false_type)
+{
+ reset();
+ max_ = other.max_;
+ copy_from(other);
+}
+
+template<class Allocator>
+inline
+void
+basic_flat_buffer<Allocator>::
+swap(basic_flat_buffer& other)
+{
+ swap(other, typename
+ alloc_traits::propagate_on_container_swap{});
+}
+
+template<class Allocator>
+inline
+void
+basic_flat_buffer<Allocator>::
+swap(basic_flat_buffer& other, std::true_type)
+{
+ using std::swap;
+ swap(this->member(), other.member());
+ swap(max_, other.max_);
+ swap(begin_, other.begin_);
+ swap(in_, other.in_);
+ swap(out_, other.out_);
+ last_ = this->out_;
+ other.last_ = other.out_;
+ swap(end_, other.end_);
+}
+
+template<class Allocator>
+inline
+void
+basic_flat_buffer<Allocator>::
+swap(basic_flat_buffer& other, std::false_type)
+{
+ BOOST_ASSERT(this->member() == other.member());
+ using std::swap;
+ swap(max_, other.max_);
+ swap(begin_, other.begin_);
+ swap(in_, other.in_);
+ swap(out_, other.out_);
+ last_ = this->out_;
+ other.last_ = other.out_;
+ swap(end_, other.end_);
+}
+
+template<class Allocator>
+void
+swap(
+ basic_flat_buffer<Allocator>& lhs,
+ basic_flat_buffer<Allocator>& rhs)
+{
+ lhs.swap(rhs);
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/flat_static_buffer.ipp b/boost/beast/core/impl/flat_static_buffer.ipp
new file mode 100644
index 0000000000..3d38b92958
--- /dev/null
+++ b/boost/beast/core/impl/flat_static_buffer.ipp
@@ -0,0 +1,151 @@
+//
+// 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_IMPL_FLAT_STATIC_BUFFER_IPP
+#define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP
+
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <cstring>
+#include <iterator>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+
+/* Memory is laid out thusly:
+
+ begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
+*/
+
+inline
+auto
+flat_static_buffer_base::
+data() const ->
+ const_buffers_type
+{
+ return {in_, dist(in_, out_)};
+}
+
+inline
+void
+flat_static_buffer_base::
+reset()
+{
+ reset_impl();
+}
+
+inline
+auto
+flat_static_buffer_base::
+prepare(std::size_t n) ->
+ mutable_buffers_type
+{
+ return prepare_impl(n);
+}
+
+inline
+void
+flat_static_buffer_base::
+reset(void* p, std::size_t n)
+{
+ reset_impl(p, n);
+}
+
+template<class>
+void
+flat_static_buffer_base::
+reset_impl()
+{
+ in_ = begin_;
+ out_ = begin_;
+ last_ = begin_;
+}
+
+template<class>
+void
+flat_static_buffer_base::
+reset_impl(void* p, std::size_t n)
+{
+ begin_ =
+ reinterpret_cast<char*>(p);
+ in_ = begin_;
+ out_ = begin_;
+ last_ = begin_;
+ end_ = begin_ + n;
+}
+
+template<class>
+auto
+flat_static_buffer_base::
+prepare_impl(std::size_t n) ->
+ mutable_buffers_type
+{
+ if(n <= dist(out_, end_))
+ {
+ last_ = out_ + n;
+ return {out_, n};
+ }
+ auto const len = size();
+ if(n > capacity() - len)
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "buffer overflow"});
+ if(len > 0)
+ std::memmove(begin_, in_, len);
+ in_ = begin_;
+ out_ = in_ + len;
+ last_ = out_ + n;
+ return {out_, n};
+}
+
+template<class>
+void
+flat_static_buffer_base::
+consume_impl(std::size_t n)
+{
+ if(n >= size())
+ {
+ in_ = begin_;
+ out_ = in_;
+ return;
+ }
+ in_ += n;
+}
+
+//------------------------------------------------------------------------------
+
+template<std::size_t N>
+flat_static_buffer<N>::
+flat_static_buffer(flat_static_buffer const& other)
+ : flat_static_buffer_base(buf_, N)
+{
+ using boost::asio::buffer_copy;
+ this->commit(buffer_copy(
+ this->prepare(other.size()), other.data()));
+}
+
+template<std::size_t N>
+auto
+flat_static_buffer<N>::
+operator=(flat_static_buffer const& other) ->
+ flat_static_buffer<N>&
+{
+ using boost::asio::buffer_copy;
+ this->consume(this->size());
+ this->commit(buffer_copy(
+ this->prepare(other.size()), other.data()));
+ return *this;
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/handler_ptr.ipp b/boost/beast/core/impl/handler_ptr.ipp
new file mode 100644
index 0000000000..15a159a8fd
--- /dev/null
+++ b/boost/beast/core/impl/handler_ptr.ipp
@@ -0,0 +1,147 @@
+//
+// 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_IMPL_HANDLER_PTR_HPP
+#define BOOST_BEAST_IMPL_HANDLER_PTR_HPP
+
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/assert.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+
+template<class T, class Handler>
+template<class DeducedHandler, class... Args>
+inline
+handler_ptr<T, Handler>::P::
+P(DeducedHandler&& h, Args&&... args)
+ : n(1)
+ , handler(std::forward<DeducedHandler>(h))
+{
+ typename std::allocator_traits<
+ boost::asio::associated_allocator_t<Handler>>::
+ template rebind_alloc<T> alloc{
+ boost::asio::get_associated_allocator(handler)};
+ t = std::allocator_traits<decltype(alloc)>::allocate(alloc, 1);
+ try
+ {
+ t = new(t) T{handler,
+ std::forward<Args>(args)...};
+ }
+ catch(...)
+ {
+ std::allocator_traits<
+ decltype(alloc)>::deallocate(alloc, t, 1);
+ throw;
+ }
+}
+
+template<class T, class Handler>
+handler_ptr<T, Handler>::
+~handler_ptr()
+{
+ if(! p_)
+ return;
+ if(--p_->n)
+ return;
+ if(p_->t)
+ {
+ p_->t->~T();
+ typename std::allocator_traits<
+ boost::asio::associated_allocator_t<Handler>>::
+ template rebind_alloc<T> alloc{
+ boost::asio::get_associated_allocator(
+ p_->handler)};
+ std::allocator_traits<
+ decltype(alloc)>::deallocate(alloc, p_->t, 1);
+ }
+ delete p_;
+}
+
+template<class T, class Handler>
+handler_ptr<T, Handler>::
+handler_ptr(handler_ptr&& other)
+ : p_(other.p_)
+{
+ other.p_ = nullptr;
+}
+
+template<class T, class Handler>
+handler_ptr<T, Handler>::
+handler_ptr(handler_ptr const& other)
+ : p_(other.p_)
+{
+ if(p_)
+ ++p_->n;
+}
+
+template<class T, class Handler>
+template<class... Args>
+handler_ptr<T, Handler>::
+handler_ptr(Handler&& handler, Args&&... args)
+ : p_(new P{std::move(handler),
+ std::forward<Args>(args)...})
+{
+ BOOST_STATIC_ASSERT(! std::is_array<T>::value);
+}
+
+template<class T, class Handler>
+template<class... Args>
+handler_ptr<T, Handler>::
+handler_ptr(Handler const& handler, Args&&... args)
+ : p_(new P{handler, std::forward<Args>(args)...})
+{
+ BOOST_STATIC_ASSERT(! std::is_array<T>::value);
+}
+
+template<class T, class Handler>
+auto
+handler_ptr<T, Handler>::
+release_handler() ->
+ handler_type
+{
+ BOOST_ASSERT(p_);
+ BOOST_ASSERT(p_->t);
+ p_->t->~T();
+ typename std::allocator_traits<
+ boost::asio::associated_allocator_t<Handler>>::
+ template rebind_alloc<T> alloc{
+ boost::asio::get_associated_allocator(
+ p_->handler)};
+ std::allocator_traits<
+ decltype(alloc)>::deallocate(alloc, p_->t, 1);
+ p_->t = nullptr;
+ return std::move(p_->handler);
+}
+
+template<class T, class Handler>
+template<class... Args>
+void
+handler_ptr<T, Handler>::
+invoke(Args&&... args)
+{
+ BOOST_ASSERT(p_);
+ BOOST_ASSERT(p_->t);
+ p_->t->~T();
+ typename std::allocator_traits<
+ boost::asio::associated_allocator_t<Handler>>::
+ template rebind_alloc<T> alloc{
+ boost::asio::get_associated_allocator(
+ p_->handler)};
+ std::allocator_traits<
+ decltype(alloc)>::deallocate(alloc, p_->t, 1);
+ p_->t = nullptr;
+ p_->handler(std::forward<Args>(args)...);
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/multi_buffer.ipp b/boost/beast/core/impl/multi_buffer.ipp
new file mode 100644
index 0000000000..7b66694cb3
--- /dev/null
+++ b/boost/beast/core/impl/multi_buffer.ipp
@@ -0,0 +1,1063 @@
+//
+// 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_IMPL_MULTI_BUFFER_IPP
+#define BOOST_BEAST_IMPL_MULTI_BUFFER_IPP
+
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <exception>
+#include <sstream>
+#include <string>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+/* These diagrams illustrate the layout and state variables.
+
+1 Input and output contained entirely in one element:
+
+ 0 out_
+ |<-------------+------------------------------------------->|
+ in_pos_ out_pos_ out_end_
+
+
+2 Output contained in first and second elements:
+
+ out_
+ |<------+----------+------->| |<----------+-------------->|
+ in_pos_ out_pos_ out_end_
+
+
+3 Output contained in the second element:
+
+ out_
+ |<------------+------------>| |<----+-------------------->|
+ in_pos_ out_pos_ out_end_
+
+
+4 Output contained in second and third elements:
+
+ out_
+ |<-----+-------->| |<-------+------>| |<--------------->|
+ in_pos_ out_pos_ out_end_
+
+
+5 Input sequence is empty:
+
+ out_
+ |<------+------------------>| |<-----------+------------->|
+ out_pos_ out_end_
+ in_pos_
+
+
+6 Output sequence is empty:
+
+ out_
+ |<------+------------------>| |<------+------------------>|
+ in_pos_ out_pos_
+ out_end_
+
+
+7 The end of output can point to the end of an element.
+ But out_pos_ should never point to the end:
+
+ out_
+ |<------+------------------>| |<------+------------------>|
+ in_pos_ out_pos_ out_end_
+
+
+8 When the input sequence entirely fills the last element and
+ the output sequence is empty, out_ will point to the end of
+ the list of buffers, and out_pos_ and out_end_ will be 0:
+
+
+ |<------+------------------>| out_ == list_.end()
+ in_pos_ out_pos_ == 0
+ out_end_ == 0
+*/
+
+template<class Allocator>
+class basic_multi_buffer<Allocator>::element
+ : public boost::intrusive::list_base_hook<
+ boost::intrusive::link_mode<
+ boost::intrusive::normal_link>>
+{
+ using size_type =
+ typename detail::allocator_traits<Allocator>::size_type;
+
+ size_type const size_;
+
+public:
+ element(element const&) = delete;
+ element& operator=(element const&) = delete;
+
+ explicit
+ element(size_type n)
+ : size_(n)
+ {
+ }
+
+ size_type
+ size() const
+ {
+ return size_;
+ }
+
+ char*
+ data() const
+ {
+ return const_cast<char*>(
+ reinterpret_cast<char const*>(this+1));
+ }
+};
+
+template<class Allocator>
+class basic_multi_buffer<Allocator>::const_buffers_type
+{
+ basic_multi_buffer const* b_;
+
+ friend class basic_multi_buffer;
+
+ explicit
+ const_buffers_type(basic_multi_buffer const& b);
+
+public:
+ using value_type = boost::asio::mutable_buffer;
+
+ class const_iterator;
+
+ const_buffers_type() = delete;
+ const_buffers_type(const_buffers_type const&) = default;
+ const_buffers_type& operator=(const_buffers_type const&) = default;
+
+ const_iterator
+ begin() const;
+
+ const_iterator
+ end() const;
+
+ friend
+ std::size_t
+ buffer_size(const_buffers_type const& buffers)
+ {
+ return buffers.b_->size();
+ }
+};
+
+template<class Allocator>
+class basic_multi_buffer<Allocator>::mutable_buffers_type
+{
+ basic_multi_buffer const* b_;
+
+ friend class basic_multi_buffer;
+
+ explicit
+ mutable_buffers_type(basic_multi_buffer const& b);
+
+public:
+ using value_type = mutable_buffer;
+
+ class const_iterator;
+
+ mutable_buffers_type() = delete;
+ mutable_buffers_type(mutable_buffers_type const&) = default;
+ mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
+
+ const_iterator
+ begin() const;
+
+ const_iterator
+ end() const;
+};
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+class basic_multi_buffer<Allocator>::const_buffers_type::const_iterator
+{
+ basic_multi_buffer const* b_ = nullptr;
+ typename list_type::const_iterator it_;
+
+public:
+ using value_type =
+ typename const_buffers_type::value_type;
+ using pointer = value_type const*;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ const_iterator() = default;
+ const_iterator(const_iterator&& other) = default;
+ const_iterator(const_iterator const& other) = default;
+ const_iterator& operator=(const_iterator&& other) = default;
+ const_iterator& operator=(const_iterator const& other) = default;
+
+ const_iterator(basic_multi_buffer const& b,
+ typename list_type::const_iterator const& it)
+ : b_(&b)
+ , it_(it)
+ {
+ }
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return b_ == other.b_ && it_ == other.it_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ auto const& e = *it_;
+ return value_type{e.data(),
+ (b_->out_ == b_->list_.end() ||
+ &e != &*b_->out_) ? e.size() : b_->out_pos_} +
+ (&e == &*b_->list_.begin() ? b_->in_pos_ : 0);
+ }
+
+ pointer
+ operator->() const = delete;
+
+ const_iterator&
+ operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ const_iterator&
+ operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ const_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ --(*this);
+ return temp;
+ }
+};
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+const_buffers_type::
+const_buffers_type(
+ basic_multi_buffer const& b)
+ : b_(&b)
+{
+}
+
+template<class Allocator>
+auto
+basic_multi_buffer<Allocator>::
+const_buffers_type::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{*b_, b_->list_.begin()};
+}
+
+template<class Allocator>
+auto
+basic_multi_buffer<Allocator>::
+const_buffers_type::
+end() const ->
+ const_iterator
+{
+ return const_iterator{*b_, b_->out_ ==
+ b_->list_.end() ? b_->list_.end() :
+ std::next(b_->out_)};
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator
+{
+ basic_multi_buffer const* b_ = nullptr;
+ typename list_type::const_iterator it_;
+
+public:
+ using value_type =
+ typename mutable_buffers_type::value_type;
+ using pointer = value_type const*;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ const_iterator() = default;
+ const_iterator(const_iterator&& other) = default;
+ const_iterator(const_iterator const& other) = default;
+ const_iterator& operator=(const_iterator&& other) = default;
+ const_iterator& operator=(const_iterator const& other) = default;
+
+ const_iterator(basic_multi_buffer const& b,
+ typename list_type::const_iterator const& it)
+ : b_(&b)
+ , it_(it)
+ {
+ }
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return b_ == other.b_ && it_ == other.it_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ auto const& e = *it_;
+ return value_type{e.data(),
+ &e == &*std::prev(b_->list_.end()) ?
+ b_->out_end_ : e.size()} +
+ (&e == &*b_->out_ ? b_->out_pos_ : 0);
+ }
+
+ pointer
+ operator->() const = delete;
+
+ const_iterator&
+ operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ const_iterator&
+ operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ const_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ --(*this);
+ return temp;
+ }
+};
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+mutable_buffers_type::
+mutable_buffers_type(
+ basic_multi_buffer const& b)
+ : b_(&b)
+{
+}
+
+template<class Allocator>
+auto
+basic_multi_buffer<Allocator>::
+mutable_buffers_type::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{*b_, b_->out_};
+}
+
+template<class Allocator>
+auto
+basic_multi_buffer<Allocator>::
+mutable_buffers_type::
+end() const ->
+ const_iterator
+{
+ return const_iterator{*b_, b_->list_.end()};
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+~basic_multi_buffer()
+{
+ delete_list();
+}
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer()
+ : out_(list_.end())
+{
+}
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(std::size_t limit)
+ : max_(limit)
+ , out_(list_.end())
+{
+}
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(Allocator const& alloc)
+ : detail::empty_base_optimization<
+ base_alloc_type>(alloc)
+ , out_(list_.end())
+{
+}
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(std::size_t limit,
+ Allocator const& alloc)
+ : detail::empty_base_optimization<
+ base_alloc_type>(alloc)
+ , max_(limit)
+ , out_(list_.end())
+{
+}
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(basic_multi_buffer&& other)
+ : detail::empty_base_optimization<
+ base_alloc_type>(std::move(other.member()))
+ , max_(other.max_)
+ , in_size_(other.in_size_)
+ , in_pos_(other.in_pos_)
+ , out_pos_(other.out_pos_)
+ , out_end_(other.out_end_)
+{
+ auto const at_end =
+ other.out_ == other.list_.end();
+ list_ = std::move(other.list_);
+ out_ = at_end ? list_.end() : other.out_;
+ other.in_size_ = 0;
+ other.out_ = other.list_.end();
+ other.in_pos_ = 0;
+ other.out_pos_ = 0;
+ other.out_end_ = 0;
+}
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(basic_multi_buffer&& other,
+ Allocator const& alloc)
+ : detail::empty_base_optimization<
+ base_alloc_type>(alloc)
+ , max_(other.max_)
+{
+ if(this->member() != other.member())
+ {
+ out_ = list_.end();
+ copy_from(other);
+ other.reset();
+ }
+ else
+ {
+ auto const at_end =
+ other.out_ == other.list_.end();
+ list_ = std::move(other.list_);
+ out_ = at_end ? list_.end() : other.out_;
+ in_size_ = other.in_size_;
+ in_pos_ = other.in_pos_;
+ out_pos_ = other.out_pos_;
+ out_end_ = other.out_end_;
+ other.in_size_ = 0;
+ other.out_ = other.list_.end();
+ other.in_pos_ = 0;
+ other.out_pos_ = 0;
+ other.out_end_ = 0;
+ }
+}
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(basic_multi_buffer const& other)
+ : detail::empty_base_optimization<
+ base_alloc_type>(alloc_traits::
+ select_on_container_copy_construction(
+ other.member()))
+ , max_(other.max_)
+ , out_(list_.end())
+{
+ copy_from(other);
+}
+
+template<class Allocator>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(basic_multi_buffer const& other,
+ Allocator const& alloc)
+ : detail::empty_base_optimization<
+ base_alloc_type>(alloc)
+ , max_(other.max_)
+ , out_(list_.end())
+{
+ copy_from(other);
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(
+ basic_multi_buffer<OtherAlloc> const& other)
+ : out_(list_.end())
+{
+ copy_from(other);
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+basic_multi_buffer<Allocator>::
+basic_multi_buffer(
+ basic_multi_buffer<OtherAlloc> const& other,
+ allocator_type const& alloc)
+ : detail::empty_base_optimization<
+ base_alloc_type>(alloc)
+ , max_(other.max_)
+ , out_(list_.end())
+{
+ copy_from(other);
+}
+
+template<class Allocator>
+auto
+basic_multi_buffer<Allocator>::
+operator=(basic_multi_buffer&& other) ->
+ basic_multi_buffer&
+{
+ if(this == &other)
+ return *this;
+ reset();
+ max_ = other.max_;
+ move_assign(other, std::integral_constant<bool,
+ alloc_traits::propagate_on_container_move_assignment::value>{});
+ return *this;
+}
+
+template<class Allocator>
+auto
+basic_multi_buffer<Allocator>::
+operator=(basic_multi_buffer const& other) ->
+basic_multi_buffer&
+{
+ if(this == &other)
+ return *this;
+ copy_assign(other, std::integral_constant<bool,
+ alloc_traits::propagate_on_container_copy_assignment::value>{});
+ return *this;
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+auto
+basic_multi_buffer<Allocator>::
+operator=(
+ basic_multi_buffer<OtherAlloc> const& other) ->
+ basic_multi_buffer&
+{
+ reset();
+ max_ = other.max_;
+ copy_from(other);
+ return *this;
+}
+
+template<class Allocator>
+std::size_t
+basic_multi_buffer<Allocator>::
+capacity() const
+{
+ auto pos = out_;
+ if(pos == list_.end())
+ return in_size_;
+ auto n = pos->size() - out_pos_;
+ while(++pos != list_.end())
+ n += pos->size();
+ return in_size_ + n;
+}
+
+template<class Allocator>
+auto
+basic_multi_buffer<Allocator>::
+data() const ->
+ const_buffers_type
+{
+ return const_buffers_type(*this);
+}
+
+template<class Allocator>
+auto
+basic_multi_buffer<Allocator>::
+prepare(size_type n) ->
+ mutable_buffers_type
+{
+ if(in_size_ + n > max_)
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "dynamic buffer overflow"});
+ list_type reuse;
+ std::size_t total = in_size_;
+ // put all empty buffers on reuse list
+ if(out_ != list_.end())
+ {
+ total += out_->size() - out_pos_;
+ if(out_ != list_.iterator_to(list_.back()))
+ {
+ out_end_ = out_->size();
+ reuse.splice(reuse.end(), list_,
+ std::next(out_), list_.end());
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ }
+ auto const avail = out_->size() - out_pos_;
+ if(n > avail)
+ {
+ out_end_ = out_->size();
+ n -= avail;
+ }
+ else
+ {
+ out_end_ = out_pos_ + n;
+ n = 0;
+ }
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ }
+ // get space from reuse buffers
+ while(n > 0 && ! reuse.empty())
+ {
+ auto& e = reuse.front();
+ reuse.erase(reuse.iterator_to(e));
+ list_.push_back(e);
+ total += e.size();
+ if(n > e.size())
+ {
+ out_end_ = e.size();
+ n -= e.size();
+ }
+ else
+ {
+ out_end_ = n;
+ n = 0;
+ }
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ }
+ BOOST_ASSERT(total <= max_);
+ if(! reuse.empty() || n > 0)
+ {
+ for(auto it = reuse.begin(); it != reuse.end();)
+ {
+ auto& e = *it++;
+ reuse.erase(list_.iterator_to(e));
+ auto const len = sizeof(e) + e.size();
+ alloc_traits::destroy(this->member(), &e);
+ alloc_traits::deallocate(this->member(),
+ reinterpret_cast<char*>(&e), len);
+ }
+ if(n > 0)
+ {
+ static auto const growth_factor = 2.0f;
+ auto const size =
+ (std::min<std::size_t>)(
+ max_ - total,
+ (std::max<std::size_t>)({
+ static_cast<std::size_t>(
+ in_size_ * growth_factor - in_size_),
+ 512,
+ n}));
+ auto& e = *reinterpret_cast<element*>(static_cast<
+ void*>(alloc_traits::allocate(this->member(),
+ sizeof(element) + size)));
+ alloc_traits::construct(this->member(), &e, size);
+ list_.push_back(e);
+ if(out_ == list_.end())
+ out_ = list_.iterator_to(e);
+ out_end_ = n;
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ }
+ }
+ return mutable_buffers_type(*this);
+}
+
+template<class Allocator>
+void
+basic_multi_buffer<Allocator>::
+commit(size_type n)
+{
+ if(list_.empty())
+ return;
+ if(out_ == list_.end())
+ return;
+ auto const back =
+ list_.iterator_to(list_.back());
+ while(out_ != back)
+ {
+ auto const avail =
+ out_->size() - out_pos_;
+ if(n < avail)
+ {
+ out_pos_ += n;
+ in_size_ += n;
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ return;
+ }
+ ++out_;
+ n -= avail;
+ out_pos_ = 0;
+ in_size_ += avail;
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ }
+
+ n = (std::min)(n, out_end_ - out_pos_);
+ out_pos_ += n;
+ in_size_ += n;
+ if(out_pos_ == out_->size())
+ {
+ ++out_;
+ out_pos_ = 0;
+ out_end_ = 0;
+ }
+#if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+#endif
+}
+
+template<class Allocator>
+void
+basic_multi_buffer<Allocator>::
+consume(size_type n)
+{
+ if(list_.empty())
+ return;
+ for(;;)
+ {
+ if(list_.begin() != out_)
+ {
+ auto const avail =
+ list_.front().size() - in_pos_;
+ if(n < avail)
+ {
+ in_size_ -= n;
+ in_pos_ += n;
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ break;
+ }
+ n -= avail;
+ in_size_ -= avail;
+ in_pos_ = 0;
+ auto& e = list_.front();
+ list_.erase(list_.iterator_to(e));
+ auto const len = sizeof(e) + e.size();
+ alloc_traits::destroy(this->member(), &e);
+ alloc_traits::deallocate(this->member(),
+ reinterpret_cast<char*>(&e), len);
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ }
+ else
+ {
+ auto const avail = out_pos_ - in_pos_;
+ if(n < avail)
+ {
+ in_size_ -= n;
+ in_pos_ += n;
+ }
+ else
+ {
+ in_size_ = 0;
+ if(out_ != list_.iterator_to(list_.back()) ||
+ out_pos_ != out_end_)
+ {
+ in_pos_ = out_pos_;
+ }
+ else
+ {
+ // Input and output sequences are empty, reuse buffer.
+ // Alternatively we could deallocate it.
+ in_pos_ = 0;
+ out_pos_ = 0;
+ out_end_ = 0;
+ }
+ }
+ #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
+ debug_check();
+ #endif
+ break;
+ }
+ }
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+delete_list()
+{
+ for(auto iter = list_.begin(); iter != list_.end();)
+ {
+ auto& e = *iter++;
+ auto const len = sizeof(e) + e.size();
+ alloc_traits::destroy(this->member(), &e);
+ alloc_traits::deallocate(this->member(),
+ reinterpret_cast<char*>(&e), len);
+ }
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+reset()
+{
+ delete_list();
+ list_.clear();
+ out_ = list_.end();
+ in_size_ = 0;
+ in_pos_ = 0;
+ out_pos_ = 0;
+ out_end_ = 0;
+}
+
+template<class Allocator>
+template<class DynamicBuffer>
+inline
+void
+basic_multi_buffer<Allocator>::
+copy_from(DynamicBuffer const& buffer)
+{
+ if(buffer.size() == 0)
+ return;
+ using boost::asio::buffer_copy;
+ commit(buffer_copy(
+ prepare(buffer.size()), buffer.data()));
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+move_assign(basic_multi_buffer& other, std::false_type)
+{
+ if(this->member() != other.member())
+ {
+ copy_from(other);
+ other.reset();
+ }
+ else
+ {
+ move_assign(other, std::true_type{});
+ }
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+move_assign(basic_multi_buffer& other, std::true_type)
+{
+ this->member() = std::move(other.member());
+ auto const at_end =
+ other.out_ == other.list_.end();
+ list_ = std::move(other.list_);
+ out_ = at_end ? list_.end() : other.out_;
+
+ in_size_ = other.in_size_;
+ in_pos_ = other.in_pos_;
+ out_pos_ = other.out_pos_;
+ out_end_ = other.out_end_;
+
+ other.in_size_ = 0;
+ other.out_ = other.list_.end();
+ other.in_pos_ = 0;
+ other.out_pos_ = 0;
+ other.out_end_ = 0;
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+copy_assign(
+ basic_multi_buffer const& other, std::false_type)
+{
+ reset();
+ max_ = other.max_;
+ copy_from(other);
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+copy_assign(
+ basic_multi_buffer const& other, std::true_type)
+{
+ reset();
+ max_ = other.max_;
+ this->member() = other.member();
+ copy_from(other);
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+swap(basic_multi_buffer& other)
+{
+ swap(other, typename
+ alloc_traits::propagate_on_container_swap{});
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+swap(basic_multi_buffer& other, std::true_type)
+{
+ using std::swap;
+ auto const at_end0 =
+ out_ == list_.end();
+ auto const at_end1 =
+ other.out_ == other.list_.end();
+ swap(this->member(), other.member());
+ swap(list_, other.list_);
+ swap(out_, other.out_);
+ if(at_end1)
+ out_ = list_.end();
+ if(at_end0)
+ other.out_ = other.list_.end();
+ swap(in_size_, other.in_size_);
+ swap(in_pos_, other.in_pos_);
+ swap(out_pos_, other.out_pos_);
+ swap(out_end_, other.out_end_);
+}
+
+template<class Allocator>
+inline
+void
+basic_multi_buffer<Allocator>::
+swap(basic_multi_buffer& other, std::false_type)
+{
+ BOOST_ASSERT(this->member() == other.member());
+ using std::swap;
+ auto const at_end0 =
+ out_ == list_.end();
+ auto const at_end1 =
+ other.out_ == other.list_.end();
+ swap(list_, other.list_);
+ swap(out_, other.out_);
+ if(at_end1)
+ out_ = list_.end();
+ if(at_end0)
+ other.out_ = other.list_.end();
+ swap(in_size_, other.in_size_);
+ swap(in_pos_, other.in_pos_);
+ swap(out_pos_, other.out_pos_);
+ swap(out_end_, other.out_end_);
+}
+
+template<class Allocator>
+void
+swap(
+ basic_multi_buffer<Allocator>& lhs,
+ basic_multi_buffer<Allocator>& rhs)
+{
+ lhs.swap(rhs);
+}
+
+template<class Allocator>
+void
+basic_multi_buffer<Allocator>::
+debug_check() const
+{
+#ifndef NDEBUG
+ using boost::asio::buffer_size;
+ BOOST_ASSERT(buffer_size(data()) == in_size_);
+ if(list_.empty())
+ {
+ BOOST_ASSERT(in_pos_ == 0);
+ BOOST_ASSERT(in_size_ == 0);
+ BOOST_ASSERT(out_pos_ == 0);
+ BOOST_ASSERT(out_end_ == 0);
+ BOOST_ASSERT(out_ == list_.end());
+ return;
+ }
+
+ auto const& front = list_.front();
+
+ BOOST_ASSERT(in_pos_ < front.size());
+
+ if(out_ == list_.end())
+ {
+ BOOST_ASSERT(out_pos_ == 0);
+ BOOST_ASSERT(out_end_ == 0);
+ }
+ else
+ {
+ auto const& out = *out_;
+ auto const& back = list_.back();
+
+ BOOST_ASSERT(out_end_ <= back.size());
+ BOOST_ASSERT(out_pos_ < out.size());
+ BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
+ BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
+ BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);
+ }
+#endif
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/read_size.ipp b/boost/beast/core/impl/read_size.ipp
new file mode 100644
index 0000000000..ddcaf397a9
--- /dev/null
+++ b/boost/beast/core/impl/read_size.ipp
@@ -0,0 +1,80 @@
+//
+// 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_IMPL_READ_SIZE_IPP
+#define BOOST_BEAST_IMPL_READ_SIZE_IPP
+
+namespace boost {
+namespace beast {
+
+namespace detail {
+
+template<class T, class = void>
+struct has_read_size_helper : std::false_type {};
+
+template<class T>
+struct has_read_size_helper<T, decltype(
+ read_size_helper(std::declval<T&>(), 512),
+ (void)0)> : std::true_type
+{
+};
+
+template<class DynamicBuffer>
+std::size_t
+read_size(DynamicBuffer& buffer,
+ std::size_t max_size, std::true_type)
+{
+ return read_size_helper(buffer, max_size);
+}
+
+template<class DynamicBuffer>
+std::size_t
+read_size(DynamicBuffer& buffer,
+ std::size_t max_size, std::false_type)
+{
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ BOOST_ASSERT(max_size >= 1);
+ auto const size = buffer.size();
+ auto const limit = buffer.max_size() - size;
+ BOOST_ASSERT(size <= buffer.max_size());
+ return (std::min<std::size_t>)(
+ (std::max<std::size_t>)(512, buffer.capacity() - size),
+ (std::min<std::size_t>)(max_size, limit));
+}
+
+} // detail
+
+template<class DynamicBuffer>
+inline
+std::size_t
+read_size(
+ DynamicBuffer& buffer, std::size_t max_size)
+{
+ return detail::read_size(buffer, max_size,
+ detail::has_read_size_helper<DynamicBuffer>{});
+}
+
+template<class DynamicBuffer>
+std::size_t
+read_size_or_throw(
+ DynamicBuffer& buffer, std::size_t max_size)
+{
+ auto const n = read_size(buffer, max_size);
+ if(n == 0)
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "buffer overflow"});
+ return n;
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/static_buffer.ipp b/boost/beast/core/impl/static_buffer.ipp
new file mode 100644
index 0000000000..d93a1e6911
--- /dev/null
+++ b/boost/beast/core/impl/static_buffer.ipp
@@ -0,0 +1,148 @@
+//
+// 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_IMPL_STATIC_BUFFER_IPP
+#define BOOST_BEAST_IMPL_STATIC_BUFFER_IPP
+
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <cstring>
+#include <iterator>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+
+inline
+static_buffer_base::
+static_buffer_base(void* p, std::size_t size)
+ : begin_(reinterpret_cast<char*>(p))
+ , capacity_(size)
+{
+}
+
+inline
+auto
+static_buffer_base::
+data() const ->
+ const_buffers_type
+{
+ using boost::asio::mutable_buffer;
+ const_buffers_type result;
+ if(in_off_ + in_size_ <= capacity_)
+ {
+ result[0] = mutable_buffer{begin_ + in_off_, in_size_};
+ result[1] = mutable_buffer{begin_, 0};
+ }
+ else
+ {
+ result[0] = mutable_buffer{begin_ + in_off_, capacity_ - in_off_};
+ result[1] = mutable_buffer{begin_, in_size_ - (capacity_ - in_off_)};
+ }
+ return result;
+}
+
+inline
+auto
+static_buffer_base::
+prepare(std::size_t size) ->
+ mutable_buffers_type
+{
+ using boost::asio::mutable_buffer;
+ if(size > capacity_ - in_size_)
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "buffer overflow"});
+ out_size_ = size;
+ auto const out_off = (in_off_ + in_size_) % capacity_;
+ mutable_buffers_type result;
+ if(out_off + out_size_ <= capacity_ )
+ {
+ result[0] = mutable_buffer{begin_ + out_off, out_size_};
+ result[1] = mutable_buffer{begin_, 0};
+ }
+ else
+ {
+ result[0] = mutable_buffer{begin_ + out_off, capacity_ - out_off};
+ result[1] = mutable_buffer{begin_, out_size_ - (capacity_ - out_off)};
+ }
+ return result;
+}
+
+inline
+void
+static_buffer_base::
+commit(std::size_t size)
+{
+ in_size_ += (std::min)(size, out_size_);
+ out_size_ = 0;
+}
+
+inline
+void
+static_buffer_base::
+consume(std::size_t size)
+{
+ if(size < in_size_)
+ {
+ in_off_ = (in_off_ + size) % capacity_;
+ in_size_ -= size;
+ }
+ else
+ {
+ // rewind the offset, so the next call to prepare
+ // can have a longer continguous segment. this helps
+ // algorithms optimized for larger buffesr.
+ in_off_ = 0;
+ in_size_ = 0;
+ }
+}
+
+inline
+void
+static_buffer_base::
+reset(void* p, std::size_t size)
+{
+ begin_ = reinterpret_cast<char*>(p);
+ capacity_ = size;
+ in_off_ = 0;
+ in_size_ = 0;
+ out_size_ = 0;
+}
+
+//------------------------------------------------------------------------------
+
+template<std::size_t N>
+static_buffer<N>::
+static_buffer(static_buffer const& other)
+ : static_buffer_base(buf_, N)
+{
+ using boost::asio::buffer_copy;
+ this->commit(buffer_copy(
+ this->prepare(other.size()), other.data()));
+}
+
+template<std::size_t N>
+auto
+static_buffer<N>::
+operator=(static_buffer const& other) ->
+ static_buffer<N>&
+{
+ using boost::asio::buffer_copy;
+ this->consume(this->size());
+ this->commit(buffer_copy(
+ this->prepare(other.size()), other.data()));
+ return *this;
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/static_string.ipp b/boost/beast/core/impl/static_string.ipp
new file mode 100644
index 0000000000..29571dfeee
--- /dev/null
+++ b/boost/beast/core/impl/static_string.ipp
@@ -0,0 +1,618 @@
+//
+// 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_IMPL_STATIC_STRING_IPP
+#define BOOST_BEAST_IMPL_STATIC_STRING_IPP
+
+#include <boost/beast/core/detail/static_string.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/throw_exception.hpp>
+
+namespace boost {
+namespace beast {
+
+//
+// (constructor)
+//
+
+template<std::size_t N, class CharT, class Traits>
+static_string<N, CharT, Traits>::
+static_string()
+{
+ n_ = 0;
+ term();
+}
+
+template<std::size_t N, class CharT, class Traits>
+static_string<N, CharT, Traits>::
+static_string(size_type count, CharT ch)
+{
+ assign(count, ch);
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<std::size_t M>
+static_string<N, CharT, Traits>::
+static_string(static_string<M, CharT, Traits> const& other,
+ size_type pos)
+{
+ assign(other, pos);
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<std::size_t M>
+static_string<N, CharT, Traits>::
+static_string(static_string<M, CharT, Traits> const& other,
+ size_type pos, size_type count)
+{
+ assign(other, pos, count);
+}
+
+template<std::size_t N, class CharT, class Traits>
+static_string<N, CharT, Traits>::
+static_string(CharT const* s, size_type count)
+{
+ assign(s, count);
+}
+
+template<std::size_t N, class CharT, class Traits>
+static_string<N, CharT, Traits>::
+static_string(CharT const* s)
+{
+ assign(s);
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<class InputIt>
+static_string<N, CharT, Traits>::
+static_string(InputIt first, InputIt last)
+{
+ assign(first, last);
+}
+
+template<std::size_t N, class CharT, class Traits>
+static_string<N, CharT, Traits>::
+static_string(static_string const& s)
+{
+ assign(s);
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<std::size_t M>
+static_string<N, CharT, Traits>::
+static_string(static_string<M, CharT, Traits> const& s)
+{
+ assign(s);
+}
+
+template<std::size_t N, class CharT, class Traits>
+static_string<N, CharT, Traits>::
+static_string(std::initializer_list<CharT> init)
+{
+ assign(init.begin(), init.end());
+}
+
+template<std::size_t N, class CharT, class Traits>
+static_string<N, CharT, Traits>::
+static_string(string_view_type sv)
+{
+ assign(sv);
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<class T, class>
+static_string<N, CharT, Traits>::
+static_string(T const& t, size_type pos, size_type n)
+{
+ assign(t, pos, n);
+}
+
+//
+// (assignment)
+//
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+assign(size_type count, CharT ch) ->
+ static_string&
+{
+ if(count > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "count > max_size()"});
+ n_ = count;
+ Traits::assign(&s_[0], n_, ch);
+ term();
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+assign(static_string const& str) ->
+ static_string&
+{
+ n_ = str.n_;
+ Traits::copy(&s_[0], &str.s_[0], n_ + 1);
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<std::size_t M>
+auto
+static_string<N, CharT, Traits>::
+assign(static_string<M, CharT, Traits> const& str,
+ size_type pos, size_type count) ->
+ static_string&
+{
+ auto const ss = str.substr(pos, count);
+ return assign(ss.data(), ss.size());
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+assign(CharT const* s, size_type count) ->
+ static_string&
+{
+ if(count > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "count > max_size()"});
+ n_ = count;
+ Traits::copy(&s_[0], s, n_);
+ term();
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<class InputIt>
+auto
+static_string<N, CharT, Traits>::
+assign(InputIt first, InputIt last) ->
+ static_string&
+{
+ std::size_t const n = std::distance(first, last);
+ if(n > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "n > max_size()"});
+ n_ = n;
+ for(auto it = &s_[0]; first != last; ++it, ++first)
+ Traits::assign(*it, *first);
+ term();
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<class T>
+auto
+static_string<N, CharT, Traits>::
+assign(T const& t, size_type pos, size_type count) ->
+ typename std::enable_if<std::is_convertible<T,
+ string_view_type>::value, static_string&>::type
+{
+ auto const sv = string_view_type(t).substr(pos, count);
+ if(sv.size() > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "sv.size() > max_size()"});
+ n_ = sv.size();
+ Traits::copy(&s_[0], &sv[0], n_);
+ term();
+ return *this;
+}
+
+//
+// Element access
+//
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+at(size_type pos) ->
+ reference
+{
+ if(pos >= size())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "pos >= size()"});
+ return s_[pos];
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+at(size_type pos) const ->
+ const_reference
+{
+ if(pos >= size())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "pos >= size()"});
+ return s_[pos];
+}
+
+//
+// Capacity
+//
+
+template<std::size_t N, class CharT, class Traits>
+void
+static_string<N, CharT, Traits>::
+reserve(std::size_t n)
+{
+ if(n > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "n > max_size()"});
+}
+
+//
+// Operations
+//
+
+template<std::size_t N, class CharT, class Traits>
+void
+static_string<N, CharT, Traits>::
+clear()
+{
+ n_ = 0;
+ term();
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+insert(size_type index, size_type count, CharT ch) ->
+ static_string&
+{
+ if(index > size())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "index > size()"});
+ insert(begin() + index, count, ch);
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+insert(size_type index, CharT const* s, size_type count) ->
+ static_string&
+{
+ if(index > size())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "index > size()"});
+ if(size() + count > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "size() + count > max_size()"});
+ Traits::move(
+ &s_[index + count], &s_[index], size() - index);
+ n_ += count;
+ Traits::copy(&s_[index], s, count);
+ term();
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<std::size_t M>
+auto
+static_string<N, CharT, Traits>::
+insert(size_type index,
+ static_string<M, CharT, Traits> const& str,
+ size_type index_str, size_type count) ->
+ static_string&
+{
+ auto const ss = str.substr(index_str, count);
+ return insert(index, ss.data(), ss.size());
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+insert(const_iterator pos, size_type count, CharT ch) ->
+ iterator
+{
+ if(size() + count > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "size() + count() > max_size()"});
+ auto const index = pos - &s_[0];
+ Traits::move(
+ &s_[index + count], &s_[index], size() - index);
+ n_ += count;
+ Traits::assign(&s_[index], count, ch);
+ term();
+ return &s_[index];
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<class InputIt>
+auto
+static_string<N, CharT, Traits>::
+insert(const_iterator pos, InputIt first, InputIt last) ->
+ typename std::enable_if<
+ detail::is_input_iterator<InputIt>::value,
+ iterator>::type
+{
+ std::size_t const count = std::distance(first, last);
+ if(size() + count > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "size() + count > max_size()"});
+ std::size_t const index = pos - begin();
+ Traits::move(
+ &s_[index + count], &s_[index], size() - index);
+ n_ += count;
+ for(auto it = begin() + index;
+ first != last; ++it, ++first)
+ Traits::assign(*it, *first);
+ term();
+ return begin() + index;
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<class T>
+auto
+static_string<N, CharT, Traits>::
+insert(size_type index, const T& t,
+ size_type index_str, size_type count) ->
+ typename std::enable_if<std::is_convertible<
+ T const&, string_view_type>::value &&
+ ! std::is_convertible<T const&, CharT const*>::value,
+ static_string&>::type
+{
+ auto const str =
+ string_view_type(t).substr(index_str, count);
+ return insert(index, str.data(), str.size());
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+erase(size_type index, size_type count) ->
+ static_string&
+{
+ if(index > size())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "index > size()"});
+ auto const n = (std::min)(count, size() - index);
+ Traits::move(
+ &s_[index], &s_[index + n], size() - (index + n) + 1);
+ n_ -= n;
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+erase(const_iterator pos) ->
+ iterator
+{
+ erase(pos - begin(), 1);
+ return begin() + (pos - begin());
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+erase(const_iterator first, const_iterator last) ->
+ iterator
+{
+ erase(first - begin(),
+ std::distance(first, last));
+ return begin() + (first - begin());
+}
+
+template<std::size_t N, class CharT, class Traits>
+void
+static_string<N, CharT, Traits>::
+push_back(CharT ch)
+{
+ if(size() >= max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "size() >= max_size()"});
+ Traits::assign(s_[n_++], ch);
+ term();
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<std::size_t M>
+auto
+static_string<N, CharT, Traits>::
+append(static_string<M, CharT, Traits> const& str,
+ size_type pos, size_type count) ->
+ static_string&
+{
+ // Valid range is [0, size)
+ if(pos >= str.size())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "pos > str.size()"});
+ string_view_type const ss{&str.s_[pos],
+ (std::min)(count, str.size() - pos)};
+ insert(size(), ss.data(), ss.size());
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+substr(size_type pos, size_type count) const ->
+ string_view_type
+{
+ if(pos > size())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "pos > size()"});
+ return{&s_[pos], (std::min)(count, size() - pos)};
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+copy(CharT* dest, size_type count, size_type pos) const ->
+ size_type
+{
+ auto const str = substr(pos, count);
+ Traits::copy(dest, str.data(), str.size());
+ return str.size();
+}
+
+template<std::size_t N, class CharT, class Traits>
+void
+static_string<N, CharT, Traits>::
+resize(std::size_t n)
+{
+ if(n > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "n > max_size()"});
+ n_ = n;
+ term();
+}
+
+template<std::size_t N, class CharT, class Traits>
+void
+static_string<N, CharT, Traits>::
+resize(std::size_t n, CharT c)
+{
+ if(n > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "n > max_size()"});
+ if(n > n_)
+ Traits::assign(&s_[n_], n - n_, c);
+ n_ = n;
+ term();
+}
+
+template<std::size_t N, class CharT, class Traits>
+void
+static_string<N, CharT, Traits>::
+swap(static_string& str)
+{
+ static_string tmp(str);
+ str.n_ = n_;
+ Traits::copy(&str.s_[0], &s_[0], n_ + 1);
+ n_ = tmp.n_;
+ Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
+}
+
+template<std::size_t N, class CharT, class Traits>
+template<std::size_t M>
+void
+static_string<N, CharT, Traits>::
+swap(static_string<M, CharT, Traits>& str)
+{
+ if(size() > str.max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "size() > str.max_size()"});
+ if(str.size() > max_size())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "str.size() > max_size()"});
+ static_string tmp(str);
+ str.n_ = n_;
+ Traits::copy(&str.s_[0], &s_[0], n_ + 1);
+ n_ = tmp.n_;
+ Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
+}
+
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+assign_char(CharT ch, std::true_type) ->
+ static_string&
+{
+ n_ = 1;
+ Traits::assign(s_[0], ch);
+ term();
+ return *this;
+}
+
+template<std::size_t N, class CharT, class Traits>
+auto
+static_string<N, CharT, Traits>::
+assign_char(CharT, std::false_type) ->
+ static_string&
+{
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "max_size() == 0"});
+}
+
+namespace detail {
+
+template<class Integer>
+static_string<max_digits(sizeof(Integer))>
+to_static_string(Integer x, std::true_type)
+{
+ if(x == 0)
+ return {'0'};
+ static_string<detail::max_digits(
+ sizeof(Integer))> s;
+ if(x < 0)
+ {
+ x = -x;
+ char buf[max_digits(sizeof(x))];
+ char* p = buf;
+ for(;x > 0; x /= 10)
+ *p++ = "0123456789"[x % 10];
+ s.resize(1 + p - buf);
+ s[0] = '-';
+ auto d = &s[1];
+ while(p > buf)
+ *d++ = *--p;
+ }
+ else
+ {
+ char buf[max_digits(sizeof(x))];
+ char* p = buf;
+ for(;x > 0; x /= 10)
+ *p++ = "0123456789"[x % 10];
+ s.resize(p - buf);
+ auto d = &s[0];
+ while(p > buf)
+ *d++ = *--p;
+ }
+ return s;
+}
+
+template<class Integer>
+static_string<max_digits(sizeof(Integer))>
+to_static_string(Integer x, std::false_type)
+{
+ if(x == 0)
+ return {'0'};
+ char buf[max_digits(sizeof(x))];
+ char* p = buf;
+ for(;x > 0; x /= 10)
+ *p++ = "0123456789"[x % 10];
+ static_string<detail::max_digits(
+ sizeof(Integer))> s;
+ s.resize(p - buf);
+ auto d = &s[0];
+ while(p > buf)
+ *d++ = *--p;
+ return s;
+}
+
+} // detail
+
+template<class Integer>
+static_string<detail::max_digits(sizeof(Integer))>
+to_static_string(Integer x)
+{
+ using CharT = char;
+ using Traits = std::char_traits<CharT>;
+ BOOST_STATIC_ASSERT(std::is_integral<Integer>::value);
+ char buf[detail::max_digits(sizeof(Integer))];
+ auto last = buf + sizeof(buf);
+ auto it = detail::raw_to_string<
+ CharT, Integer, Traits>(last, sizeof(buf), x);
+ static_string<detail::max_digits(sizeof(Integer))> s;
+ s.resize(static_cast<std::size_t>(last - it));
+ auto p = s.data();
+ while(it < last)
+ Traits::assign(*p++, *it++);
+ return s;
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/impl/string_param.ipp b/boost/beast/core/impl/string_param.ipp
new file mode 100644
index 0000000000..a60771cc12
--- /dev/null
+++ b/boost/beast/core/impl/string_param.ipp
@@ -0,0 +1,108 @@
+//
+// 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_IMPL_STRING_PARAM_IPP
+#define BOOST_BEAST_IMPL_STRING_PARAM_IPP
+
+namespace boost {
+namespace beast {
+
+template<class T>
+typename std::enable_if<
+ std::is_integral<T>::value>::type
+string_param::
+print(T const& t)
+{
+ auto const last = buf_ + sizeof(buf_);
+ auto const it = detail::raw_to_string<
+ char, T, std::char_traits<char>>(
+ last, sizeof(buf_), t);
+ sv_ = {it, static_cast<std::size_t>(
+ last - it)};
+}
+
+template<class T>
+typename std::enable_if<
+ ! std::is_integral<T>::value &&
+ ! std::is_convertible<T, string_view>::value
+>::type
+string_param::
+print(T const& t)
+{
+ os_.emplace(buf_, sizeof(buf_));
+ *os_ << t;
+ os_->flush();
+ sv_ = os_->str();
+}
+
+inline
+void
+string_param::
+print(string_view sv)
+{
+ sv_ = sv;
+}
+
+template<class T>
+typename std::enable_if<
+ std::is_integral<T>::value>::type
+string_param::
+print_1(T const& t)
+{
+ char buf[detail::max_digits(sizeof(T))];
+ auto const last = buf + sizeof(buf);
+ auto const it = detail::raw_to_string<
+ char, T, std::char_traits<char>>(
+ last, sizeof(buf), t);
+ *os_ << string_view{it,
+ static_cast<std::size_t>(last - it)};
+}
+
+template<class T>
+typename std::enable_if<
+ ! std::is_integral<T>::value>::type
+string_param::
+print_1(T const& t)
+{
+ *os_ << t;
+}
+
+template<class T0, class... TN>
+void
+string_param::
+print_n(T0 const& t0, TN const&... tn)
+{
+ print_1(t0);
+ print_n(tn...);
+}
+
+template<class T0, class T1, class... TN>
+void
+string_param::
+print(T0 const& t0, T1 const& t1, TN const&... tn)
+{
+ os_.emplace(buf_, sizeof(buf_));
+ print_1(t0);
+ print_1(t1);
+ print_n(tn...);
+ os_->flush();
+ sv_ = os_->str();
+}
+
+template<class... Args>
+string_param::
+string_param(Args const&... args)
+{
+ print(args...);
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/multi_buffer.hpp b/boost/beast/core/multi_buffer.hpp
new file mode 100644
index 0000000000..ee14b419a5
--- /dev/null
+++ b/boost/beast/core/multi_buffer.hpp
@@ -0,0 +1,322 @@
+//
+// 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_MULTI_BUFFER_HPP
+#define BOOST_BEAST_MULTI_BUFFER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/detail/allocator.hpp>
+#include <boost/beast/core/detail/empty_base_optimization.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/intrusive/list.hpp>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+
+/** A @b DynamicBuffer that uses multiple buffers internally.
+
+ The implementation uses a sequence of one or more character arrays
+ of varying sizes. Additional character array objects are appended to
+ the sequence to accommodate changes in the size of the character
+ sequence.
+
+ @note Meets the requirements of @b DynamicBuffer.
+
+ @tparam Allocator The allocator to use for managing memory.
+*/
+template<class Allocator>
+class basic_multi_buffer
+#if ! BOOST_BEAST_DOXYGEN
+ : private detail::empty_base_optimization<
+ typename detail::allocator_traits<Allocator>::
+ template rebind_alloc<char>>
+#endif
+{
+ using base_alloc_type = typename
+ detail::allocator_traits<Allocator>::
+ template rebind_alloc<char>;
+
+ // Storage for the list of buffers representing the input
+ // and output sequences. The allocation for each element
+ // contains `element` followed by raw storage bytes.
+ class element;
+
+ using alloc_traits = detail::allocator_traits<base_alloc_type>;
+ using list_type = typename boost::intrusive::make_list<element,
+ boost::intrusive::constant_time_size<true>>::type;
+ using iter = typename list_type::iterator;
+ using const_iter = typename list_type::const_iterator;
+
+ using size_type = typename alloc_traits::size_type;
+ using const_buffer = boost::asio::const_buffer;
+ using mutable_buffer = boost::asio::mutable_buffer;
+
+ static_assert(std::is_base_of<std::bidirectional_iterator_tag,
+ typename std::iterator_traits<iter>::iterator_category>::value,
+ "BidirectionalIterator requirements not met");
+
+ static_assert(std::is_base_of<std::bidirectional_iterator_tag,
+ typename std::iterator_traits<const_iter>::iterator_category>::value,
+ "BidirectionalIterator requirements not met");
+
+ std::size_t max_ =
+ (std::numeric_limits<std::size_t>::max)();
+ list_type list_; // list of allocated buffers
+ iter out_; // element that contains out_pos_
+ size_type in_size_ = 0; // size of the input sequence
+ size_type in_pos_ = 0; // input offset in list_.front()
+ size_type out_pos_ = 0; // output offset in *out_
+ size_type out_end_ = 0; // output end offset in list_.back()
+
+public:
+ /// The type of allocator used.
+ using allocator_type = Allocator;
+
+#if BOOST_BEAST_DOXYGEN
+ /// The type used to represent the input sequence as a list of buffers.
+ using const_buffers_type = implementation_defined;
+
+ /// The type used to represent the output sequence as a list of buffers.
+ using mutable_buffers_type = implementation_defined;
+
+#else
+ class const_buffers_type;
+
+ class mutable_buffers_type;
+
+#endif
+
+ /// Destructor
+ ~basic_multi_buffer();
+
+ /** Constructor
+
+ Upon construction, capacity will be zero.
+ */
+ basic_multi_buffer();
+
+ /** Constructor.
+
+ @param limit The setting for @ref max_size.
+ */
+ explicit
+ basic_multi_buffer(std::size_t limit);
+
+ /** Constructor.
+
+ @param alloc The allocator to use.
+ */
+ explicit
+ basic_multi_buffer(Allocator const& alloc);
+
+ /** Constructor.
+
+ @param limit The setting for @ref max_size.
+
+ @param alloc The allocator to use.
+ */
+ basic_multi_buffer(
+ std::size_t limit, Allocator const& alloc);
+
+ /** Move constructor
+
+ After the move, `*this` will have an empty output sequence.
+
+ @param other The object to move from. After the move,
+ The object's state will be as if constructed using
+ its current allocator and limit.
+ */
+ basic_multi_buffer(basic_multi_buffer&& other);
+
+ /** Move constructor
+
+ After the move, `*this` will have an empty output sequence.
+
+ @param other The object to move from. After the move,
+ The object's state will be as if constructed using
+ its current allocator and limit.
+
+ @param alloc The allocator to use.
+ */
+ basic_multi_buffer(basic_multi_buffer&& other,
+ Allocator const& alloc);
+
+ /** Copy constructor.
+
+ @param other The object to copy from.
+ */
+ basic_multi_buffer(basic_multi_buffer const& other);
+
+ /** Copy constructor
+
+ @param other The object to copy from.
+
+ @param alloc The allocator to use.
+ */
+ basic_multi_buffer(basic_multi_buffer const& other,
+ Allocator const& alloc);
+
+ /** Copy constructor.
+
+ @param other The object to copy from.
+ */
+ template<class OtherAlloc>
+ basic_multi_buffer(basic_multi_buffer<
+ OtherAlloc> const& other);
+
+ /** Copy constructor.
+
+ @param other The object to copy from.
+
+ @param alloc The allocator to use.
+ */
+ template<class OtherAlloc>
+ basic_multi_buffer(basic_multi_buffer<
+ OtherAlloc> const& other, allocator_type const& alloc);
+
+ /** Move assignment
+
+ After the move, `*this` will have an empty output sequence.
+
+ @param other The object to move from. After the move,
+ The object's state will be as if constructed using
+ its current allocator and limit.
+ */
+ basic_multi_buffer&
+ operator=(basic_multi_buffer&& other);
+
+ /** Copy assignment
+
+ After the copy, `*this` will have an empty output sequence.
+
+ @param other The object to copy from.
+ */
+ basic_multi_buffer& operator=(basic_multi_buffer const& other);
+
+ /** Copy assignment
+
+ After the copy, `*this` will have an empty output sequence.
+
+ @param other The object to copy from.
+ */
+ template<class OtherAlloc>
+ basic_multi_buffer& operator=(
+ basic_multi_buffer<OtherAlloc> const& other);
+
+ /// Returns a copy of the associated allocator.
+ allocator_type
+ get_allocator() const
+ {
+ return this->member();
+ }
+
+ /// Returns the size of the input sequence.
+ size_type
+ size() const
+ {
+ return in_size_;
+ }
+
+ /// Returns the permitted maximum sum of the sizes of the input and output sequence.
+ size_type
+ max_size() const
+ {
+ return max_;
+ }
+
+ /// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
+ std::size_t
+ capacity() const;
+
+ /** Get a list of buffers that represents the input sequence.
+
+ @note These buffers remain valid across subsequent calls to `prepare`.
+ */
+ const_buffers_type
+ data() const;
+
+ /** Get a list of buffers that represents the output sequence, with the given size.
+
+ @note Buffers representing the input sequence acquired prior to
+ this call remain valid.
+ */
+ mutable_buffers_type
+ prepare(size_type n);
+
+ /** Move bytes from the output sequence to the input sequence.
+
+ @note Buffers representing the input sequence acquired prior to
+ this call remain valid.
+ */
+ void
+ commit(size_type n);
+
+ /// Remove bytes from the input sequence.
+ void
+ consume(size_type n);
+
+ template<class Alloc>
+ friend
+ void
+ swap(
+ basic_multi_buffer<Alloc>& lhs,
+ basic_multi_buffer<Alloc>& rhs);
+
+private:
+ template<class OtherAlloc>
+ friend class basic_multi_buffer;
+
+ void
+ delete_list();
+
+ void
+ reset();
+
+ template<class DynamicBuffer>
+ void
+ copy_from(DynamicBuffer const& other);
+
+ void
+ move_assign(basic_multi_buffer& other, std::false_type);
+
+ void
+ move_assign(basic_multi_buffer& other, std::true_type);
+
+ void
+ copy_assign(basic_multi_buffer const& other, std::false_type);
+
+ void
+ copy_assign(basic_multi_buffer const& other, std::true_type);
+
+ void
+ swap(basic_multi_buffer&);
+
+ void
+ swap(basic_multi_buffer&, std::true_type);
+
+ void
+ swap(basic_multi_buffer&, std::false_type);
+
+ void
+ debug_check() const;
+};
+
+/// A typical multi buffer
+using multi_buffer = basic_multi_buffer<std::allocator<char>>;
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/multi_buffer.ipp>
+
+#endif
diff --git a/boost/beast/core/ostream.hpp b/boost/beast/core/ostream.hpp
new file mode 100644
index 0000000000..1f00289bd1
--- /dev/null
+++ b/boost/beast/core/ostream.hpp
@@ -0,0 +1,104 @@
+//
+// 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_WRITE_OSTREAM_HPP
+#define BOOST_BEAST_WRITE_OSTREAM_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/ostream.hpp>
+#include <type_traits>
+#include <streambuf>
+#include <utility>
+
+namespace boost {
+namespace beast {
+
+/** Return an object representing a @b ConstBufferSequence.
+
+ This function wraps a reference to a buffer sequence and permits
+ the following operation:
+
+ @li `operator<<` to `std::ostream`. No character translation is
+ performed; unprintable and null characters will be transferred
+ as-is to the output stream.
+
+ @par Example
+ @code
+ multi_buffer b;
+ ...
+ std::cout << buffers(b.data()) << std::endl;
+ @endcode
+
+ @param b An object meeting the requirements of @b ConstBufferSequence
+ to be streamed. The implementation will make a copy of this object.
+ Ownership of the underlying memory is not transferred, the application
+ is still responsible for managing its lifetime.
+*/
+template<class ConstBufferSequence>
+#if BOOST_BEAST_DOXYGEN
+implementation_defined
+#else
+detail::buffers_helper<ConstBufferSequence>
+#endif
+buffers(ConstBufferSequence const& b)
+{
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ return detail::buffers_helper<
+ ConstBufferSequence>{b};
+}
+
+/** Return an output stream that formats values into a @b DynamicBuffer.
+
+ This function wraps the caller provided @b DynamicBuffer into
+ a `std::ostream` derived class, to allow `operator<<` stream style
+ formatting operations.
+
+ @par Example
+ @code
+ ostream(buffer) << "Hello, world!" << std::endl;
+ @endcode
+
+ @note Calling members of the underlying buffer before the output
+ stream is destroyed results in undefined behavior.
+
+ @param buffer An object meeting the requirements of @b DynamicBuffer
+ into which the formatted output will be placed.
+
+ @return An object derived from `std::ostream` which redirects output
+ The wrapped dynamic buffer is not modified, a copy is made instead.
+ Ownership of the underlying memory is not transferred, the application
+ is still responsible for managing its lifetime. The caller is
+ responsible for ensuring the dynamic buffer is not destroyed for the
+ lifetime of the output stream.
+*/
+template<class DynamicBuffer>
+#if BOOST_BEAST_DOXYGEN
+implementation_defined
+#else
+detail::ostream_helper<
+ DynamicBuffer, char, std::char_traits<char>,
+ detail::basic_streambuf_movable::value>
+#endif
+ostream(DynamicBuffer& buffer)
+{
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ return detail::ostream_helper<
+ DynamicBuffer, char, std::char_traits<char>,
+ detail::basic_streambuf_movable::value>{buffer};
+}
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/read_size.hpp b/boost/beast/core/read_size.hpp
new file mode 100644
index 0000000000..d9aaaa61ab
--- /dev/null
+++ b/boost/beast/core/read_size.hpp
@@ -0,0 +1,64 @@
+//
+// 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_READ_SIZE_HELPER_HPP
+#define BOOST_BEAST_READ_SIZE_HELPER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/throw_exception.hpp>
+
+namespace boost {
+namespace beast {
+
+/** Returns a natural read size.
+
+ This function inspects the capacity, size, and maximum
+ size of the dynamic buffer. Then it computes a natural
+ read size given the passed-in upper limit. It favors
+ a read size that does not require a reallocation, subject
+ to a reasonable minimum to avoid tiny reads.
+
+ @param buffer The dynamic buffer to inspect.
+
+ @param max_size An upper limit on the returned value.
+
+ @note If the buffer is already at its maximum size, zero
+ is returned.
+*/
+template<class DynamicBuffer>
+std::size_t
+read_size(DynamicBuffer& buffer, std::size_t max_size);
+
+/** Returns a natural read size or throw if the buffer is full.
+
+ This function inspects the capacity, size, and maximum
+ size of the dynamic buffer. Then it computes a natural
+ read size given the passed-in upper limit. It favors
+ a read size that does not require a reallocation, subject
+ to a reasonable minimum to avoid tiny reads.
+
+ @param buffer The dynamic buffer to inspect.
+
+ @param max_size An upper limit on the returned value.
+
+ @throws std::length_error if `max_size > 0` and the buffer
+ is full.
+*/
+template<class DynamicBuffer>
+std::size_t
+read_size_or_throw(DynamicBuffer& buffer,
+ std::size_t max_size);
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/read_size.ipp>
+
+#endif
diff --git a/boost/beast/core/span.hpp b/boost/beast/core/span.hpp
new file mode 100644
index 0000000000..4aa7788501
--- /dev/null
+++ b/boost/beast/core/span.hpp
@@ -0,0 +1,215 @@
+//
+// 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_CORE_SPAN_HPP
+#define BOOST_BEAST_CORE_SPAN_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+
+/** A range of bytes expressed as a ContiguousContainer
+
+ This class implements a non-owning reference to a storage
+ area of a certain size and having an underlying integral
+ type with size of 1.
+
+ @tparam T The type pointed to by span iterators
+*/
+template<class T>
+class span
+{
+ T* data_ = nullptr;
+ std::size_t size_ = 0;
+
+public:
+ /// The type of value, including cv qualifiers
+ using element_type = T;
+
+ /// The type of value of each span element
+ using value_type = typename std::remove_const<T>::type;
+
+ /// The type of integer used to index the span
+ using index_type = std::ptrdiff_t;
+
+ /// A pointer to a span element
+ using pointer = T*;
+
+ /// A reference to a span element
+ using reference = T&;
+
+ /// The iterator used by the container
+ using iterator = pointer;
+
+ /// The const pointer used by the container
+ using const_pointer = T const*;
+
+ /// The const reference used by the container
+ using const_reference = T const&;
+
+ /// The const iterator used by the container
+ using const_iterator = const_pointer;
+
+ /// Constructor
+ span() = default;
+
+ /// Constructor
+ span(span const&) = default;
+
+ /// Assignment
+ span& operator=(span const&) = default;
+
+ /** Constructor
+
+ @param data A pointer to the beginning of the range of elements
+
+ @param size The number of elements pointed to by `data`
+ */
+ span(T* data, std::size_t size)
+ : data_(data), size_(size)
+ {
+ }
+
+ /** Constructor
+
+ @param container The container to construct from
+ */
+ template<class ContiguousContainer
+#if ! BOOST_BEAST_DOXYGEN
+ , class = typename std::enable_if<
+ detail::is_contiguous_container<
+ ContiguousContainer, T>::value>::type
+#endif
+ >
+ explicit
+ span(ContiguousContainer&& container)
+ : data_(container.data())
+ , size_(container.size())
+ {
+ }
+
+#if ! BOOST_BEAST_DOXYGEN
+ template<class CharT, class Traits, class Allocator>
+ explicit
+ span(std::basic_string<CharT, Traits, Allocator>& s)
+ : data_(&s[0])
+ , size_(s.size())
+ {
+ }
+
+ template<class CharT, class Traits, class Allocator>
+ explicit
+ span(std::basic_string<CharT, Traits, Allocator> const& s)
+ : data_(s.data())
+ , size_(s.size())
+ {
+ }
+#endif
+
+ /** Assignment
+
+ @param container The container to assign from
+ */
+ template<class ContiguousContainer>
+#if BOOST_BEAST_DOXYGEN
+ span&
+#else
+ typename std::enable_if<detail::is_contiguous_container<
+ ContiguousContainer, T>::value,
+ span&>::type
+#endif
+ operator=(ContiguousContainer&& container)
+ {
+ data_ = container.data();
+ size_ = container.size();
+ return *this;
+ }
+
+#if ! BOOST_BEAST_DOXYGEN
+ template<class CharT, class Traits, class Allocator>
+ span&
+ operator=(std::basic_string<
+ CharT, Traits, Allocator>& s)
+ {
+ data_ = &s[0];
+ size_ = s.size();
+ return *this;
+ }
+
+ template<class CharT, class Traits, class Allocator>
+ span&
+ operator=(std::basic_string<
+ CharT, Traits, Allocator> const& s)
+ {
+ data_ = s.data();
+ size_ = s.size();
+ return *this;
+ }
+#endif
+
+ /// Returns `true` if the span is empty
+ bool
+ empty() const
+ {
+ return size_ == 0;
+ }
+
+ /// Returns a pointer to the beginning of the span
+ T*
+ data() const
+ {
+ return data_;
+ }
+
+ /// Returns the number of elements in the span
+ std::size_t
+ size() const
+ {
+ return size_;
+ }
+
+ /// Returns an iterator to the beginning of the span
+ const_iterator
+ begin() const
+ {
+ return data_;
+ }
+
+ /// Returns an iterator to the beginning of the span
+ const_iterator
+ cbegin() const
+ {
+ return data_;
+ }
+
+ /// Returns an iterator to one past the end of the span
+ const_iterator
+ end() const
+ {
+ return data_ + size_;
+ }
+
+ /// Returns an iterator to one past the end of the span
+ const_iterator
+ cend() const
+ {
+ return data_ + size_;
+ }
+};
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/static_buffer.hpp b/boost/beast/core/static_buffer.hpp
new file mode 100644
index 0000000000..05496c4018
--- /dev/null
+++ b/boost/beast/core/static_buffer.hpp
@@ -0,0 +1,217 @@
+//
+// 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_STATIC_BUFFER_HPP
+#define BOOST_BEAST_STATIC_BUFFER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/buffer.hpp>
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <cstring>
+
+namespace boost {
+namespace beast {
+
+/** A circular @b DynamicBuffer with a fixed size internal buffer.
+
+ This implements a circular dynamic buffer. Calls to @ref prepare
+ never require moving memory. The buffer sequences returned may
+ be up to length two.
+ Ownership of the underlying storage belongs to the derived class.
+
+ @note Variables are usually declared using the template class
+ @ref static_buffer; however, to reduce the number of instantiations
+ of template functions receiving static stream buffer arguments in a
+ deduced context, the signature of the receiving function should use
+ @ref static_buffer_base.
+
+ When used with @ref static_buffer this implements a dynamic
+ buffer using no memory allocations.
+
+ @see @ref static_buffer
+*/
+class static_buffer_base
+{
+ char* begin_;
+ std::size_t in_off_ = 0;
+ std::size_t in_size_ = 0;
+ std::size_t out_size_ = 0;
+ std::size_t capacity_;
+
+ static_buffer_base(static_buffer_base const& other) = delete;
+ static_buffer_base& operator=(static_buffer_base const&) = delete;
+
+public:
+ /// The type used to represent the input sequence as a list of buffers.
+ using const_buffers_type =
+ std::array<boost::asio::mutable_buffer, 2>;
+
+ /// The type used to represent the output sequence as a list of buffers.
+ using mutable_buffers_type =
+ std::array<boost::asio::mutable_buffer, 2>;
+
+ /** Constructor
+
+ This creates a dynamic buffer using the provided storage area.
+
+ @param p A pointer to valid storage of at least `n` bytes.
+
+ @param size The number of valid bytes pointed to by `p`.
+ */
+ static_buffer_base(void* p, std::size_t size);
+
+ /// Return the size of the input sequence.
+ std::size_t
+ size() const
+ {
+ return in_size_;
+ }
+
+ /// Return the maximum sum of the input and output sequence sizes.
+ std::size_t
+ max_size() const
+ {
+ return capacity_;
+ }
+
+ /// Return the maximum sum of input and output sizes that can be held without an allocation.
+ std::size_t
+ capacity() const
+ {
+ return capacity_;
+ }
+
+ /** Get a list of buffers that represent the input sequence.
+ */
+ const_buffers_type
+ data() const;
+
+ /** Get a list of buffers that represent the output sequence, with the given size.
+
+ @param size The number of bytes to request.
+
+ @throws std::length_error if the size would exceed the capacity.
+ */
+ mutable_buffers_type
+ prepare(std::size_t size);
+
+ /** Move bytes from the output sequence to the input sequence.
+
+ @param size The nubmer of bytes to commit. If this is greater
+ than the size of the output sequence, the entire output
+ sequence is committed.
+ */
+ void
+ commit(std::size_t size);
+
+ /** Remove bytes from the input sequence.
+
+ @param size The number of bytes to consume. If this is greater
+ than the size of the input sequence, the entire input sequence
+ is consumed.
+ */
+ void
+ consume(std::size_t size);
+
+protected:
+ /** Constructor
+
+ The buffer will be in an undefined state. It is necessary
+ for the derived class to call @ref reset in order to
+ initialize the object.
+ */
+ static_buffer_base() = default;
+
+ /** Reset the pointed-to buffer.
+
+ This function resets the internal state to the buffer provided.
+ All input and output sequences are invalidated. This function
+ allows the derived class to construct its members before
+ initializing the static buffer.
+
+ @param p A pointer to valid storage of at least `n` bytes.
+
+ @param size The number of valid bytes pointed to by `p`.
+ */
+ void
+ reset(void* p, std::size_t size);
+};
+
+//------------------------------------------------------------------------------
+
+/** A circular @b DynamicBuffer with a fixed size internal buffer.
+
+ This implements a circular dynamic buffer. Calls to @ref prepare
+ never require moving memory. The buffer sequences returned may
+ be up to length two.
+ Ownership of the underlying storage belongs to the derived class.
+
+ @tparam N The number of bytes in the internal buffer.
+
+ @note To reduce the number of template instantiations when passing
+ objects of this type in a deduced context, the signature of the
+ receiving function should use @ref static_buffer_base instead.
+
+ @see @ref static_buffer_base
+*/
+template<std::size_t N>
+class static_buffer : public static_buffer_base
+{
+ char buf_[N];
+
+public:
+ /// Constructor
+ static_buffer(static_buffer const&);
+
+ /// Constructor
+ static_buffer()
+ : static_buffer_base(buf_, N)
+ {
+ }
+
+ /// Assignment
+ static_buffer& operator=(static_buffer const&);
+
+ /// Returns the @ref static_buffer_base portion of this object
+ static_buffer_base&
+ base()
+ {
+ return *this;
+ }
+
+ /// Returns the @ref static_buffer_base portion of this object
+ static_buffer_base const&
+ base() const
+ {
+ return *this;
+ }
+
+ /// Return the maximum sum of the input and output sequence sizes.
+ std::size_t constexpr
+ max_size() const
+ {
+ return N;
+ }
+
+ /// Return the maximum sum of input and output sizes that can be held without an allocation.
+ std::size_t constexpr
+ capacity() const
+ {
+ return N;
+ }
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/static_buffer.ipp>
+
+#endif
diff --git a/boost/beast/core/static_string.hpp b/boost/beast/core/static_string.hpp
new file mode 100644
index 0000000000..991c2e64a5
--- /dev/null
+++ b/boost/beast/core/static_string.hpp
@@ -0,0 +1,1112 @@
+//
+// 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_STATIC_STRING_HPP
+#define BOOST_BEAST_STATIC_STRING_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/detail/static_string.hpp>
+#include <algorithm>
+#include <cstdint>
+#include <initializer_list>
+#include <iterator>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+
+/** A modifiable string with a fixed-size storage area.
+
+ These objects behave like `std::string` except that the storage
+ is not dynamically allocated but rather fixed in size.
+
+ These strings offer performance advantages when a protocol
+ imposes a natural small upper limit on the size of a value.
+
+ @note The stored string is always null-terminated.
+
+ @see @ref to_static_string
+*/
+template<
+ std::size_t N,
+ class CharT = char,
+ class Traits = std::char_traits<CharT>>
+class static_string
+{
+ template<std::size_t, class, class>
+ friend class static_string;
+
+ void
+ term()
+ {
+ Traits::assign(s_[n_], 0);
+ }
+
+ std::size_t n_;
+ CharT s_[N+1];
+
+public:
+ //
+ // Member types
+ //
+
+ using traits_type = Traits;
+ using value_type = typename Traits::char_type;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using const_pointer = value_type const*;
+ using const_reference = value_type const&;
+ using iterator = value_type*;
+ using const_iterator = value_type const*;
+ using reverse_iterator =
+ std::reverse_iterator<iterator>;
+ using const_reverse_iterator =
+ std::reverse_iterator<const_iterator>;
+
+ /// The type of `string_view` returned by the interface
+ using string_view_type =
+ basic_string_view<CharT, Traits>;
+
+ //
+ // Constants
+ //
+
+ /// Maximum size of the string excluding the null terminator
+ static std::size_t constexpr max_size_n = N;
+
+ /// A special index
+ static constexpr size_type npos = size_type(-1);
+
+ //
+ // (constructor)
+ //
+
+ /// Default constructor (empty string).
+ static_string();
+
+ /** Construct with count copies of character `ch`.
+
+ The behavior is undefined if `count >= npos`
+ */
+ static_string(size_type count, CharT ch);
+
+ /// Construct with a substring (pos, other.size()) of `other`.
+ template<std::size_t M>
+ static_string(static_string<M, CharT, Traits> const& other,
+ size_type pos);
+
+ /// Construct with a substring (pos, count) of `other`.
+ template<std::size_t M>
+ static_string(static_string<M, CharT, Traits> const& other,
+ size_type pos, size_type count);
+
+ /// Construct with the first `count` characters of `s`, including nulls.
+ static_string(CharT const* s, size_type count);
+
+ /// Construct from a null terminated string.
+ static_string(CharT const* s);
+
+ /// Construct from a range of characters
+ template<class InputIt>
+ static_string(InputIt first, InputIt last);
+
+ /// Copy constructor.
+ static_string(static_string const& other);
+
+ /// Copy constructor.
+ template<std::size_t M>
+ static_string(static_string<M, CharT, Traits> const& other);
+
+ /// Construct from an initializer list
+ static_string(std::initializer_list<CharT> init);
+
+ /// Construct from a `string_view`
+ explicit
+ static_string(string_view_type sv);
+
+ /** Construct from any object convertible to `string_view_type`.
+
+ The range (pos, n) is extracted from the value
+ obtained by converting `t` to `string_view_type`,
+ and used to construct the string.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class T>
+#else
+ template<class T, class = typename std::enable_if<
+ std::is_convertible<T, string_view_type>::value>::type>
+#endif
+ static_string(T const& t, size_type pos, size_type n);
+
+ //
+ // (assignment)
+ //
+
+ /// Copy assignment.
+ static_string&
+ operator=(static_string const& str)
+ {
+ return assign(str);
+ }
+
+ /// Copy assignment.
+ template<std::size_t M>
+ static_string&
+ operator=(static_string<M, CharT, Traits> const& str)
+ {
+ return assign(str);
+ }
+
+ /// Assign from null-terminated string.
+ static_string&
+ operator=(CharT const* s)
+ {
+ return assign(s);
+ }
+
+ /// Assign from single character.
+ static_string&
+ operator=(CharT ch)
+ {
+ return assign_char(ch,
+ std::integral_constant<bool, (N>0)>{});
+ }
+
+ /// Assign from initializer list.
+ static_string&
+ operator=(std::initializer_list<CharT> init)
+ {
+ return assign(init);
+ }
+
+ /// Assign from `string_view_type`.
+ static_string&
+ operator=(string_view_type sv)
+ {
+ return assign(sv);
+ }
+
+ /// Assign `count` copies of `ch`.
+ static_string&
+ assign(size_type count, CharT ch);
+
+ /// Assign from another `static_string`
+ static_string&
+ assign(static_string const& str);
+
+ // VFALCO NOTE this could come in two flavors,
+ // N>M and N<M, and skip the exception
+ // check when N>M
+
+ /// Assign from another `static_string`
+ template<std::size_t M>
+ static_string&
+ assign(static_string<M, CharT, Traits> const& str)
+ {
+ return assign(str.data(), str.size());
+ }
+
+ /// Assign `count` characterss starting at `npos` from `other`.
+ template<std::size_t M>
+ static_string&
+ assign(static_string<M, CharT, Traits> const& str,
+ size_type pos, size_type count = npos);
+
+ /// Assign the first `count` characters of `s`, including nulls.
+ static_string&
+ assign(CharT const* s, size_type count);
+
+ /// Assign a null terminated string.
+ static_string&
+ assign(CharT const* s)
+ {
+ return assign(s, Traits::length(s));
+ }
+
+ /// Assign from an iterator range of characters.
+ template<class InputIt>
+ static_string&
+ assign(InputIt first, InputIt last);
+
+ /// Assign from initializer list.
+ static_string&
+ assign(std::initializer_list<CharT> init)
+ {
+ return assign(init.begin(), init.end());
+ }
+
+ /// Assign from `string_view_type`.
+ static_string&
+ assign(string_view_type str)
+ {
+ return assign(str.data(), str.size());
+ }
+
+ /** Assign from any object convertible to `string_view_type`.
+
+ The range (pos, n) is extracted from the value
+ obtained by converting `t` to `string_view_type`,
+ and used to assign the string.
+ */
+ template<class T>
+#if BOOST_BEAST_DOXYGEN
+ static_string&
+#else
+ typename std::enable_if<std::is_convertible<T,
+ string_view_type>::value, static_string&>::type
+#endif
+ assign(T const& t,
+ size_type pos, size_type count = npos);
+
+ //
+ // Element access
+ //
+
+ /// Access specified character with bounds checking.
+ reference
+ at(size_type pos);
+
+ /// Access specified character with bounds checking.
+ const_reference
+ at(size_type pos) const;
+
+ /// Access specified character.
+ reference
+ operator[](size_type pos)
+ {
+ return s_[pos];
+ }
+
+ /// Access specified character.
+ const_reference
+ operator[](size_type pos) const
+ {
+ return s_[pos];
+ }
+
+ /// Accesses the first character.
+ CharT&
+ front()
+ {
+ return s_[0];
+ }
+
+ /// Accesses the first character.
+ CharT const&
+ front() const
+ {
+ return s_[0];
+ }
+
+ /// Accesses the last character.
+ CharT&
+ back()
+ {
+ return s_[n_-1];
+ }
+
+ /// Accesses the last character.
+ CharT const&
+ back() const
+ {
+ return s_[n_-1];
+ }
+
+ /// Returns a pointer to the first character of a string.
+ CharT*
+ data()
+ {
+ return &s_[0];
+ }
+
+ /// Returns a pointer to the first character of a string.
+ CharT const*
+ data() const
+ {
+ return &s_[0];
+ }
+
+ /// Returns a non-modifiable standard C character array version of the string.
+ CharT const*
+ c_str() const
+ {
+ return data();
+ }
+
+ /// Convert a static string to a `string_view_type`
+ operator string_view_type() const
+ {
+ return basic_string_view<
+ CharT, Traits>{data(), size()};
+ }
+
+ //
+ // Iterators
+ //
+
+ /// Returns an iterator to the beginning.
+ iterator
+ begin()
+ {
+ return &s_[0];
+ }
+
+ /// Returns an iterator to the beginning.
+ const_iterator
+ begin() const
+ {
+ return &s_[0];
+ }
+
+ /// Returns an iterator to the beginning.
+ const_iterator
+ cbegin() const
+ {
+ return &s_[0];
+ }
+
+ /// Returns an iterator to the end.
+ iterator
+ end()
+ {
+ return &s_[n_];
+ }
+
+ /// Returns an iterator to the end.
+ const_iterator
+ end() const
+ {
+ return &s_[n_];
+ }
+
+ /// Returns an iterator to the end.
+ const_iterator
+ cend() const
+ {
+ return &s_[n_];
+ }
+
+ /// Returns a reverse iterator to the beginning.
+ reverse_iterator
+ rbegin()
+ {
+ return reverse_iterator{end()};
+ }
+
+ /// Returns a reverse iterator to the beginning.
+ const_reverse_iterator
+ rbegin() const
+ {
+ return const_reverse_iterator{cend()};
+ }
+
+ /// Returns a reverse iterator to the beginning.
+ const_reverse_iterator
+ crbegin() const
+ {
+ return const_reverse_iterator{cend()};
+ }
+
+ /// Returns a reverse iterator to the end.
+ reverse_iterator
+ rend()
+ {
+ return reverse_iterator{begin()};
+ }
+
+ /// Returns a reverse iterator to the end.
+ const_reverse_iterator
+ rend() const
+ {
+ return const_reverse_iterator{cbegin()};
+ }
+
+ /// Returns a reverse iterator to the end.
+ const_reverse_iterator
+ crend() const
+ {
+ return const_reverse_iterator{cbegin()};
+ }
+
+ //
+ // Capacity
+ //
+
+ /// Returns `true` if the string is empty.
+ bool
+ empty() const
+ {
+ return n_ == 0;
+ }
+
+ /// Returns the number of characters, excluding the null terminator.
+ size_type
+ size() const
+ {
+ return n_;
+ }
+
+ /// Returns the number of characters, excluding the null terminator.
+ size_type
+ length() const
+ {
+ return size();
+ }
+
+ /// Returns the maximum number of characters that can be stored, excluding the null terminator.
+ size_type constexpr
+ max_size() const
+ {
+ return N;
+ }
+
+ /** Reserves storage.
+
+ This actually just throws an exception if `n > N`,
+ otherwise does nothing since the storage is fixed.
+ */
+ void
+ reserve(std::size_t n);
+
+ /// Returns the number of characters that can be held in currently allocated storage.
+ size_type constexpr
+ capacity() const
+ {
+ return max_size();
+ }
+
+ /** Reduces memory usage by freeing unused memory.
+
+ This actually does nothing, since the storage is fixed.
+ */
+ void
+ shrink_to_fit()
+ {
+ }
+
+ //
+ // Operations
+ //
+
+ /// Clears the contents.
+ void
+ clear();
+
+ static_string&
+ insert(size_type index, size_type count, CharT ch);
+
+ static_string&
+ insert(size_type index, CharT const* s)
+ {
+ return insert(index, s, Traits::length(s));
+ }
+
+ static_string&
+ insert(size_type index, CharT const* s, size_type count);
+
+ template<std::size_t M>
+ static_string&
+ insert(size_type index,
+ static_string<M, CharT, Traits> const& str)
+ {
+ return insert(index, str.data(), str.size());
+ }
+
+ template<std::size_t M>
+ static_string&
+ insert(size_type index,
+ static_string<M, CharT, Traits> const& str,
+ size_type index_str, size_type count = npos);
+
+ iterator
+ insert(const_iterator pos, CharT ch)
+ {
+ return insert(pos, 1, ch);
+ }
+
+ iterator
+ insert(const_iterator pos, size_type count, CharT ch);
+
+ template<class InputIt>
+#if BOOST_BEAST_DOXYGEN
+ iterator
+#else
+ typename std::enable_if<
+ detail::is_input_iterator<InputIt>::value,
+ iterator>::type
+#endif
+ insert(const_iterator pos, InputIt first, InputIt last);
+
+ iterator
+ insert(const_iterator pos, std::initializer_list<CharT> init)
+ {
+ return insert(pos, init.begin(), init.end());
+ }
+
+ static_string&
+ insert(size_type index, string_view_type str)
+ {
+ return insert(index, str.data(), str.size());
+ }
+
+ template<class T>
+#if BOOST_BEAST_DOXYGEN
+ static_string&
+#else
+ typename std::enable_if<
+ std::is_convertible<T const&, string_view_type>::value &&
+ ! std::is_convertible<T const&, CharT const*>::value,
+ static_string&>::type
+#endif
+ insert(size_type index, T const& t,
+ size_type index_str, size_type count = npos);
+
+ static_string&
+ erase(size_type index = 0, size_type count = npos);
+
+ iterator
+ erase(const_iterator pos);
+
+ iterator
+ erase(const_iterator first, const_iterator last);
+
+ void
+ push_back(CharT ch);
+
+ void
+ pop_back()
+ {
+ Traits::assign(s_[--n_], 0);
+ }
+
+ static_string&
+ append(size_type count, CharT ch)
+ {
+ insert(end(), count, ch);
+ return *this;
+ }
+
+ template<std::size_t M>
+ static_string&
+ append(static_string<M, CharT, Traits> const& str)
+ {
+ insert(size(), str);
+ return *this;
+ }
+
+ template<std::size_t M>
+ static_string&
+ append(static_string<M, CharT, Traits> const& str,
+ size_type pos, size_type count = npos);
+
+ static_string&
+ append(CharT const* s, size_type count)
+ {
+ insert(size(), s, count);
+ return *this;
+ }
+
+ static_string&
+ append(CharT const* s)
+ {
+ insert(size(), s);
+ return *this;
+ }
+
+ template<class InputIt>
+#if BOOST_BEAST_DOXYGEN
+ static_string&
+#else
+ typename std::enable_if<
+ detail::is_input_iterator<InputIt>::value,
+ static_string&>::type
+#endif
+ append(InputIt first, InputIt last)
+ {
+ insert(end(), first, last);
+ return *this;
+ }
+
+ static_string&
+ append(std::initializer_list<CharT> init)
+ {
+ insert(end(), init);
+ return *this;
+ }
+
+ static_string&
+ append(string_view_type sv)
+ {
+ insert(size(), sv);
+ return *this;
+ }
+
+ template<class T>
+ typename std::enable_if<
+ std::is_convertible<T const&, string_view_type>::value &&
+ ! std::is_convertible<T const&, CharT const*>::value,
+ static_string&>::type
+ append(T const& t, size_type pos, size_type count = npos)
+ {
+ insert(size(), t, pos, count);
+ return *this;
+ }
+
+ template<std::size_t M>
+ static_string&
+ operator+=(static_string<M, CharT, Traits> const& str)
+ {
+ return append(str.data(), str.size());
+ }
+
+ static_string&
+ operator+=(CharT ch)
+ {
+ push_back(ch);
+ return *this;
+ }
+
+ static_string&
+ operator+=(CharT const* s)
+ {
+ return append(s);
+ }
+
+ static_string&
+ operator+=(std::initializer_list<CharT> init)
+ {
+ return append(init);
+ }
+
+ static_string&
+ operator+=(string_view_type const& str)
+ {
+ return append(str);
+ }
+
+ template<std::size_t M>
+ int
+ compare(static_string<M, CharT, Traits> const& str) const
+ {
+ return detail::lexicographical_compare<CharT, Traits>(
+ &s_[0], n_, &str.s_[0], str.n_);
+ }
+
+ template<std::size_t M>
+ int
+ compare(size_type pos1, size_type count1,
+ static_string<M, CharT, Traits> const& str) const
+ {
+ return detail::lexicographical_compare<CharT, Traits>(
+ substr(pos1, count1), str.data(), str.size());
+ }
+
+ template<std::size_t M>
+ int
+ compare(size_type pos1, size_type count1,
+ static_string<M, CharT, Traits> const& str,
+ size_type pos2, size_type count2 = npos) const
+ {
+ return detail::lexicographical_compare(
+ substr(pos1, count1), str.substr(pos2, count2));
+ }
+
+ int
+ compare(CharT const* s) const
+ {
+ return detail::lexicographical_compare<CharT, Traits>(
+ &s_[0], n_, s, Traits::length(s));
+ }
+
+ int
+ compare(size_type pos1, size_type count1,
+ CharT const* s) const
+ {
+ return detail::lexicographical_compare<CharT, Traits>(
+ substr(pos1, count1), s, Traits::length(s));
+ }
+
+ int
+ compare(size_type pos1, size_type count1,
+ CharT const*s, size_type count2) const
+ {
+ return detail::lexicographical_compare<CharT, Traits>(
+ substr(pos1, count1), s, count2);
+ }
+
+ int
+ compare(string_view_type str) const
+ {
+ return detail::lexicographical_compare<CharT, Traits>(
+ &s_[0], n_, str.data(), str.size());
+ }
+
+ int
+ compare(size_type pos1, size_type count1,
+ string_view_type str) const
+ {
+ return detail::lexicographical_compare<CharT, Traits>(
+ substr(pos1, count1), str);
+ }
+
+ template<class T>
+#if BOOST_BEAST_DOXYGEN
+ int
+#else
+ typename std::enable_if<
+ std::is_convertible<T const&, string_view_type>::value &&
+ ! std::is_convertible<T const&, CharT const*>::value,
+ int>::type
+#endif
+ compare(size_type pos1, size_type count1,
+ T const& t, size_type pos2,
+ size_type count2 = npos) const
+ {
+ return compare(pos1, count1,
+ string_view_type(t).substr(pos2, count2));
+ }
+
+ string_view_type
+ substr(size_type pos = 0, size_type count = npos) const;
+
+ /// Copy a substring (pos, pos+count) to character string pointed to by `dest`.
+ size_type
+ copy(CharT* dest, size_type count, size_type pos = 0) const;
+
+ /** Changes the number of characters stored.
+
+ If the resulting string is larger, the new
+ characters are uninitialized.
+ */
+ void
+ resize(std::size_t n);
+
+ /** Changes the number of characters stored.
+
+ If the resulting string is larger, the new
+ characters are initialized to the value of `c`.
+ */
+ void
+ resize(std::size_t n, CharT c);
+
+ /// Exchange the contents of this string with another.
+ void
+ swap(static_string& str);
+
+ /// Exchange the contents of this string with another.
+ template<std::size_t M>
+ void
+ swap(static_string<M, CharT, Traits>& str);
+
+ //
+ // Search
+ //
+
+private:
+ static_string&
+ assign_char(CharT ch, std::true_type);
+
+ static_string&
+ assign_char(CharT ch, std::false_type);
+};
+
+//
+// Disallowed operations
+//
+
+// These operations are explicitly deleted since
+// there is no reasonable implementation possible.
+
+template<std::size_t N, std::size_t M, class CharT, class Traits>
+void
+operator+(
+ static_string<N, CharT, Traits>const& lhs,
+ static_string<M, CharT, Traits>const& rhs) = delete;
+
+template<std::size_t N, class CharT, class Traits>
+void
+operator+(CharT const* lhs,
+ static_string<N, CharT, Traits>const& rhs) = delete;
+
+template<std::size_t N, class CharT, class Traits>
+void
+operator+(CharT lhs,
+ static_string<N, CharT, Traits> const& rhs) = delete;
+
+template<std::size_t N, class CharT, class Traits>
+void
+operator+(static_string<N, CharT, Traits> const& lhs,
+ CharT const* rhs) = delete;
+
+template<std::size_t N, class CharT, class Traits>
+void
+operator+(static_string<N, CharT, Traits> const& lhs,
+ CharT rhs) = delete;
+
+//
+// Non-member functions
+//
+
+template<std::size_t N, std::size_t M,
+ class CharT, class Traits>
+bool
+operator==(
+ static_string<N, CharT, Traits> const& lhs,
+ static_string<M, CharT, Traits> const& rhs)
+{
+ return lhs.compare(rhs) == 0;
+}
+
+template<std::size_t N, std::size_t M,
+ class CharT, class Traits>
+bool
+operator!=(
+ static_string<N, CharT, Traits> const& lhs,
+ static_string<M, CharT, Traits> const& rhs)
+{
+ return lhs.compare(rhs) != 0;
+}
+
+template<std::size_t N, std::size_t M,
+ class CharT, class Traits>
+bool
+operator<(
+ static_string<N, CharT, Traits> const& lhs,
+ static_string<M, CharT, Traits> const& rhs)
+{
+ return lhs.compare(rhs) < 0;
+}
+
+template<std::size_t N, std::size_t M,
+ class CharT, class Traits>
+bool
+operator<=(
+ static_string<N, CharT, Traits> const& lhs,
+ static_string<M, CharT, Traits> const& rhs)
+{
+ return lhs.compare(rhs) <= 0;
+}
+
+template<std::size_t N, std::size_t M,
+ class CharT, class Traits>
+bool
+operator>(
+ static_string<N, CharT, Traits> const& lhs,
+ static_string<M, CharT, Traits> const& rhs)
+{
+ return lhs.compare(rhs) > 0;
+}
+
+template<std::size_t N, std::size_t M,
+ class CharT, class Traits>
+bool
+operator>=(
+ static_string<N, CharT, Traits> const& lhs,
+ static_string<M, CharT, Traits> const& rhs)
+{
+ return lhs.compare(rhs) >= 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator==(
+ CharT const* lhs,
+ static_string<N, CharT, Traits> const& rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs, Traits::length(lhs),
+ rhs.data(), rhs.size()) == 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator==(
+ static_string<N, CharT, Traits> const& lhs,
+ CharT const* rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs.data(), lhs.size(),
+ rhs, Traits::length(rhs)) == 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator!=(
+ CharT const* lhs,
+ static_string<N, CharT, Traits> const& rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs, Traits::length(lhs),
+ rhs.data(), rhs.size()) != 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator!=(
+ static_string<N, CharT, Traits> const& lhs,
+ CharT const* rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs.data(), lhs.size(),
+ rhs, Traits::length(rhs)) != 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator<(
+ CharT const* lhs,
+ static_string<N, CharT, Traits> const& rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs, Traits::length(lhs),
+ rhs.data(), rhs.size()) < 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator<(
+ static_string<N, CharT, Traits> const& lhs,
+ CharT const* rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs.data(), lhs.size(),
+ rhs, Traits::length(rhs)) < 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator<=(
+ CharT const* lhs,
+ static_string<N, CharT, Traits> const& rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs, Traits::length(lhs),
+ rhs.data(), rhs.size()) <= 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator<=(
+ static_string<N, CharT, Traits> const& lhs,
+ CharT const* rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs.data(), lhs.size(),
+ rhs, Traits::length(rhs)) <= 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator>(
+ CharT const* lhs,
+ static_string<N, CharT, Traits> const& rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs, Traits::length(lhs),
+ rhs.data(), rhs.size()) > 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator>(
+ static_string<N, CharT, Traits> const& lhs,
+ CharT const* rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs.data(), lhs.size(),
+ rhs, Traits::length(rhs)) > 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator>=(
+ CharT const* lhs,
+ static_string<N, CharT, Traits> const& rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs, Traits::length(lhs),
+ rhs.data(), rhs.size()) >= 0;
+}
+
+template<std::size_t N, class CharT, class Traits>
+bool
+operator>=(
+ static_string<N, CharT, Traits> const& lhs,
+ CharT const* rhs)
+{
+ return detail::lexicographical_compare<CharT, Traits>(
+ lhs.data(), lhs.size(),
+ rhs, Traits::length(rhs)) >= 0;
+}
+
+//
+// swap
+//
+
+template<std::size_t N, class CharT, class Traits>
+void
+swap(
+ static_string<N, CharT, Traits>& lhs,
+ static_string<N, CharT, Traits>& rhs)
+{
+ lhs.swap(rhs);
+}
+
+template<std::size_t N, std::size_t M,
+ class CharT, class Traits>
+void
+swap(
+ static_string<N, CharT, Traits>& lhs,
+ static_string<M, CharT, Traits>& rhs)
+{
+ lhs.swap(rhs);
+}
+
+//
+// Input/Output
+//
+
+template<std::size_t N, class CharT, class Traits>
+std::basic_ostream<CharT, Traits>&
+operator<<(std::basic_ostream<CharT, Traits>& os,
+ static_string<N, CharT, Traits> const& str)
+{
+ return os << static_cast<
+ beast::basic_string_view<CharT, Traits>>(str);
+}
+
+//
+// Numeric conversions
+//
+
+/** Returns a static string representing an integer as a decimal.
+
+ @param x The signed or unsigned integer to convert.
+ This must be an integral type.
+
+ @return A @ref static_string with an implementation defined
+ maximum size large enough to hold the longest possible decimal
+ representation of any integer of the given type.
+*/
+template<class Integer>
+static_string<detail::max_digits(sizeof(Integer))>
+to_static_string(Integer x);
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/static_string.ipp>
+
+#endif
diff --git a/boost/beast/core/string.hpp b/boost/beast/core/string.hpp
new file mode 100644
index 0000000000..f262b565da
--- /dev/null
+++ b/boost/beast/core/string.hpp
@@ -0,0 +1,155 @@
+//
+// 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_STRING_HPP
+#define BOOST_BEAST_STRING_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/version.hpp>
+#ifndef BOOST_BEAST_NO_BOOST_STRING_VIEW
+# if BOOST_VERSION >= 106400
+# define BOOST_BEAST_NO_BOOST_STRING_VIEW 0
+# else
+# define BOOST_BEAST_NO_BOOST_STRING_VIEW 1
+# endif
+#endif
+
+#if BOOST_BEAST_NO_BOOST_STRING_VIEW
+#include <boost/utility/string_ref.hpp>
+#else
+#include <boost/utility/string_view.hpp>
+#endif
+
+#include <algorithm>
+
+namespace boost {
+namespace beast {
+
+#if BOOST_BEAST_NO_BOOST_STRING_VIEW
+/// The type of string view used by the library
+using string_view = boost::string_ref;
+
+/// The type of basic string view used by the library
+template<class CharT, class Traits>
+using basic_string_view =
+ boost::basic_string_ref<CharT, Traits>;
+#else
+/// The type of string view used by the library
+using string_view = boost::string_view;
+
+/// The type of basic string view used by the library
+template<class CharT, class Traits>
+using basic_string_view =
+ boost::basic_string_view<CharT, Traits>;
+#endif
+
+namespace detail {
+
+inline
+char
+ascii_tolower(char c)
+{
+ if(c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ return c;
+}
+
+template<class = void>
+bool
+iequals(
+ beast::string_view lhs,
+ beast::string_view rhs)
+{
+ auto n = lhs.size();
+ if(rhs.size() != n)
+ return false;
+ auto p1 = lhs.data();
+ auto p2 = rhs.data();
+ char a, b;
+ while(n--)
+ {
+ a = *p1++;
+ b = *p2++;
+ if(a != b)
+ goto slow;
+ }
+ return true;
+
+ while(n--)
+ {
+ slow:
+ if(ascii_tolower(a) != ascii_tolower(b))
+ return false;
+ a = *p1++;
+ b = *p2++;
+ }
+ return true;
+}
+
+} // detail
+
+/** Returns `true` if two strings are equal, using a case-insensitive comparison.
+
+ The case-comparison operation is defined only for low-ASCII characters.
+
+ @param lhs The string on the left side of the equality
+
+ @param rhs The string on the right side of the equality
+*/
+inline
+bool
+iequals(
+ beast::string_view lhs,
+ beast::string_view rhs)
+{
+ return detail::iequals(lhs, rhs);
+}
+
+/** A case-insensitive less predicate for strings.
+
+ The case-comparison operation is defined only for low-ASCII characters.
+*/
+struct iless
+{
+ bool
+ operator()(
+ string_view lhs,
+ string_view rhs) const
+ {
+ using std::begin;
+ using std::end;
+ return std::lexicographical_compare(
+ begin(lhs), end(lhs), begin(rhs), end(rhs),
+ [](char c1, char c2)
+ {
+ return detail::ascii_tolower(c1) < detail::ascii_tolower(c2);
+ }
+ );
+ }
+};
+
+/** A case-insensitive equality predicate for strings.
+
+ The case-comparison operation is defined only for low-ASCII characters.
+*/
+struct iequal
+{
+ bool
+ operator()(
+ string_view lhs,
+ string_view rhs) const
+ {
+ return iequals(lhs, rhs);
+ }
+};
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/core/string_param.hpp b/boost/beast/core/string_param.hpp
new file mode 100644
index 0000000000..a068cc4091
--- /dev/null
+++ b/boost/beast/core/string_param.hpp
@@ -0,0 +1,130 @@
+//
+// 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_STRING_PARAM_HPP
+#define BOOST_BEAST_STRING_PARAM_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/detail/static_ostream.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/optional.hpp>
+
+namespace boost {
+namespace beast {
+
+/** A function parameter which efficiently converts to string.
+
+ This is used as a function parameter type to allow callers
+ notational convenience: objects other than strings may be
+ passed in contexts where a string is expected. The conversion
+ to string is made using `operator<<` to a non-dynamically
+ allocated static buffer if possible, else to a `std::string`
+ on overflow.
+
+ To use it, modify your function signature to accept
+ `string_param` and then extract the string inside the
+ function:
+ @code
+ void print(string_param s)
+ {
+ std::cout << s.str();
+ }
+ @endcode
+*/
+class string_param
+{
+ string_view sv_;
+ char buf_[128];
+ boost::optional<detail::static_ostream> os_;
+
+ template<class T>
+ typename std::enable_if<
+ std::is_integral<T>::value>::type
+ print(T const&);
+
+ template<class T>
+ typename std::enable_if<
+ ! std::is_integral<T>::value &&
+ ! std::is_convertible<T, string_view>::value
+ >::type
+ print(T const&);
+
+ void
+ print(string_view);
+
+ template<class T>
+ typename std::enable_if<
+ std::is_integral<T>::value>::type
+ print_1(T const&);
+
+ template<class T>
+ typename std::enable_if<
+ ! std::is_integral<T>::value>::type
+ print_1(T const&);
+
+ void
+ print_n()
+ {
+ }
+
+ template<class T0, class... TN>
+ void
+ print_n(T0 const&, TN const&...);
+
+ template<class T0, class T1, class... TN>
+ void
+ print(T0 const&, T1 const&, TN const&...);
+
+public:
+ /// Copy constructor (disallowed)
+ string_param(string_param const&) = delete;
+
+ /// Copy assignment (disallowed)
+ string_param& operator=(string_param const&) = delete;
+
+ /** Constructor
+
+ This function constructs a string as if by concatenating
+ the result of streaming each argument in order into an
+ output stream. It is used as a notational convenience
+ at call sites which expect a parameter with the semantics
+ of a @ref string_view.
+
+ The implementation uses a small, internal static buffer
+ to avoid memory allocations especially for the case where
+ the list of arguments to be converted consists of a single
+ integral type.
+
+ @param args One or more arguments to convert
+ */
+ template<class... Args>
+ string_param(Args const&... args);
+
+ /// Returns the contained string
+ string_view
+ str() const
+ {
+ return sv_;
+ }
+
+ /// Implicit conversion to @ref string_view
+ operator string_view const() const
+ {
+ return sv_;
+ }
+};
+
+} // beast
+} // boost
+
+#include <boost/beast/core/impl/string_param.ipp>
+
+#endif
diff --git a/boost/beast/core/type_traits.hpp b/boost/beast/core/type_traits.hpp
new file mode 100644
index 0000000000..d622e0e8ec
--- /dev/null
+++ b/boost/beast/core/type_traits.hpp
@@ -0,0 +1,490 @@
+//
+// 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_TYPE_TRAITS_HPP
+#define BOOST_BEAST_TYPE_TRAITS_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/file_base.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+
+//------------------------------------------------------------------------------
+//
+// Handler concepts
+//
+//------------------------------------------------------------------------------
+
+/** Determine if `T` meets the requirements of @b CompletionHandler.
+
+ This trait checks whether a type meets the requirements for a completion
+ handler, and is also callable with the specified signature.
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` meets the requirements,
+ else the type will be `std::false_type`.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ struct handler
+ {
+ void operator()(error_code&);
+ };
+
+ static_assert(is_completion_handler<handler, void(error_code&)>::value,
+ "Not a completion handler");
+ @endcode
+*/
+template<class T, class Signature>
+#if BOOST_BEAST_DOXYGEN
+using is_completion_handler = std::integral_constant<bool, ...>;
+#else
+using is_completion_handler = std::integral_constant<bool,
+ std::is_copy_constructible<typename std::decay<T>::type>::value &&
+ detail::is_invocable<T, Signature>::value>;
+#endif
+
+//------------------------------------------------------------------------------
+//
+// Stream concepts
+//
+//------------------------------------------------------------------------------
+
+/** Determine if `T` has the `get_executor` member function.
+
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` has the member
+ function with the correct signature, else type will be `std::false_type`.
+
+ @par Example
+
+ Use with tag dispatching:
+
+ @code
+ template<class T>
+ void maybe_hello(T& t, std::true_type)
+ {
+ boost::asio::post(
+ t.get_executor(),
+ []
+ {
+ std::cout << "Hello, world!" << std::endl;
+ });
+ }
+
+ template<class T>
+ void maybe_hello(T&, std::false_type)
+ {
+ // T does not have get_executor
+ }
+
+ template<class T>
+ void maybe_hello(T& t)
+ {
+ maybe_hello(t, has_get_executor<T>{});
+ }
+ @endcode
+
+ Use with `static_assert`:
+
+ @code
+ struct stream
+ {
+ using executor_type = boost::asio::io_context::executor_type;
+ executor_type get_executor() noexcept;
+ };
+
+ static_assert(has_get_executor<stream>::value, "Missing get_executor member");
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct has_get_executor : std::integral_constant<bool, ...>{};
+#else
+template<class T, class = void>
+struct has_get_executor : std::false_type {};
+
+template<class T>
+struct has_get_executor<T, beast::detail::void_t<decltype(
+ std::declval<T&>().get_executor(),
+ (void)0)>> : std::true_type {};
+#endif
+
+/** Returns `T::lowest_layer_type` if it exists, else `T`
+
+ This will contain a nested `type` equal to `T::lowest_layer_type`
+ if it exists, else `type` will be equal to `T`.
+
+ @par Example
+
+ Declaring a wrapper:
+
+ @code
+ template<class Stream>
+ struct stream_wrapper
+ {
+ using next_layer_type = typename std::remove_reference<Stream>::type;
+ using lowest_layer_type = typename get_lowest_layer<stream_type>::type;
+ };
+ @endcode
+
+ Defining a metafunction:
+
+ @code
+ /// Alias for `std::true_type` if `T` wraps another stream
+ template<class T>
+ using is_stream_wrapper : std::integral_constant<bool,
+ ! std::is_same<T, typename get_lowest_layer<T>::type>::value> {};
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct get_lowest_layer;
+#else
+template<class T, class = void>
+struct get_lowest_layer
+{
+ using type = T;
+};
+
+template<class T>
+struct get_lowest_layer<T, detail::void_t<
+ typename T::lowest_layer_type>>
+{
+ using type = typename T::lowest_layer_type;
+};
+#endif
+
+/** Determine if `T` meets the requirements of @b AsyncReadStream.
+
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` meets the requirements,
+ else the type will be `std::false_type`.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ template<class AsyncReadStream>
+ void f(AsyncReadStream& stream)
+ {
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ ...
+ @endcode
+
+ Use with `std::enable_if` (SFINAE):
+
+ @code
+ template<class AsyncReadStream>
+ typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type
+ f(AsyncReadStream& stream);
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_async_read_stream : std::integral_constant<bool, ...>{};
+#else
+template<class T, class = void>
+struct is_async_read_stream : std::false_type {};
+
+template<class T>
+struct is_async_read_stream<T, detail::void_t<decltype(
+ std::declval<T>().async_read_some(
+ std::declval<detail::MutableBufferSequence>(),
+ std::declval<detail::ReadHandler>()),
+ (void)0)>> : std::integral_constant<bool,
+ has_get_executor<T>::value
+ > {};
+#endif
+
+/** Determine if `T` meets the requirements of @b AsyncWriteStream.
+
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` meets the requirements,
+ else the type will be `std::false_type`.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ template<class AsyncWriteStream>
+ void f(AsyncWriteStream& stream)
+ {
+ static_assert(is_async_write_stream<AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ ...
+ @endcode
+
+ Use with `std::enable_if` (SFINAE):
+
+ @code
+ template<class AsyncWriteStream>
+ typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type
+ f(AsyncWriteStream& stream);
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_async_write_stream : std::integral_constant<bool, ...>{};
+#else
+template<class T, class = void>
+struct is_async_write_stream : std::false_type {};
+
+template<class T>
+struct is_async_write_stream<T, detail::void_t<decltype(
+ std::declval<T>().async_write_some(
+ std::declval<detail::ConstBufferSequence>(),
+ std::declval<detail::WriteHandler>()),
+ (void)0)>> : std::integral_constant<bool,
+ has_get_executor<T>::value
+ > {};
+#endif
+
+/** Determine if `T` meets the requirements of @b SyncReadStream.
+
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` meets the requirements,
+ else the type will be `std::false_type`.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ template<class SyncReadStream>
+ void f(SyncReadStream& stream)
+ {
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ ...
+ @endcode
+
+ Use with `std::enable_if` (SFINAE):
+
+ @code
+ template<class SyncReadStream>
+ typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
+ f(SyncReadStream& stream);
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_sync_read_stream : std::integral_constant<bool, ...>{};
+#else
+template<class T, class = void>
+struct is_sync_read_stream : std::false_type {};
+
+template<class T>
+struct is_sync_read_stream<T, detail::void_t<decltype(
+ std::declval<std::size_t&>() = std::declval<T>().read_some(
+ std::declval<detail::MutableBufferSequence>()),
+ std::declval<std::size_t&>() = std::declval<T>().read_some(
+ std::declval<detail::MutableBufferSequence>(),
+ std::declval<boost::system::error_code&>()),
+ (void)0)>> : std::true_type {};
+#endif
+
+/** Determine if `T` meets the requirements of @b SyncWriteStream.
+
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` meets the requirements,
+ else the type will be `std::false_type`.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ template<class SyncReadStream>
+ void f(SyncReadStream& stream)
+ {
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ ...
+ @endcode
+
+ Use with `std::enable_if` (SFINAE):
+
+ @code
+ template<class SyncReadStream>
+ typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
+ f(SyncReadStream& stream);
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_sync_write_stream : std::integral_constant<bool, ...>{};
+#else
+template<class T, class = void>
+struct is_sync_write_stream : std::false_type {};
+
+template<class T>
+struct is_sync_write_stream<T, detail::void_t<decltype(
+ std::declval<std::size_t&>() = std::declval<T&>().write_some(
+ std::declval<detail::ConstBufferSequence>()),
+ std::declval<std::size_t&>() = std::declval<T&>().write_some(
+ std::declval<detail::ConstBufferSequence>(),
+ std::declval<boost::system::error_code&>()),
+ (void)0)>> : std::true_type {};
+#endif
+
+/** Determine if `T` meets the requirements of @b AsyncStream.
+
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` meets the requirements,
+ else the type will be `std::false_type`.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ template<class AsyncStream>
+ void f(AsyncStream& stream)
+ {
+ static_assert(is_async_stream<AsyncStream>::value,
+ "AsyncStream requirements not met");
+ ...
+ @endcode
+
+ Use with `std::enable_if` (SFINAE):
+
+ @code
+ template<class AsyncStream>
+ typename std::enable_if<is_async_stream<AsyncStream>::value>::type
+ f(AsyncStream& stream);
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_async_stream : std::integral_constant<bool, ...>{};
+#else
+template<class T>
+using is_async_stream = std::integral_constant<bool,
+ is_async_read_stream<T>::value && is_async_write_stream<T>::value>;
+#endif
+
+/** Determine if `T` meets the requirements of @b SyncStream.
+
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` meets the requirements,
+ else the type will be `std::false_type`.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ template<class SyncStream>
+ void f(SyncStream& stream)
+ {
+ static_assert(is_sync_stream<SyncStream>::value,
+ "SyncStream requirements not met");
+ ...
+ @endcode
+
+ Use with `std::enable_if` (SFINAE):
+
+ @code
+ template<class SyncStream>
+ typename std::enable_if<is_sync_stream<SyncStream>::value>::type
+ f(SyncStream& stream);
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_sync_stream : std::integral_constant<bool, ...>{};
+#else
+template<class T>
+using is_sync_stream = std::integral_constant<bool,
+ is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>;
+#endif
+
+//------------------------------------------------------------------------------
+//
+// File concepts
+//
+//------------------------------------------------------------------------------
+
+/** Determine if `T` meets the requirements of @b File.
+
+ Metafunctions are used to perform compile time checking of template
+ types. This type will be `std::true_type` if `T` meets the requirements,
+ else the type will be `std::false_type`.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ template<class File>
+ void f(File& file)
+ {
+ static_assert(is_file<File>::value,
+ "File requirements not met");
+ ...
+ @endcode
+
+ Use with `std::enable_if` (SFINAE):
+
+ @code
+ template<class File>
+ typename std::enable_if<is_file<File>::value>::type
+ f(File& file);
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_file : std::integral_constant<bool, ...>{};
+#else
+template<class T, class = void>
+struct is_file : std::false_type {};
+
+template<class T>
+struct is_file<T, detail::void_t<decltype(
+ std::declval<bool&>() = std::declval<T const&>().is_open(),
+ std::declval<T&>().close(std::declval<error_code&>()),
+ std::declval<T&>().open(
+ std::declval<char const*>(),
+ std::declval<file_mode>(),
+ std::declval<error_code&>()),
+ std::declval<std::uint64_t&>() = std::declval<T&>().size(
+ std::declval<error_code&>()),
+ std::declval<std::uint64_t&>() = std::declval<T&>().pos(
+ std::declval<error_code&>()),
+ std::declval<T&>().seek(
+ std::declval<std::uint64_t>(),
+ std::declval<error_code&>()),
+ std::declval<std::size_t&>() = std::declval<T&>().read(
+ std::declval<void*>(),
+ std::declval<std::size_t>(),
+ std::declval<error_code&>()),
+ std::declval<std::size_t&>() = std::declval<T&>().write(
+ std::declval<void const*>(),
+ std::declval<std::size_t>(),
+ std::declval<error_code&>()),
+ (void)0)>> : std::integral_constant<bool,
+ std::is_default_constructible<T>::value &&
+ std::is_destructible<T>::value
+ > {};
+#endif
+
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http.hpp b/boost/beast/http.hpp
new file mode 100644
index 0000000000..c419724db2
--- /dev/null
+++ b/boost/beast/http.hpp
@@ -0,0 +1,38 @@
+//
+// 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_HTTP_HPP
+#define BOOST_BEAST_HTTP_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+
+#include <boost/beast/http/basic_dynamic_body.hpp>
+#include <boost/beast/http/basic_parser.hpp>
+#include <boost/beast/http/buffer_body.hpp>
+#include <boost/beast/http/chunk_encode.hpp>
+#include <boost/beast/http/dynamic_body.hpp>
+#include <boost/beast/http/empty_body.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/field.hpp>
+#include <boost/beast/http/fields.hpp>
+#include <boost/beast/http/file_body.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/parser.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#include <boost/beast/http/serializer.hpp>
+#include <boost/beast/http/span_body.hpp>
+#include <boost/beast/http/status.hpp>
+#include <boost/beast/http/string_body.hpp>
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/beast/http/vector_body.hpp>
+#include <boost/beast/http/verb.hpp>
+#include <boost/beast/http/write.hpp>
+
+#endif
diff --git a/boost/beast/http/basic_dynamic_body.hpp b/boost/beast/http/basic_dynamic_body.hpp
new file mode 100644
index 0000000000..76c499c554
--- /dev/null
+++ b/boost/beast/http/basic_dynamic_body.hpp
@@ -0,0 +1,169 @@
+//
+// 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_HTTP_BASIC_DYNAMIC_BODY_HPP
+#define BOOST_BEAST_HTTP_BASIC_DYNAMIC_BODY_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
+#include <cstdint>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A @b Body using a @b DynamicBuffer
+
+ This body uses a @b DynamicBuffer as a memory-based container
+ for holding message payloads. Messages using this body type
+ may be serialized and parsed.
+*/
+template<class DynamicBuffer>
+struct basic_dynamic_body
+{
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+
+ /** The type of container used for the body
+
+ This determines the type of @ref message::body
+ when this body type is used with a message container.
+ */
+ using value_type = DynamicBuffer;
+
+ /** Returns the payload size of the body
+
+ When this body is used with @ref message::prepare_payload,
+ the Content-Length will be set to the payload size, and
+ any chunked Transfer-Encoding will be removed.
+ */
+ static
+ std::uint64_t
+ size(value_type const& v)
+ {
+ return v.size();
+ }
+
+ /** The algorithm for parsing the body
+
+ Meets the requirements of @b BodyReader.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using reader = implementation_defined;
+#else
+ class reader
+ {
+ value_type& body_;
+
+ public:
+ template<bool isRequest, class Fields>
+ explicit
+ reader(message<isRequest,
+ basic_dynamic_body, Fields>& msg)
+ : body_(msg.body())
+ {
+ }
+
+ void
+ init(boost::optional<
+ std::uint64_t> const&, error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers,
+ error_code& ec)
+ {
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ auto const n = buffer_size(buffers);
+ if(body_.size() > body_.max_size() - n)
+ {
+ ec = error::buffer_overflow;
+ return 0;
+ }
+ boost::optional<typename
+ DynamicBuffer::mutable_buffers_type> b;
+ try
+ {
+ b.emplace(body_.prepare((std::min)(n,
+ body_.max_size() - body_.size())));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ auto const bytes_transferred =
+ buffer_copy(*b, buffers);
+ body_.commit(bytes_transferred);
+ return bytes_transferred;
+ }
+
+ void
+ finish(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+ };
+#endif
+
+ /** The algorithm for serializing the body
+
+ Meets the requirements of @b BodyWriter.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using writer = implementation_defined;
+#else
+ class writer
+ {
+ DynamicBuffer const& body_;
+
+ public:
+ using const_buffers_type =
+ typename DynamicBuffer::const_buffers_type;
+
+ template<bool isRequest, class Fields>
+ explicit
+ writer(message<isRequest,
+ basic_dynamic_body, Fields> const& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ boost::optional<std::pair<const_buffers_type, bool>>
+ get(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ return {{body_.data(), false}};
+ }
+ };
+#endif
+};
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/basic_file_body.hpp b/boost/beast/http/basic_file_body.hpp
new file mode 100644
index 0000000000..cec72a8f54
--- /dev/null
+++ b/boost/beast/http/basic_file_body.hpp
@@ -0,0 +1,538 @@
+//
+// 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_HTTP_BASIC_FILE_BODY_HPP
+#define BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/file_base.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/assert.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
+#include <cstdio>
+#include <cstdint>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+//[example_http_file_body_1
+
+/** A message body represented by a file on the filesystem.
+
+ Messages with this type have bodies represented by a
+ file on the file system. When parsing a message using
+ this body type, the data is stored in the file pointed
+ to by the path, which must be writable. When serializing,
+ the implementation will read the file and present those
+ octets as the body content. This may be used to serve
+ content from a directory as part of a web service.
+
+ @tparam File The implementation to use for accessing files.
+ This type must meet the requirements of @b File.
+*/
+template<class File>
+struct basic_file_body
+{
+ // Make sure the type meets the requirements
+ static_assert(is_file<File>::value,
+ "File requirements not met");
+
+ /// The type of File this body uses
+ using file_type = File;
+
+ // Algorithm for storing buffers when parsing.
+ class reader;
+
+ // Algorithm for retrieving buffers when serializing.
+ class writer;
+
+ // The type of the @ref message::body member.
+ class value_type;
+
+ /** Returns the size of the body
+
+ @param body The file body to use
+ */
+ static
+ std::uint64_t
+ size(value_type const& body);
+};
+
+//]
+
+//[example_http_file_body_2
+
+/** The type of the @ref message::body member.
+
+ Messages declared using `basic_file_body` will have this type for
+ the body member. This rich class interface allow the file to be
+ opened with the file handle maintained directly in the object,
+ which is attached to the message.
+*/
+template<class File>
+class basic_file_body<File>::value_type
+{
+ // This body container holds a handle to the file
+ // when it is open, and also caches the size when set.
+
+ friend class reader;
+ friend class writer;
+ friend struct basic_file_body;
+
+ // This represents the open file
+ File file_;
+
+ // The cached file size
+ std::uint64_t file_size_ = 0;
+
+public:
+ /** Destructor.
+
+ If the file is open, it is closed first.
+ */
+ ~value_type() = default;
+
+ /// Constructor
+ value_type() = default;
+
+ /// Constructor
+ value_type(value_type&& other) = default;
+
+ /// Move assignment
+ value_type& operator=(value_type&& other) = default;
+
+ /// Returns `true` if the file is open
+ bool
+ is_open() const
+ {
+ return file_.is_open();
+ }
+
+ /// Returns the size of the file if open
+ std::uint64_t
+ size() const
+ {
+ return file_size_;
+ }
+
+ /// Close the file if open
+ void
+ close();
+
+ /** Open a file at the given path with the specified mode
+
+ @param path The utf-8 encoded path to the file
+
+ @param mode The file mode to use
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ open(char const* path, file_mode mode, error_code& ec);
+
+ /** Set the open file
+
+ This function is used to set the open file. Any previously
+ set file will be closed.
+
+ @param file The file to set. The file must be open or else
+ an error occurs
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ reset(File&& file, error_code& ec);
+};
+
+template<class File>
+void
+basic_file_body<File>::
+value_type::
+close()
+{
+ error_code ignored;
+ file_.close(ignored);
+}
+
+template<class File>
+void
+basic_file_body<File>::
+value_type::
+open(char const* path, file_mode mode, error_code& ec)
+{
+ // Open the file
+ file_.open(path, mode, ec);
+ if(ec)
+ return;
+
+ // Cache the size
+ file_size_ = file_.size(ec);
+ if(ec)
+ {
+ close();
+ return;
+ }
+}
+
+template<class File>
+void
+basic_file_body<File>::
+value_type::
+reset(File&& file, error_code& ec)
+{
+ // First close the file if open
+ if(file_.is_open())
+ {
+ error_code ignored;
+ file_.close(ignored);
+ }
+
+ // Take ownership of the new file
+ file_ = std::move(file);
+
+ // Cache the size
+ file_size_ = file_.size(ec);
+}
+
+// This is called from message::payload_size
+template<class File>
+std::uint64_t
+basic_file_body<File>::
+size(value_type const& body)
+{
+ // Forward the call to the body
+ return body.size();
+}
+
+//]
+
+//[example_http_file_body_3
+
+/** Algorithm for retrieving buffers when serializing.
+
+ Objects of this type are created during serialization
+ to extract the buffers representing the body.
+*/
+template<class File>
+class basic_file_body<File>::writer
+{
+ value_type& body_; // The body we are reading from
+ std::uint64_t remain_; // The number of unread bytes
+ char buf_[4096]; // Small buffer for reading
+
+public:
+ // The type of buffer sequence returned by `get`.
+ //
+ using const_buffers_type =
+ boost::asio::const_buffer;
+
+ // Constructor.
+ //
+ // `m` holds the message we are serializing, which will
+ // always have the `basic_file_body` as the body type.
+ //
+ // Note that the message is passed by non-const reference.
+ // This is intentional, because reading from the file
+ // changes its "current position" which counts makes the
+ // operation logically not-const (although it is bitwise
+ // const).
+ //
+ // The BodyWriter concept allows the writer to choose
+ // whether to take the message by const reference or
+ // non-const reference. Depending on the choice, a
+ // serializer constructed using that body type will
+ // require the same const or non-const reference to
+ // construct.
+ //
+ // Readers which accept const messages usually allow
+ // the same body to be serialized by multiple threads
+ // concurrently, while readers accepting non-const
+ // messages may only be serialized by one thread at
+ // a time.
+ //
+ template<bool isRequest, class Fields>
+ writer(message<
+ isRequest, basic_file_body, Fields>& m);
+
+ // Initializer
+ //
+ // This is called before the body is serialized and
+ // gives the writer a chance to do something that might
+ // need to return an error code.
+ //
+ void
+ init(error_code& ec);
+
+ // This function is called zero or more times to
+ // retrieve buffers. A return value of `boost::none`
+ // means there are no more buffers. Otherwise,
+ // the contained pair will have the next buffer
+ // to serialize, and a `bool` indicating whether
+ // or not there may be additional buffers.
+ boost::optional<std::pair<const_buffers_type, bool>>
+ get(error_code& ec);
+};
+
+//]
+
+//[example_http_file_body_4
+
+// Here we just stash a reference to the path for later.
+// Rather than dealing with messy constructor exceptions,
+// we save the things that might fail for the call to `init`.
+//
+template<class File>
+template<bool isRequest, class Fields>
+basic_file_body<File>::
+writer::
+writer(message<isRequest, basic_file_body, Fields>& m)
+ : body_(m.body())
+{
+ // The file must already be open
+ BOOST_ASSERT(body_.file_.is_open());
+
+ // Get the size of the file
+ remain_ = body_.file_size_;
+}
+
+// Initializer
+template<class File>
+void
+basic_file_body<File>::
+writer::
+init(error_code& ec)
+{
+ // The error_code specification requires that we
+ // either set the error to some value, or set it
+ // to indicate no error.
+ //
+ // We don't do anything fancy so set "no error"
+ ec.assign(0, ec.category());
+}
+
+// This function is called repeatedly by the serializer to
+// retrieve the buffers representing the body. Our strategy
+// is to read into our buffer and return it until we have
+// read through the whole file.
+//
+template<class File>
+auto
+basic_file_body<File>::
+writer::
+get(error_code& ec) ->
+ boost::optional<std::pair<const_buffers_type, bool>>
+{
+ // Calculate the smaller of our buffer size,
+ // or the amount of unread data in the file.
+ auto const amount = remain_ > sizeof(buf_) ?
+ sizeof(buf_) : static_cast<std::size_t>(remain_);
+
+ // Handle the case where the file is zero length
+ if(amount == 0)
+ {
+ // Modify the error code to indicate success
+ // This is required by the error_code specification.
+ //
+ // NOTE We use the existing category instead of calling
+ // into the library to get the generic category because
+ // that saves us a possibly expensive atomic operation.
+ //
+ ec.assign(0, ec.category());
+ return boost::none;
+ }
+
+ // Now read the next buffer
+ auto const nread = body_.file_.read(buf_, amount, ec);
+ if(ec)
+ return boost::none;
+
+ // Make sure there is forward progress
+ BOOST_ASSERT(nread != 0);
+ BOOST_ASSERT(nread <= remain_);
+
+ // Update the amount remaining based on what we got
+ remain_ -= nread;
+
+ // Return the buffer to the caller.
+ //
+ // The second element of the pair indicates whether or
+ // not there is more data. As long as there is some
+ // unread bytes, there will be more data. Otherwise,
+ // we set this bool to `false` so we will not be called
+ // again.
+ //
+ ec.assign(0, ec.category());
+ return {{
+ const_buffers_type{buf_, nread}, // buffer to return.
+ remain_ > 0 // `true` if there are more buffers.
+ }};
+}
+
+//]
+
+//[example_http_file_body_5
+
+/** Algorithm for storing buffers when parsing.
+
+ Objects of this type are created during parsing
+ to store incoming buffers representing the body.
+*/
+template<class File>
+class basic_file_body<File>::reader
+{
+ value_type& body_; // The body we are writing to
+
+public:
+ // Constructor.
+ //
+ // This is called after the header is parsed and
+ // indicates that a non-zero sized body may be present.
+ // `m` holds the message we are receiving, which will
+ // always have the `basic_file_body` as the body type.
+ //
+ template<bool isRequest, class Fields>
+ explicit
+ reader(
+ message<isRequest, basic_file_body, Fields>& m);
+
+ // Initializer
+ //
+ // This is called before the body is parsed and
+ // gives the reader a chance to do something that might
+ // need to return an error code. It informs us of
+ // the payload size (`content_length`) which we can
+ // optionally use for optimization.
+ //
+ void
+ init(boost::optional<std::uint64_t> const&, error_code& ec);
+
+ // This function is called one or more times to store
+ // buffer sequences corresponding to the incoming body.
+ //
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers,
+ error_code& ec);
+
+ // This function is called when writing is complete.
+ // It is an opportunity to perform any final actions
+ // which might fail, in order to return an error code.
+ // Operations that might fail should not be attemped in
+ // destructors, since an exception thrown from there
+ // would terminate the program.
+ //
+ void
+ finish(error_code& ec);
+};
+
+//]
+
+//[example_http_file_body_6
+
+// We don't do much in the reader constructor since the
+// file is already open.
+//
+template<class File>
+template<bool isRequest, class Fields>
+basic_file_body<File>::
+reader::
+reader(message<isRequest, basic_file_body, Fields>& m)
+ : body_(m.body())
+{
+}
+
+template<class File>
+void
+basic_file_body<File>::
+reader::
+init(
+ boost::optional<std::uint64_t> const& content_length,
+ error_code& ec)
+{
+ // The file must already be open for writing
+ BOOST_ASSERT(body_.file_.is_open());
+
+ // We don't do anything with this but a sophisticated
+ // application might check available space on the device
+ // to see if there is enough room to store the body.
+ boost::ignore_unused(content_length);
+
+ // The error_code specification requires that we
+ // either set the error to some value, or set it
+ // to indicate no error.
+ //
+ // We don't do anything fancy so set "no error"
+ ec.assign(0, ec.category());
+}
+
+// This will get called one or more times with body buffers
+//
+template<class File>
+template<class ConstBufferSequence>
+std::size_t
+basic_file_body<File>::
+reader::
+put(ConstBufferSequence const& buffers, error_code& ec)
+{
+ // This function must return the total number of
+ // bytes transferred from the input buffers.
+ std::size_t nwritten = 0;
+
+ // Loop over all the buffers in the sequence,
+ // and write each one to the file.
+ for(auto it = boost::asio::buffer_sequence_begin(buffers);
+ it != boost::asio::buffer_sequence_end(buffers); ++it)
+ {
+ // Write this buffer to the file
+ boost::asio::const_buffer buffer = *it;
+ nwritten += body_.file_.write(
+ buffer.data(), buffer.size(), ec);
+ if(ec)
+ return nwritten;
+ }
+
+ // Indicate success
+ // This is required by the error_code specification
+ ec.assign(0, ec.category());
+
+ return nwritten;
+}
+
+// Called after writing is done when there's no error.
+template<class File>
+void
+basic_file_body<File>::
+reader::
+finish(error_code& ec)
+{
+ // This has to be cleared before returning, to
+ // indicate no error. The specification requires it.
+ ec.assign(0, ec.category());
+}
+
+//]
+
+#if ! BOOST_BEAST_DOXYGEN
+// operator<< is not supported for file_body
+template<bool isRequest, class File, class Fields>
+std::ostream&
+operator<<(std::ostream& os, message<
+ isRequest, basic_file_body<File>, Fields> const& msg) = delete;
+#endif
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/basic_parser.hpp b/boost/beast/http/basic_parser.hpp
new file mode 100644
index 0000000000..552adec7e4
--- /dev/null
+++ b/boost/beast/http/basic_parser.hpp
@@ -0,0 +1,621 @@
+//
+// 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_HTTP_BASIC_PARSER_HPP
+#define BOOST_BEAST_HTTP_BASIC_PARSER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/http/field.hpp>
+#include <boost/beast/http/verb.hpp>
+#include <boost/beast/http/detail/basic_parser.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/optional.hpp>
+#include <boost/assert.hpp>
+#include <limits>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A parser for decoding HTTP/1 wire format messages.
+
+ This parser is designed to efficiently parse messages in the
+ HTTP/1 wire format. It allocates no memory when input is
+ presented as a single contiguous buffer, and uses minimal
+ state. It will handle chunked encoding and it understands
+ the semantics of the Connection, Content-Length, and Upgrade
+ fields.
+ The parser is optimized for the case where the input buffer
+ sequence consists of a single contiguous buffer. The
+ @ref flat_buffer class is provided, which guarantees
+ that the input sequence of the stream buffer will be represented
+ by exactly one contiguous buffer. To ensure the optimum performance
+ of the parser, use @ref flat_buffer with HTTP algorithms
+ such as @ref read, @ref read_some, @ref async_read, and @ref async_read_some.
+ Alternatively, the caller may use custom techniques to ensure that
+ the structured portion of the HTTP message (header or chunk header)
+ is contained in a linear buffer.
+
+ The interface uses CRTP (Curiously Recurring Template Pattern).
+ To use this class directly, derive from @ref basic_parser. When
+ bytes are presented, the implementation will make a series of zero
+ or more calls to derived class members functions (termed "callbacks"
+ in this context) matching a specific signature.
+
+ Every callback must be provided by the derived class, or else
+ a compilation error will be generated. This exemplar shows
+ the signature and description of the callbacks required in
+ the derived class.
+ For each callback, the function will ensure that `!ec` is `true`
+ if there was no error or set to the appropriate error code if
+ there was one. If an error is set, the value is propagated to
+ the caller of the parser.
+
+ @par Derived Class Requirements
+ @code
+ template<bool isRequest>
+ class derived
+ : public basic_parser<isRequest, derived<isRequest>>
+ {
+ private:
+ // The friend declaration is needed,
+ // otherwise the callbacks must be made public.
+ friend class basic_parser<isRequest, derived>;
+
+ /// Called after receiving the request-line (isRequest == true).
+ void
+ on_request_impl(
+ verb method, // The method verb, verb::unknown if no match
+ string_view method_str, // The method as a string
+ string_view target, // The request-target
+ int version, // The HTTP-version
+ error_code& ec); // The error returned to the caller, if any
+
+ /// Called after receiving the start-line (isRequest == false).
+ void
+ on_response_impl(
+ int code, // The status-code
+ string_view reason, // The obsolete reason-phrase
+ int version, // The HTTP-version
+ error_code& ec); // The error returned to the caller, if any
+
+ /// Called after receiving a header field.
+ void
+ on_field_impl(
+ field f, // The known-field enumeration constant
+ string_view name, // The field name string.
+ string_view value, // The field value
+ error_code& ec); // The error returned to the caller, if any
+
+ /// Called after the complete header is received.
+ void
+ on_header_impl(
+ error_code& ec); // The error returned to the caller, if any
+
+ /// Called just before processing the body, if a body exists.
+ void
+ on_body_init_impl(
+ boost::optional<
+ std::uint64_t> const&
+ content_length, // Content length if known, else `boost::none`
+ error_code& ec); // The error returned to the caller, if any
+
+ /// Called for each piece of the body, if a body exists.
+ //!
+ //! This is used when there is no chunked transfer coding.
+ //!
+ //! The function returns the number of bytes consumed from the
+ //! input buffer. Any input octets not consumed will be will be
+ //! presented on subsequent calls.
+ //!
+ std::size_t
+ on_body_impl(
+ string_view s, // A portion of the body
+ error_code& ec); // The error returned to the caller, if any
+
+ /// Called for each chunk header.
+ void
+ on_chunk_header_impl(
+ std::uint64_t size, // The size of the upcoming chunk,
+ // or zero for the last chunk
+ string_view extension, // The chunk extensions (may be empty)
+ error_code& ec); // The error returned to the caller, if any
+
+ /// Called to deliver the chunk body.
+ //!
+ //! This is used when there is a chunked transfer coding. The
+ //! implementation will automatically remove the encoding before
+ //! calling this function.
+ //!
+ //! The function returns the number of bytes consumed from the
+ //! input buffer. Any input octets not consumed will be will be
+ //! presented on subsequent calls.
+ //!
+ std::size_t
+ on_chunk_body_impl(
+ std::uint64_t remain, // The number of bytes remaining in the chunk,
+ // including what is being passed here.
+ // or zero for the last chunk
+ string_view body, // The next piece of the chunk body
+ error_code& ec); // The error returned to the caller, if any
+
+ /// Called when the complete message is parsed.
+ void
+ on_finish_impl(error_code& ec);
+
+ public:
+ derived() = default;
+ };
+ @endcode
+
+ @tparam isRequest A `bool` indicating whether the parser will be
+ presented with request or response message.
+
+ @tparam Derived The derived class type. This is part of the
+ Curiously Recurring Template Pattern interface.
+
+ @note If the parser encounters a field value with obs-fold
+ longer than 4 kilobytes in length, an error is generated.
+*/
+template<bool isRequest, class Derived>
+class basic_parser
+ : private detail::basic_parser_base
+{
+ template<bool OtherIsRequest, class OtherDerived>
+ friend class basic_parser;
+
+ // limit on the size of the stack flat buffer
+ static std::size_t constexpr max_stack_buffer = 8192;
+
+ // Message will be complete after reading header
+ static unsigned constexpr flagSkipBody = 1<< 0;
+
+ // Consume input buffers across semantic boundaries
+ static unsigned constexpr flagEager = 1<< 1;
+
+ // The parser has read at least one byte
+ static unsigned constexpr flagGotSome = 1<< 2;
+
+ // Message semantics indicate a body is expected.
+ // cleared if flagSkipBody set
+ //
+ static unsigned constexpr flagHasBody = 1<< 3;
+
+ static unsigned constexpr flagHTTP11 = 1<< 4;
+ static unsigned constexpr flagNeedEOF = 1<< 5;
+ static unsigned constexpr flagExpectCRLF = 1<< 6;
+ static unsigned constexpr flagConnectionClose = 1<< 7;
+ static unsigned constexpr flagConnectionUpgrade = 1<< 8;
+ static unsigned constexpr flagConnectionKeepAlive = 1<< 9;
+ static unsigned constexpr flagContentLength = 1<< 10;
+ static unsigned constexpr flagChunked = 1<< 11;
+ static unsigned constexpr flagUpgrade = 1<< 12;
+ static unsigned constexpr flagFinalChunk = 1<< 13;
+
+ static constexpr
+ std::uint64_t
+ default_body_limit(std::true_type)
+ {
+ // limit for requests
+ return 1 * 1024 * 1024; // 1MB
+ }
+
+ static constexpr
+ std::uint64_t
+ default_body_limit(std::false_type)
+ {
+ // limit for responses
+ return 8 * 1024 * 1024; // 8MB
+ }
+
+ std::uint64_t body_limit_ =
+ default_body_limit(is_request{}); // max payload body
+ std::uint64_t len_ = 0; // size of chunk or body
+ std::unique_ptr<char[]> buf_; // temp storage
+ std::size_t buf_len_ = 0; // size of buf_
+ std::size_t skip_ = 0; // resume search here
+ std::uint32_t header_limit_ = 8192; // max header size
+ unsigned short status_ = 0; // response status
+ state state_ = state::nothing_yet; // initial state
+ unsigned f_ = 0; // flags
+
+protected:
+ /// Default constructor
+ basic_parser() = default;
+
+ /// Move constructor
+ basic_parser(basic_parser &&) = default;
+
+ /// Move assignment
+ basic_parser& operator=(basic_parser &&) = default;
+
+ /** Move constructor
+
+ @note
+
+ After the move, the only valid operation on the
+ moved-from object is destruction.
+ */
+ template<class OtherDerived>
+ basic_parser(basic_parser<isRequest, OtherDerived>&&);
+
+public:
+ /// `true` if this parser parses requests, `false` for responses.
+ using is_request =
+ std::integral_constant<bool, isRequest>;
+
+ /// Destructor
+ ~basic_parser() = default;
+
+ /// Copy constructor
+ basic_parser(basic_parser const&) = delete;
+
+ /// Copy assignment
+ basic_parser& operator=(basic_parser const&) = delete;
+
+ /** Returns a reference to this object as a @ref basic_parser.
+
+ This is used to pass a derived class where a base class is
+ expected, to choose a correct function overload when the
+ resolution would be ambiguous.
+ */
+ basic_parser&
+ base()
+ {
+ return *this;
+ }
+
+ /** Returns a constant reference to this object as a @ref basic_parser.
+
+ This is used to pass a derived class where a base class is
+ expected, to choose a correct function overload when the
+ resolution would be ambiguous.
+ */
+ basic_parser const&
+ base() const
+ {
+ return *this;
+ }
+
+ /// Returns `true` if the parser has received at least one byte of input.
+ bool
+ got_some() const
+ {
+ return state_ != state::nothing_yet;
+ }
+
+ /** Returns `true` if the message is complete.
+
+ The message is complete after the full header is prduced
+ and one of the following is true:
+
+ @li The skip body option was set.
+
+ @li The semantics of the message indicate there is no body.
+
+ @li The semantics of the message indicate a body is expected,
+ and the entire body was parsed.
+ */
+ bool
+ is_done() const
+ {
+ return state_ == state::complete;
+ }
+
+ /** Returns `true` if a the parser has produced the full header.
+ */
+ bool
+ is_header_done() const
+ {
+ return state_ > state::fields;
+ }
+
+ /** Returns `true` if the message is an upgrade message.
+
+ @note The return value is undefined unless
+ @ref is_header_done would return `true`.
+ */
+ bool
+ upgrade() const
+ {
+ return (f_ & flagConnectionUpgrade) != 0;
+ }
+
+ /** Returns `true` if the last value for Transfer-Encoding is "chunked".
+
+ @note The return value is undefined unless
+ @ref is_header_done would return `true`.
+ */
+ bool
+ chunked() const
+ {
+ return (f_ & flagChunked) != 0;
+ }
+
+ /** Returns `true` if the message has keep-alive connection semantics.
+
+ This function always returns `false` if @ref need_eof would return
+ `false`.
+
+ @note The return value is undefined unless
+ @ref is_header_done would return `true`.
+ */
+ bool
+ keep_alive() const;
+
+ /** Returns the optional value of Content-Length if known.
+
+ @note The return value is undefined unless
+ @ref is_header_done would return `true`.
+ */
+ boost::optional<std::uint64_t>
+ content_length() const;
+
+ /** Returns `true` if the message semantics require an end of file.
+
+ Depending on the contents of the header, the parser may
+ require and end of file notification to know where the end
+ of the body lies. If this function returns `true` it will be
+ necessary to call @ref put_eof when there will never be additional
+ data from the input.
+ */
+ bool
+ need_eof() const
+ {
+ return (f_ & flagNeedEOF) != 0;
+ }
+
+ /** Set the limit on the payload body.
+
+ This function sets the maximum allowed size of the payload body,
+ before any encodings except chunked have been removed. Depending
+ on the message semantics, one of these cases will apply:
+
+ @li The Content-Length is specified and exceeds the limit. In
+ this case the result @ref error::body_limit is returned
+ immediately after the header is parsed.
+
+ @li The Content-Length is unspecified and the chunked encoding
+ is not specified as the last encoding. In this case the end of
+ message is determined by the end of file indicator on the
+ associated stream or input source. If a sufficient number of
+ body payload octets are presented to the parser to exceed the
+ configured limit, the parse fails with the result
+ @ref error::body_limit
+
+ @li The Transfer-Encoding specifies the chunked encoding as the
+ last encoding. In this case, when the number of payload body
+ octets produced by removing the chunked encoding exceeds
+ the configured limit, the parse fails with the result
+ @ref error::body_limit.
+
+ Setting the limit after any body octets have been parsed
+ results in undefined behavior.
+
+ The default limit is 1MB for requests and 8MB for responses.
+
+ @param v The payload body limit to set
+ */
+ void
+ body_limit(std::uint64_t v)
+ {
+ body_limit_ = v;
+ }
+
+ /** Set a limit on the total size of the header.
+
+ This function sets the maximum allowed size of the header
+ including all field name, value, and delimiter characters
+ and also including the CRLF sequences in the serialized
+ input. If the end of the header is not found within the
+ limit of the header size, the error @ref error::header_limit
+ is returned by @ref put.
+
+ Setting the limit after any header octets have been parsed
+ results in undefined behavior.
+ */
+ void
+ header_limit(std::uint32_t v)
+ {
+ header_limit_ = v;
+ }
+
+ /// Returns `true` if the eager parse option is set.
+ bool
+ eager() const
+ {
+ return (f_ & flagEager) != 0;
+ }
+
+ /** Set the eager parse option.
+
+ Normally the parser returns after successfully parsing a structured
+ element (header, chunk header, or chunk body) even if there are octets
+ remaining in the input. This is necessary when attempting to parse the
+ header first, or when the caller wants to inspect information which may
+ be invalidated by subsequent parsing, such as a chunk extension. The
+ `eager` option controls whether the parser keeps going after parsing
+ structured element if there are octets remaining in the buffer and no
+ error occurs. This option is automatically set or cleared during certain
+ stream operations to improve performance with no change in functionality.
+
+ The default setting is `false`.
+
+ @param v `true` to set the eager parse option or `false` to disable it.
+ */
+ void
+ eager(bool v)
+ {
+ if(v)
+ f_ |= flagEager;
+ else
+ f_ &= ~flagEager;
+ }
+
+ /// Returns `true` if the skip parse option is set.
+ bool
+ skip() const
+ {
+ return (f_ & flagSkipBody) != 0;
+ }
+
+ /** Set the skip parse option.
+
+ This option controls whether or not the parser expects to see an HTTP
+ body, regardless of the presence or absence of certain fields such as
+ Content-Length or a chunked Transfer-Encoding. Depending on the request,
+ some responses do not carry a body. For example, a 200 response to a
+ CONNECT request from a tunneling proxy, or a response to a HEAD request.
+ In these cases, callers may use this function inform the parser that
+ no body is expected. The parser will consider the message complete
+ after the header has been received.
+
+ @param v `true` to set the skip body option or `false` to disable it.
+
+ @note This function must called before any bytes are processed.
+ */
+ void
+ skip(bool v);
+
+ /** Write a buffer sequence to the parser.
+
+ This function attempts to incrementally parse the HTTP
+ message data stored in the caller provided buffers. Upon
+ success, a positive return value indicates that the parser
+ made forward progress, consuming that number of
+ bytes.
+
+ In some cases there may be an insufficient number of octets
+ in the input buffer in order to make forward progress. This
+ is indicated by the code @ref error::need_more. When
+ this happens, the caller should place additional bytes into
+ the buffer sequence and call @ref put again.
+
+ The error code @ref error::need_more is special. When this
+ error is returned, a subsequent call to @ref put may succeed
+ if the buffers have been updated. Otherwise, upon error
+ the parser may not be restarted.
+
+ @param buffers An object meeting the requirements of
+ @b ConstBufferSequence that represents the next chunk of
+ message data. If the length of this buffer sequence is
+ one, the implementation will not allocate additional memory.
+ The class @ref beast::flat_buffer is provided as one way to
+ meet this requirement
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of octets consumed in the buffer
+ sequence. The caller should remove these octets even if the
+ error is set.
+ */
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers, error_code& ec);
+
+#if ! BOOST_BEAST_DOXYGEN
+ std::size_t
+ put(boost::asio::const_buffer const& buffer,
+ error_code& ec);
+#endif
+
+ /** Inform the parser that the end of stream was reached.
+
+ In certain cases, HTTP needs to know where the end of
+ the stream is. For example, sometimes servers send
+ responses without Content-Length and expect the client
+ to consume input (for the body) until EOF. Callbacks
+ and errors will still be processed as usual.
+
+ This is typically called when a read from the
+ underlying stream object sets the error code to
+ `boost::asio::error::eof`.
+
+ @note Only valid after parsing a complete header.
+
+ @param ec Set to the error, if any occurred.
+ */
+ void
+ put_eof(error_code& ec);
+
+private:
+ inline
+ Derived&
+ impl()
+ {
+ return *static_cast<Derived*>(this);
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put_from_stack(std::size_t size,
+ ConstBufferSequence const& buffers,
+ error_code& ec);
+
+ void
+ maybe_need_more(
+ char const* p, std::size_t n,
+ error_code& ec);
+
+ void
+ parse_start_line(
+ char const*& p, char const* last,
+ error_code& ec, std::true_type);
+
+ void
+ parse_start_line(
+ char const*& p, char const* last,
+ error_code& ec, std::false_type);
+
+ void
+ parse_fields(
+ char const*& p, char const* last,
+ error_code& ec);
+
+ void
+ finish_header(
+ error_code& ec, std::true_type);
+
+ void
+ finish_header(
+ error_code& ec, std::false_type);
+
+ void
+ parse_body(char const*& p,
+ std::size_t n, error_code& ec);
+
+ void
+ parse_body_to_eof(char const*& p,
+ std::size_t n, error_code& ec);
+
+ void
+ parse_chunk_header(char const*& p,
+ std::size_t n, error_code& ec);
+
+ void
+ parse_chunk_body(char const*& p,
+ std::size_t n, error_code& ec);
+
+ void
+ do_field(field f,
+ string_view value, error_code& ec);
+};
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/basic_parser.ipp>
+
+#endif
diff --git a/boost/beast/http/buffer_body.hpp b/boost/beast/http/buffer_body.hpp
new file mode 100644
index 0000000000..675ea9475a
--- /dev/null
+++ b/boost/beast/http/buffer_body.hpp
@@ -0,0 +1,228 @@
+//
+// 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_HTTP_BUFFER_BODY_HPP
+#define BOOST_BEAST_HTTP_BUFFER_BODY_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/optional.hpp>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A @b Body using a caller provided buffer
+
+ Messages using this body type may be serialized and parsed.
+ To use this class, the caller must initialize the members
+ of @ref buffer_body::value_type to appropriate values before
+ each call to read or write during a stream operation.
+*/
+struct buffer_body
+{
+ /// The type of the body member when used in a message.
+ struct value_type
+ {
+ /** A pointer to a contiguous area of memory of @ref size octets, else `nullptr`.
+
+ @par When Serializing
+
+ If this is `nullptr` and `more` is `true`, the error
+ @ref error::need_buffer will be returned from @ref serializer::get
+ Otherwise, the serializer will use the memory pointed to
+ by `data` having `size` octets of valid storage as the
+ next buffer representing the body.
+
+ @par When Parsing
+
+ If this is `nullptr`, the error @ref error::need_buffer
+ will be returned from @ref parser::put. Otherwise, the
+ parser will store body octets into the memory pointed to
+ by `data` having `size` octets of valid storage. After
+ octets are stored, the `data` and `size` members are
+ adjusted: `data` is incremented to point to the next
+ octet after the data written, while `size` is decremented
+ to reflect the remaining space at the memory location
+ pointed to by `data`.
+ */
+ void* data = nullptr;
+
+ /** The number of octets in the buffer pointed to by @ref data.
+
+ @par When Serializing
+
+ If `data` is `nullptr` during serialization, this value
+ is ignored. Otherwise, it represents the number of valid
+ body octets pointed to by `data`.
+
+ @par When Parsing
+
+ The value of this field will be decremented during parsing
+ to indicate the number of remaining free octets in the
+ buffer pointed to by `data`. When it reaches zero, the
+ parser will return @ref error::need_buffer, indicating to
+ the caller that the values of `data` and `size` should be
+ updated to point to a new memory buffer.
+ */
+ std::size_t size = 0;
+
+ /** `true` if this is not the last buffer.
+
+ @par When Serializing
+
+ If this is `true` and `data` is `nullptr`, the error
+ @ref error::need_buffer will be returned from @ref serializer::get
+
+ @par When Parsing
+
+ This field is not used during parsing.
+ */
+ bool more = true;
+ };
+
+ /** The algorithm for parsing the body
+
+ Meets the requirements of @b BodyReader.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using reader = implementation_defined;
+#else
+ class reader
+ {
+ value_type& body_;
+
+ public:
+ template<bool isRequest, class Fields>
+ explicit
+ reader(message<isRequest, buffer_body, Fields>& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(boost::optional<std::uint64_t> const&, error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers,
+ error_code& ec)
+ {
+ using boost::asio::buffer_size;
+ using boost::asio::buffer_copy;
+ if(! body_.data)
+ {
+ ec = error::need_buffer;
+ return 0;
+ }
+ auto const bytes_transferred =
+ buffer_copy(boost::asio::buffer(
+ body_.data, body_.size), buffers);
+ body_.data = reinterpret_cast<char*>(
+ body_.data) + bytes_transferred;
+ body_.size -= bytes_transferred;
+ if(bytes_transferred == buffer_size(buffers))
+ ec.assign(0, ec.category());
+ else
+ ec = error::need_buffer;
+ return bytes_transferred;
+ }
+
+ void
+ finish(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+ };
+#endif
+
+ /** The algorithm for serializing the body
+
+ Meets the requirements of @b BodyWriter.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using writer = implementation_defined;
+#else
+ class writer
+ {
+ bool toggle_ = false;
+ value_type const& body_;
+
+ public:
+ using const_buffers_type =
+ boost::asio::const_buffer;
+
+ template<bool isRequest, class Fields>
+ explicit
+ writer(message<isRequest,
+ buffer_body, Fields> const& msg)
+ : body_(msg.body())
+ {
+ }
+
+ void
+ init(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ boost::optional<
+ std::pair<const_buffers_type, bool>>
+ get(error_code& ec)
+ {
+ if(toggle_)
+ {
+ if(body_.more)
+ {
+ toggle_ = false;
+ ec = error::need_buffer;
+ }
+ else
+ {
+ ec.assign(0, ec.category());
+ }
+ return boost::none;
+ }
+ if(body_.data)
+ {
+ ec.assign(0, ec.category());
+ toggle_ = true;
+ return {{const_buffers_type{
+ body_.data, body_.size}, body_.more}};
+ }
+ if(body_.more)
+ ec = error::need_buffer;
+ else
+ ec.assign(0, ec.category());
+ return boost::none;
+ }
+ };
+#endif
+};
+
+#if ! BOOST_BEAST_DOXYGEN
+// operator<< is not supported for buffer_body
+template<bool isRequest, class Fields>
+std::ostream&
+operator<<(std::ostream& os, message<isRequest,
+ buffer_body, Fields> const& msg) = delete;
+#endif
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/chunk_encode.hpp b/boost/beast/http/chunk_encode.hpp
new file mode 100644
index 0000000000..4994765fc8
--- /dev/null
+++ b/boost/beast/http/chunk_encode.hpp
@@ -0,0 +1,736 @@
+//
+// 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_HTTP_CHUNK_ENCODE_HPP
+#define BOOST_BEAST_HTTP_CHUNK_ENCODE_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/beast/http/detail/chunk_encode.hpp>
+#include <boost/asio/buffer.hpp>
+#include <memory>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A chunked encoding crlf
+
+ This implements a @b ConstBufferSequence holding the CRLF
+ (`"\r\n"`) used as a delimiter in a @em chunk.
+
+ To use this class, pass an instance of it to a
+ stream algorithm as the buffer sequence:
+ @code
+ // writes "\r\n"
+ boost::asio::write(stream, chunk_crlf{});
+ @endcode
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+*/
+struct chunk_crlf
+{
+ /// Constructor
+ chunk_crlf() = default;
+
+ //-----
+
+ /// Required for @b ConstBufferSequence
+#if BOOST_BEAST_DOXYGEN
+ using value_type = implementation_defined;
+#else
+ using value_type = detail::chunk_crlf_iter::value_type;
+#endif
+
+ /// Required for @b ConstBufferSequence
+ using const_iterator = value_type const*;
+
+ /// Required for @b ConstBufferSequence
+ chunk_crlf(chunk_crlf const&) = default;
+
+ /// Required for @b ConstBufferSequence
+ const_iterator
+ begin() const
+ {
+ return &detail::chunk_crlf_iter::value;
+ }
+
+ /// Required for @b ConstBufferSequence
+ const_iterator
+ end() const
+ {
+ return begin() + 1;
+ }
+};
+
+//------------------------------------------------------------------------------
+
+/** A @em chunk header
+
+ This implements a @b ConstBufferSequence representing the
+ header of a @em chunk. The serialized format is as follows:
+ @code
+ chunk-header = 1*HEXDIG chunk-ext CRLF
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+ @endcode
+ The chunk extension is optional. After the header and
+ chunk body have been serialized, it is the callers
+ responsibility to also serialize the final CRLF (`"\r\n"`).
+
+ This class allows the caller to emit piecewise chunk bodies,
+ by first serializing the chunk header using this class and then
+ serializing the chunk body in a series of one or more calls to
+ a stream write operation.
+
+ To use this class, pass an instance of it to a
+ stream algorithm as the buffer sequence:
+ @code
+ // writes "400;x\r\n"
+ boost::asio::write(stream, chunk_header{1024, "x"});
+ @endcode
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+*/
+class chunk_header
+{
+ using view_type = buffers_cat_view<
+ detail::chunk_size, // chunk-size
+ boost::asio::const_buffer, // chunk-extensions
+ chunk_crlf>; // CRLF
+
+ std::shared_ptr<
+ detail::chunk_extensions> exts_;
+ view_type view_;
+
+public:
+ /** Constructor
+
+ This constructs a buffer sequence representing a
+ @em chunked-body size and terminating CRLF (`"\r\n"`)
+ with no chunk extensions.
+
+ @param size The size of the chunk body that follows.
+ The value must be greater than zero.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+ */
+ explicit
+ chunk_header(std::size_t size);
+
+ /** Constructor
+
+ This constructs a buffer sequence representing a
+ @em chunked-body size and terminating CRLF (`"\r\n"`)
+ with provided chunk extensions.
+
+ @param size The size of the chunk body that follows.
+ The value must be greater than zero.
+
+ @param extensions The chunk extensions string. This
+ string must be formatted correctly as per rfc7230,
+ using this BNF syntax:
+ @code
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+ @endcode
+ The data pointed to by this string view must remain
+ valid for the lifetime of any operations performed on
+ the object.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1.1
+ */
+ chunk_header(
+ std::size_t size,
+ string_view extensions);
+
+ /** Constructor
+
+ This constructs a buffer sequence representing a
+ @em chunked-body size and terminating CRLF (`"\r\n"`)
+ with provided chunk extensions.
+ The default allocator is used to provide storage for the
+ extensions object.
+
+ @param size The size of the chunk body that follows.
+ The value must be greater than zero.
+
+ @param extensions The chunk extensions object. The expression
+ `extensions.str()` must be valid, and the return type must
+ be convertible to @ref string_view. This object will be copied
+ or moved as needed to ensure that the chunk header object retains
+ ownership of the buffers provided by the chunk extensions object.
+
+ @note This function participates in overload resolution only
+ if @b ChunkExtensions meets the requirements stated above.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+ */
+ template<class ChunkExtensions
+#if ! BOOST_BEAST_DOXYGEN
+ , class = typename std::enable_if<
+ detail::is_chunk_extensions<
+ ChunkExtensions>::value>::type
+#endif
+ >
+ chunk_header(
+ std::size_t size,
+ ChunkExtensions&& extensions);
+
+ /** Constructor
+
+ This constructs a buffer sequence representing a
+ @em chunked-body size and terminating CRLF (`"\r\n"`)
+ with provided chunk extensions.
+ The specified allocator is used to provide storage for the
+ extensions object.
+
+ @param size The size of the chunk body that follows.
+ The value be greater than zero.
+
+ @param extensions The chunk extensions object. The expression
+ `extensions.str()` must be valid, and the return type must
+ be convertible to @ref string_view. This object will be copied
+ or moved as needed to ensure that the chunk header object retains
+ ownership of the buffers provided by the chunk extensions object.
+
+ @param allocator The allocator to provide storage for the moved
+ or copied extensions object.
+
+ @note This function participates in overload resolution only
+ if @b ChunkExtensions meets the requirements stated above.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+ */
+ template<class ChunkExtensions, class Allocator
+#if ! BOOST_BEAST_DOXYGEN
+ , class = typename std::enable_if<
+ detail::is_chunk_extensions<
+ ChunkExtensions>::value>::type
+#endif
+ >
+ chunk_header(
+ std::size_t size,
+ ChunkExtensions&& extensions,
+ Allocator const& allocator);
+
+ //-----
+
+ /// Required for @b ConstBufferSequence
+#if BOOST_BEAST_DOXYGEN
+ using value_type = implementation_defined;
+#else
+ using value_type = typename view_type::value_type;
+#endif
+
+ /// Required for @b ConstBufferSequence
+#if BOOST_BEAST_DOXYGEN
+ using const_iterator = implementation_defined;
+#else
+ using const_iterator = typename view_type::const_iterator;
+#endif
+
+ /// Required for @b ConstBufferSequence
+ chunk_header(chunk_header const&) = default;
+
+ /// Required for @b ConstBufferSequence
+ const_iterator
+ begin() const
+ {
+ return view_.begin();
+ }
+
+ /// Required for @b ConstBufferSequence
+ const_iterator
+ end() const
+ {
+ return view_.end();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+/** A @em chunk
+
+ This implements a @b ConstBufferSequence representing
+ a @em chunk. The serialized format is as follows:
+ @code
+ chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
+ chunk-size = 1*HEXDIG
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+ chunk-data = 1*OCTET ; a sequence of chunk-size octets
+ @endcode
+ The chunk extension is optional.
+
+ To use this class, pass an instance of it to a
+ stream algorithm as the buffer sequence.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+*/
+template<class ConstBufferSequence>
+class chunk_body
+{
+ using view_type = buffers_cat_view<
+ detail::chunk_size, // chunk-size
+ boost::asio::const_buffer, // chunk-extensions
+ chunk_crlf, // CRLF
+ ConstBufferSequence, // chunk-body
+ chunk_crlf>; // CRLF
+
+ std::shared_ptr<
+ detail::chunk_extensions> exts_;
+ view_type view_;
+
+public:
+ /** Constructor
+
+ This constructs buffers representing a complete @em chunk
+ with no chunk extensions and having the size and contents
+ of the specified buffer sequence.
+
+ @param buffers A buffer sequence representing the chunk
+ body. Although the buffers object may be copied as necessary,
+ ownership of the underlying memory blocks is retained by the
+ caller, which must guarantee that they remain valid while this
+ object is in use.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+ */
+ explicit
+ chunk_body(
+ ConstBufferSequence const& buffers);
+
+ /** Constructor
+
+ This constructs buffers representing a complete @em chunk
+ with the passed chunk extensions and having the size and
+ contents of the specified buffer sequence.
+
+ @param buffers A buffer sequence representing the chunk
+ body. Although the buffers object may be copied as necessary,
+ ownership of the underlying memory blocks is retained by the
+ caller, which must guarantee that they remain valid while this
+ object is in use.
+
+ @param extensions The chunk extensions string. This
+ string must be formatted correctly as per rfc7230,
+ using this BNF syntax:
+ @code
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+ @endcode
+ The data pointed to by this string view must remain
+ valid for the lifetime of any operations performed on
+ the object.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1.1
+ */
+ chunk_body(
+ ConstBufferSequence const& buffers,
+ string_view extensions);
+
+ /** Constructor
+
+ This constructs buffers representing a complete @em chunk
+ with the passed chunk extensions and having the size and
+ contents of the specified buffer sequence.
+ The default allocator is used to provide storage for the
+ extensions object.
+
+ @param buffers A buffer sequence representing the chunk
+ body. Although the buffers object may be copied as necessary,
+ ownership of the underlying memory blocks is retained by the
+ caller, which must guarantee that they remain valid while this
+ object is in use.
+
+ @param extensions The chunk extensions object. The expression
+ `extensions.str()` must be valid, and the return type must
+ be convertible to @ref string_view. This object will be copied
+ or moved as needed to ensure that the chunk header object retains
+ ownership of the buffers provided by the chunk extensions object.
+
+ @note This function participates in overload resolution only
+ if @b ChunkExtensions meets the requirements stated above.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+ */
+ template<class ChunkExtensions
+#if ! BOOST_BEAST_DOXYGEN
+ , class = typename std::enable_if<
+ ! std::is_convertible<typename std::decay<
+ ChunkExtensions>::type, string_view>::value>::type
+#endif
+ >
+ chunk_body(
+ ConstBufferSequence const& buffers,
+ ChunkExtensions&& extensions);
+
+ /** Constructor
+
+ This constructs buffers representing a complete @em chunk
+ with the passed chunk extensions and having the size and
+ contents of the specified buffer sequence.
+ The specified allocator is used to provide storage for the
+ extensions object.
+
+ @param buffers A buffer sequence representing the chunk
+ body. Although the buffers object may be copied as necessary,
+ ownership of the underlying memory blocks is retained by the
+ caller, which must guarantee that they remain valid while this
+ object is in use.
+
+ @param extensions The chunk extensions object. The expression
+ `extensions.str()` must be valid, and the return type must
+ be convertible to @ref string_view. This object will be copied
+ or moved as needed to ensure that the chunk header object retains
+ ownership of the buffers provided by the chunk extensions object.
+
+ @param allocator The allocator to provide storage for the moved
+ or copied extensions object.
+
+ @note This function participates in overload resolution only
+ if @b ChunkExtensions meets the requirements stated above.
+
+ @see https://tools.ietf.org/html/rfc7230#section-4.1
+ */
+ template<class ChunkExtensions, class Allocator
+#if ! BOOST_BEAST_DOXYGEN
+ , class = typename std::enable_if<
+ ! std::is_convertible<typename std::decay<
+ ChunkExtensions>::type, string_view>::value>::type
+#endif
+ >
+ chunk_body(
+ ConstBufferSequence const& buffers,
+ ChunkExtensions&& extensions,
+ Allocator const& allocator);
+
+ //-----
+
+ /// Required for @b ConstBufferSequence
+#if BOOST_BEAST_DOXYGEN
+ using value_type = implementation_defined;
+#else
+ using value_type = typename view_type::value_type;
+#endif
+
+ /// Required for @b ConstBufferSequence
+#if BOOST_BEAST_DOXYGEN
+ using const_iterator = implementation_defined;
+#else
+ using const_iterator = typename view_type::const_iterator;
+#endif
+
+ /// Required for @b ConstBufferSequence
+ const_iterator
+ begin() const
+ {
+ return view_.begin();
+ }
+
+ /// Required for @b ConstBufferSequence
+ const_iterator
+ end() const
+ {
+ return view_.end();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+/** A chunked-encoding last chunk
+*/
+template<class Trailer = chunk_crlf>
+class chunk_last
+{
+ static_assert(
+ is_fields<Trailer>::value ||
+ boost::asio::is_const_buffer_sequence<Trailer>::value,
+ "Trailer requirements not met");
+
+ using buffers_type = typename
+ detail::buffers_or_fields<Trailer>::type;
+
+ using view_type =
+ buffers_cat_view<
+ detail::chunk_size0, // "0\r\n"
+ buffers_type>; // Trailer (includes CRLF)
+
+ template<class Allocator>
+ buffers_type
+ prepare(Trailer const& trailer, Allocator const& alloc);
+
+ buffers_type
+ prepare(Trailer const& trailer, std::true_type);
+
+ buffers_type
+ prepare(Trailer const& trailer, std::false_type);
+
+ std::shared_ptr<void> sp_;
+ view_type view_;
+
+public:
+ /** Constructor
+
+ The last chunk will have an empty trailer
+ */
+ chunk_last();
+
+ /** Constructor
+
+ @param trailer The trailer to use. This may be
+ a type meeting the requirements of either Fields
+ or ConstBufferSequence. If it is a ConstBufferSequence,
+ the trailer must be formatted correctly as per rfc7230
+ including a CRLF on its own line to denote the end
+ of the trailer.
+ */
+ explicit
+ chunk_last(Trailer const& trailer);
+
+ /** Constructor
+
+ @param trailer The trailer to use. This type must
+ meet the requirements of Fields.
+
+ @param allocator The allocator to use for storing temporary
+ data associated with the serialized trailer buffers.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class Allocator>
+ chunk_last(Trailer const& trailer, Allocator const& allocator);
+#else
+ template<class DeducedTrailer, class Allocator,
+ class = typename std::enable_if<
+ is_fields<DeducedTrailer>::value>::type>
+ chunk_last(
+ DeducedTrailer const& trailer, Allocator const& allocator);
+#endif
+
+ //-----
+
+ /// Required for @b ConstBufferSequence
+ chunk_last(chunk_last const&) = default;
+
+ /// Required for @b ConstBufferSequence
+#if BOOST_BEAST_DOXYGEN
+ using value_type = implementation_defined;
+#else
+ using value_type =
+ typename view_type::value_type;
+#endif
+
+ /// Required for @b ConstBufferSequence
+#if BOOST_BEAST_DOXYGEN
+ using const_iterator = implementation_defined;
+#else
+ using const_iterator =
+ typename view_type::const_iterator;
+#endif
+
+ /// Required for @b ConstBufferSequence
+ const_iterator
+ begin() const
+ {
+ return view_.begin();
+ }
+
+ /// Required for @b ConstBufferSequence
+ const_iterator
+ end() const
+ {
+ return view_.end();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+/** A set of chunk extensions
+
+ This container stores a set of chunk extensions suited for use with
+ @ref chunk_header and @ref chunk_body. The container may be iterated
+ to access the extensions in their structured form.
+
+ Meets the requirements of ChunkExtensions
+*/
+template<class Allocator>
+class basic_chunk_extensions
+{
+ std::basic_string<char,
+ std::char_traits<char>, Allocator> s_;
+
+ std::basic_string<char,
+ std::char_traits<char>, Allocator> range_;
+
+ template<class FwdIt>
+ FwdIt
+ do_parse(FwdIt it, FwdIt last, error_code& ec);
+
+ void
+ do_insert(string_view name, string_view value);
+
+public:
+ /** The type of value when iterating.
+
+ The first element of the pair is the name, and the second
+ element is the value which may be empty. The value is
+ stored in its raw representation, without quotes or escapes.
+ */
+ using value_type = std::pair<string_view, string_view>;
+
+ class const_iterator;
+
+ /// Constructor
+ basic_chunk_extensions() = default;
+
+ /// Constructor
+ basic_chunk_extensions(basic_chunk_extensions&&) = default;
+
+ /// Constructor
+ basic_chunk_extensions(basic_chunk_extensions const&) = default;
+
+ /** Constructor
+
+ @param allocator The allocator to use for storing the serialized extension
+ */
+ explicit
+ basic_chunk_extensions(Allocator const& allocator)
+ : s_(allocator)
+ {
+ }
+
+ /** Clear the chunk extensions
+
+ This preserves the capacity of the internal string
+ used to hold the serialized representation.
+ */
+ void
+ clear()
+ {
+ s_.clear();
+ }
+
+ /** Parse a set of chunk extensions
+
+ Any previous extensions will be cleared
+ */
+ void
+ parse(string_view s, error_code& ec);
+
+ /** Insert an extension name with an empty value
+
+ @param name The name of the extension
+ */
+ void
+ insert(string_view name);
+
+ /** Insert an extension value
+
+ @param name The name of the extension
+
+ @param value The value to insert. Depending on the
+ contents, the serialized extension may use a quoted string.
+ */
+ void
+ insert(string_view name, string_view value);
+
+ /// Return the serialized representation of the chunk extension
+ string_view
+ str() const
+ {
+ return s_;
+ }
+
+ const_iterator
+ begin() const;
+
+ const_iterator
+ end() const;
+};
+
+//------------------------------------------------------------------------------
+
+/// A set of chunk extensions
+using chunk_extensions =
+ basic_chunk_extensions<std::allocator<char>>;
+
+/** Returns a @ref chunk_body
+
+ This functions constructs and returns a complete
+ @ref chunk_body for a chunk body represented by the
+ specified buffer sequence.
+
+ @param buffers The buffers representing the chunk body.
+
+ @param args Optional arguments passed to the @ref chunk_body constructor.
+
+ @note This function is provided as a notational convenience
+ to omit specification of the class template arguments.
+*/
+template<class ConstBufferSequence, class... Args>
+auto
+make_chunk(
+ ConstBufferSequence const& buffers,
+ Args&&... args) ->
+ chunk_body<ConstBufferSequence>
+{
+ return chunk_body<ConstBufferSequence>(
+ buffers, std::forward<Args>(args)...);
+}
+
+/** Returns a @ref chunk_last
+
+ @note This function is provided as a notational convenience
+ to omit specification of the class template arguments.
+*/
+inline
+chunk_last<chunk_crlf>
+make_chunk_last()
+{
+ return chunk_last<chunk_crlf>{};
+}
+
+/** Returns a @ref chunk_last
+
+ This function construct and returns a complete
+ @ref chunk_last for a last chunk containing the
+ specified trailers.
+
+ @param trailer A ConstBufferSequence or
+ @note This function is provided as a notational convenience
+ to omit specification of the class template arguments.
+
+ @param args Optional arguments passed to the @ref chunk_last
+ constructor.
+*/
+template<class Trailer, class... Args>
+chunk_last<Trailer>
+make_chunk_last(
+ Trailer const& trailer,
+ Args&&... args)
+{
+ return chunk_last<Trailer>{
+ trailer, std::forward<Args>(args)...};
+}
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/chunk_encode.ipp>
+
+#endif
diff --git a/boost/beast/http/detail/basic_parsed_list.hpp b/boost/beast/http/detail/basic_parsed_list.hpp
new file mode 100644
index 0000000000..d1e1565252
--- /dev/null
+++ b/boost/beast/http/detail/basic_parsed_list.hpp
@@ -0,0 +1,198 @@
+//
+// 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_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
+#define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
+
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/detail/empty_base_optimization.hpp>
+#include <cstddef>
+#include <iterator>
+
+namespace boost {
+namespace beast {
+namespace http {
+namespace detail {
+
+/** A list parser which presents the sequence as a container.
+*/
+template<class Policy>
+class basic_parsed_list
+{
+ string_view s_;
+
+public:
+ /// The type of policy this list uses for parsing.
+ using policy_type = Policy;
+
+ /// The type of each element in the list.
+ using value_type = typename Policy::value_type;
+
+ /// A constant iterator to a list element.
+#if BOOST_BEAST_DOXYGEN
+ using const_iterator = implementation_defined;
+#else
+ class const_iterator;
+#endif
+
+ class const_iterator
+ : private beast::detail::
+ empty_base_optimization<Policy>
+ {
+ basic_parsed_list const* list_ = nullptr;
+ char const* it_ = nullptr;
+ typename Policy::value_type v_;
+ bool error_ = false;
+
+ public:
+ using value_type =
+ typename Policy::value_type;
+ using reference = value_type const&;
+ using pointer = value_type const*;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::forward_iterator_tag;
+
+ const_iterator() = default;
+
+ bool
+ operator==(
+ const_iterator const& other) const
+ {
+ return
+ other.list_ == list_ &&
+ other.it_ == it_;
+ }
+
+ bool
+ operator!=(
+ const_iterator const& other) const
+ {
+ return ! (*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return v_;
+ }
+
+ const_iterator&
+ operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ bool
+ error() const
+ {
+ return error_;
+ }
+
+ private:
+ friend class basic_parsed_list;
+
+ const_iterator(
+ basic_parsed_list const& list, bool at_end)
+ : list_(&list)
+ , it_(at_end ? nullptr :
+ list.s_.begin())
+ {
+ if(! at_end)
+ increment();
+ }
+
+ void
+ increment()
+ {
+ if(! this->member()(
+ v_, it_, list_->s_))
+ {
+ it_ = nullptr;
+ error_ = true;
+ }
+ }
+ };
+
+ /// Construct a list from a string
+ explicit
+ basic_parsed_list(string_view s)
+ : s_(s)
+ {
+ }
+
+ /// Return a const iterator to the beginning of the list
+ const_iterator begin() const;
+
+ /// Return a const iterator to the end of the list
+ const_iterator end() const;
+
+ /// Return a const iterator to the beginning of the list
+ const_iterator cbegin() const;
+
+ /// Return a const iterator to the end of the list
+ const_iterator cend() const;
+};
+
+template<class Policy>
+inline
+auto
+basic_parsed_list<Policy>::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{*this, false};
+}
+
+template<class Policy>
+inline
+auto
+basic_parsed_list<Policy>::
+end() const ->
+ const_iterator
+{
+ return const_iterator{*this, true};
+}
+
+template<class Policy>
+inline
+auto
+basic_parsed_list<Policy>::
+cbegin() const ->
+ const_iterator
+{
+ return const_iterator{*this, false};
+}
+
+template<class Policy>
+inline
+auto
+basic_parsed_list<Policy>::
+cend() const ->
+ const_iterator
+{
+ return const_iterator{*this, true};
+}
+
+} // detail
+} // http
+} // beast
+} // boost
+
+#endif
+
diff --git a/boost/beast/http/detail/basic_parser.hpp b/boost/beast/http/detail/basic_parser.hpp
new file mode 100644
index 0000000000..0936862add
--- /dev/null
+++ b/boost/beast/http/detail/basic_parser.hpp
@@ -0,0 +1,896 @@
+//
+// 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_HTTP_DETAIL_BASIC_PARSER_HPP
+#define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
+
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/detail/cpu_info.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/detail/rfc7230.hpp>
+#include <boost/config.hpp>
+#include <boost/version.hpp>
+#include <algorithm>
+#include <cstddef>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+namespace detail {
+
+class basic_parser_base
+{
+protected:
+ // limit on the size of the obs-fold buffer
+ //
+ // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
+ //
+ static std::size_t constexpr max_obs_fold = 4096;
+
+ enum class state
+ {
+ nothing_yet = 0,
+ start_line,
+ fields,
+ body0,
+ body,
+ body_to_eof0,
+ body_to_eof,
+ chunk_header0,
+ chunk_header,
+ chunk_body,
+ complete
+ };
+
+ static
+ bool
+ is_pathchar(char c)
+ {
+ // VFALCO This looks the same as the one below...
+
+ // TEXT = <any OCTET except CTLs, and excluding LWS>
+ static bool constexpr tab[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
+ };
+ return tab[static_cast<unsigned char>(c)];
+ }
+
+ static
+ inline
+ bool
+ unhex(unsigned char& d, char c)
+ {
+ static signed char constexpr tab[256] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48
+ -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80
+ -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 112
+
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 128
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 144
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 160
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 176
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 192
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 208
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 224
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240
+ };
+ d = static_cast<unsigned char>(
+ tab[static_cast<unsigned char>(c)]);
+ return d != static_cast<unsigned char>(-1);
+ }
+
+ static
+ bool
+ is_digit(char c)
+ {
+ return static_cast<unsigned char>(c-'0') < 10;
+ }
+
+ static
+ bool
+ is_print(char c)
+ {
+ return static_cast<unsigned char>(c-32) < 95;
+ }
+
+ template<class FwdIt>
+ static
+ FwdIt
+ trim_front(FwdIt it, FwdIt const& end)
+ {
+ while(it != end)
+ {
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ }
+ return it;
+ }
+
+ template<class RanIt>
+ static
+ RanIt
+ trim_back(
+ RanIt it, RanIt const& first)
+ {
+ while(it != first)
+ {
+ auto const c = it[-1];
+ if(c != ' ' && c != '\t')
+ break;
+ --it;
+ }
+ return it;
+ }
+
+ static
+ string_view
+ make_string(char const* first, char const* last)
+ {
+ return {first, static_cast<
+ std::size_t>(last - first)};
+ }
+
+ //--------------------------------------------------------------------------
+
+ static
+ std::pair<char const*, bool>
+ find_fast(
+ char const* buf,
+ char const* buf_end,
+ char const* ranges,
+ size_t ranges_size)
+ {
+ bool found = false;
+ boost::ignore_unused(buf_end, ranges, ranges_size);
+ return {buf, found};
+ }
+
+ // VFALCO Can SIMD help this?
+ static
+ char const*
+ find_eol(
+ char const* it, char const* last,
+ error_code& ec)
+ {
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec.assign(0, ec.category());
+ return nullptr;
+ }
+ if(*it == '\r')
+ {
+ if(++it == last)
+ {
+ ec.assign(0, ec.category());
+ return nullptr;
+ }
+ if(*it != '\n')
+ {
+ ec = error::bad_line_ending;
+ return nullptr;
+ }
+ ec.assign(0, ec.category());
+ return ++it;
+ }
+ // VFALCO Should we handle the legacy case
+ // for lines terminated with a single '\n'?
+ ++it;
+ }
+ }
+
+ static
+ char const*
+ find_eom(char const* p, char const* last)
+ {
+ for(;;)
+ {
+ if(p + 4 > last)
+ return nullptr;
+ if(p[3] != '\n')
+ {
+ if(p[3] == '\r')
+ ++p;
+ else
+ p += 4;
+ }
+ else if(p[2] != '\r')
+ {
+ p += 4;
+ }
+ else if(p[1] != '\n')
+ {
+ p += 2;
+ }
+ else if(p[0] != '\r')
+ {
+ p += 2;
+ }
+ else
+ {
+ return p + 4;
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ static
+ char const*
+ parse_token_to_eol(
+ char const* p,
+ char const* last,
+ char const*& token_last,
+ error_code& ec)
+ {
+ for(;; ++p)
+ {
+ if(p >= last)
+ {
+ ec = error::need_more;
+ return p;
+ }
+ if(BOOST_UNLIKELY(! is_print(*p)))
+ if((BOOST_LIKELY(static_cast<
+ unsigned char>(*p) < '\040') &&
+ BOOST_LIKELY(*p != '\011')) ||
+ BOOST_UNLIKELY(*p == '\177'))
+ goto found_control;
+ }
+ found_control:
+ if(BOOST_LIKELY(*p == '\r'))
+ {
+ if(++p >= last)
+ {
+ ec = error::need_more;
+ return last;
+ }
+ if(*p++ != '\n')
+ {
+ ec = error::bad_line_ending;
+ return last;
+ }
+ token_last = p - 2;
+ }
+ #if 0
+ // VFALCO This allows `\n` by itself
+ // to terminate a line
+ else if(*p == '\n')
+ {
+ token_last = p;
+ ++p;
+ }
+ #endif
+ else
+ {
+ // invalid character
+ return nullptr;
+ }
+ return p;
+ }
+
+ template<class Iter, class Unsigned>
+ static
+ bool
+ parse_dec(Iter it, Iter last, Unsigned& v)
+ {
+ if(! is_digit(*it))
+ return false;
+ v = *it - '0';
+ for(;;)
+ {
+ if(! is_digit(*++it))
+ break;
+ auto const d = *it - '0';
+ if(v > ((std::numeric_limits<
+ Unsigned>::max)() - 10) / 10)
+ return false;
+ v = 10 * v + d;
+ }
+ return it == last;
+ }
+
+ template<class Iter, class Unsigned>
+ static
+ bool
+ parse_hex(Iter& it, Unsigned& v)
+ {
+ unsigned char d;
+ if(! unhex(d, *it))
+ return false;
+ v = d;
+ for(;;)
+ {
+ if(! unhex(d, *++it))
+ break;
+ auto const v0 = v;
+ v = 16 * v + d;
+ if(v < v0)
+ return false;
+ }
+ return true;
+ }
+
+ static
+ bool
+ parse_crlf(char const*& it)
+ {
+ if( it[0] != '\r' || it[1] != '\n')
+ return false;
+ it += 2;
+ return true;
+ }
+
+ static
+ void
+ parse_method(
+ char const*& it, char const* last,
+ string_view& result, error_code& ec)
+ {
+ // parse token SP
+ auto const first = it;
+ for(;; ++it)
+ {
+ if(it + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(! detail::is_token_char(*it))
+ break;
+ }
+ if(it + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it != ' ')
+ {
+ ec = error::bad_method;
+ return;
+ }
+ if(it == first)
+ {
+ // cannot be empty
+ ec = error::bad_method;
+ return;
+ }
+ result = make_string(first, it++);
+ }
+
+ static
+ void
+ parse_target(
+ char const*& it, char const* last,
+ string_view& result, error_code& ec)
+ {
+ // parse target SP
+ auto const first = it;
+ for(;; ++it)
+ {
+ if(it + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(! is_pathchar(*it))
+ break;
+ }
+ if(it + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it != ' ')
+ {
+ ec = error::bad_target;
+ return;
+ }
+ if(it == first)
+ {
+ // cannot be empty
+ ec = error::bad_target;
+ return;
+ }
+ result = make_string(first, it++);
+ }
+
+ static
+ void
+ parse_version(
+ char const*& it, char const* last,
+ int& result, error_code& ec)
+ {
+ if(it + 8 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it++ != 'H')
+ {
+ ec = error::bad_version;
+ return;
+ }
+ if(*it++ != 'T')
+ {
+ ec = error::bad_version;
+ return;
+ }
+ if(*it++ != 'T')
+ {
+ ec = error::bad_version;
+ return;
+ }
+ if(*it++ != 'P')
+ {
+ ec = error::bad_version;
+ return;
+ }
+ if(*it++ != '/')
+ {
+ ec = error::bad_version;
+ return;
+ }
+ if(! is_digit(*it))
+ {
+ ec = error::bad_version;
+ return;
+ }
+ result = 10 * (*it++ - '0');
+ if(*it++ != '.')
+ {
+ ec = error::bad_version;
+ return;
+ }
+ if(! is_digit(*it))
+ {
+ ec = error::bad_version;
+ return;
+ }
+ result += *it++ - '0';
+ }
+
+ static
+ void
+ parse_status(
+ char const*& it, char const* last,
+ unsigned short& result, error_code& ec)
+ {
+ // parse 3(digit) SP
+ if(it + 4 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(! is_digit(*it))
+ {
+ ec = error::bad_status;
+ return;
+ }
+ result = 100 * (*it++ - '0');
+ if(! is_digit(*it))
+ {
+ ec = error::bad_status;
+ return;
+ }
+ result += 10 * (*it++ - '0');
+ if(! is_digit(*it))
+ {
+ ec = error::bad_status;
+ return;
+ }
+ result += *it++ - '0';
+ if(*it++ != ' ')
+ {
+ ec = error::bad_status;
+ return;
+ }
+ }
+
+ void
+ parse_reason(
+ char const*& it, char const* last,
+ string_view& result, error_code& ec)
+ {
+ auto const first = it;
+ char const* token_last = nullptr;
+ auto p = parse_token_to_eol(
+ it, last, token_last, ec);
+ if(ec)
+ return;
+ if(! p)
+ {
+ ec = error::bad_reason;
+ return;
+ }
+ result = make_string(first, token_last);
+ it = p;
+ }
+
+ template<std::size_t N>
+ void
+ parse_field(
+ char const*& p,
+ char const* last,
+ string_view& name,
+ string_view& value,
+ static_string<N>& buf,
+ error_code& ec)
+ {
+ /* header-field = field-name ":" OWS field-value OWS
+
+ field-name = token
+ field-value = *( field-content / obs-fold )
+ field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+ field-vchar = VCHAR / obs-text
+
+ obs-fold = CRLF 1*( SP / HTAB )
+ ; obsolete line folding
+ ; see Section 3.2.4
+
+ token = 1*<any CHAR except CTLs or separators>
+ CHAR = <any US-ASCII character (octets 0 - 127)>
+ sep = "(" | ")" | "<" | ">" | "@"
+ | "," | ";" | ":" | "\" | <">
+ | "/" | "[" | "]" | "?" | "="
+ | "{" | "}" | SP | HT
+ */
+ static char const* is_token =
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
+ "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
+ "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+ // name
+ BOOST_ALIGNMENT(16) static const char ranges1[] =
+ "\x00 " /* control chars and up to SP */
+ "\"\"" /* 0x22 */
+ "()" /* 0x28,0x29 */
+ ",," /* 0x2c */
+ "//" /* 0x2f */
+ ":@" /* 0x3a-0x40 */
+ "[]" /* 0x5b-0x5d */
+ "{\377"; /* 0x7b-0xff */
+ auto first = p;
+ bool found;
+ std::tie(p, found) = find_fast(
+ p, last, ranges1, sizeof(ranges1)-1);
+ if(! found && p >= last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ for(;;)
+ {
+ if(*p == ':')
+ break;
+ if(! is_token[static_cast<
+ unsigned char>(*p)])
+ {
+ ec = error::bad_field;
+ return;
+ }
+ ++p;
+ if(p >= last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ }
+ if(p == first)
+ {
+ // empty name
+ ec = error::bad_field;
+ return;
+ }
+ name = make_string(first, p);
+ ++p; // eat ':'
+ char const* token_last = nullptr;
+ for(;;)
+ {
+ // eat leading ' ' and '\t'
+ for(;;++p)
+ {
+ if(p + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(! (*p == ' ' || *p == '\t'))
+ break;
+ }
+ // parse to CRLF
+ first = p;
+ p = parse_token_to_eol(p, last, token_last, ec);
+ if(ec)
+ return;
+ if(! p)
+ {
+ ec = error::bad_value;
+ return;
+ }
+ // Look 1 char past the CRLF to handle obs-fold.
+ if(p + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ token_last =
+ trim_back(token_last, first);
+ if(*p != ' ' && *p != '\t')
+ {
+ value = make_string(first, token_last);
+ return;
+ }
+ ++p;
+ if(token_last != first)
+ break;
+ }
+ buf.resize(0);
+ buf.append(first, token_last);
+ BOOST_ASSERT(! buf.empty());
+ try
+ {
+ for(;;)
+ {
+ // eat leading ' ' and '\t'
+ for(;;++p)
+ {
+ if(p + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(! (*p == ' ' || *p == '\t'))
+ break;
+ }
+ // parse to CRLF
+ first = p;
+ p = parse_token_to_eol(p, last, token_last, ec);
+ if(ec)
+ return;
+ if(! p)
+ {
+ ec = error::bad_value;
+ return;
+ }
+ // Look 1 char past the CRLF to handle obs-fold.
+ if(p + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ token_last = trim_back(token_last, first);
+ if(first != token_last)
+ {
+ buf.push_back(' ');
+ buf.append(first, token_last);
+ }
+ if(*p != ' ' && *p != '\t')
+ {
+ value = {buf.data(), buf.size()};
+ return;
+ }
+ ++p;
+ }
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::header_limit;
+ return;
+ }
+ }
+
+ void
+ parse_chunk_extensions(
+ char const*& it,
+ char const* last,
+ error_code& ec)
+ {
+ /*
+ chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
+ BWS = *( SP / HTAB ) ; "Bad White Space"
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+ token = 1*tchar
+ quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
+ qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
+ quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
+ obs-text = %x80-FF
+
+ https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
+ */
+ loop:
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it != ' ' && *it != '\t' && *it != ';')
+ return;
+ // BWS
+ if(*it == ' ' || *it == '\t')
+ {
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ }
+ }
+ // ';'
+ if(*it != ';')
+ {
+ ec = error::bad_chunk_extension;
+ return;
+ }
+ semi:
+ ++it; // skip ';'
+ // BWS
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ }
+ // chunk-ext-name
+ if(! detail::is_token_char(*it))
+ {
+ ec = error::bad_chunk_extension;
+ return;
+ }
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(! detail::is_token_char(*it))
+ break;
+ }
+ // BWS [ ";" / "=" ]
+ {
+ bool bws;
+ if(*it == ' ' || *it == '\t')
+ {
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ }
+ bws = true;
+ }
+ else
+ {
+ bws = false;
+ }
+ if(*it == ';')
+ goto semi;
+ if(*it != '=')
+ {
+ if(bws)
+ ec = error::bad_chunk_extension;
+ return;
+ }
+ ++it; // skip '='
+ }
+ // BWS
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ }
+ // chunk-ext-val
+ if(*it != '"')
+ {
+ // token
+ if(! detail::is_token_char(*it))
+ {
+ ec = error::bad_chunk_extension;
+ return;
+ }
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(! detail::is_token_char(*it))
+ break;
+ }
+ }
+ else
+ {
+ // quoted-string
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*it == '"')
+ break;
+ if(*it == '\\')
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ }
+ }
+ ++it;
+ }
+ goto loop;
+ }
+};
+
+} // detail
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/detail/chunk_encode.hpp b/boost/beast/http/detail/chunk_encode.hpp
new file mode 100644
index 0000000000..cad48418d9
--- /dev/null
+++ b/boost/beast/http/detail/chunk_encode.hpp
@@ -0,0 +1,247 @@
+//
+// 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_HTTP_DETAIL_CHUNK_ENCODE_HPP
+#define BOOST_BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
+
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace http {
+namespace detail {
+
+struct chunk_extensions
+{
+ virtual ~chunk_extensions() = default;
+ virtual boost::asio::const_buffer str() = 0;
+};
+
+template<class ChunkExtensions>
+struct chunk_extensions_impl : chunk_extensions
+{
+ ChunkExtensions ext_;
+
+ chunk_extensions_impl(ChunkExtensions&& ext)
+ : ext_(std::move(ext))
+ {
+ }
+
+ chunk_extensions_impl(ChunkExtensions const& ext)
+ : ext_(ext)
+ {
+ }
+
+ boost::asio::const_buffer
+ str() override
+ {
+ auto const s = ext_.str();
+ return {s.data(), s.size()};
+ }
+};
+
+template<class T, class = void>
+struct is_chunk_extensions : std::false_type {};
+
+template<class T>
+struct is_chunk_extensions<T, beast::detail::void_t<decltype(
+ std::declval<string_view&>() = std::declval<T&>().str(),
+ (void)0)>> : std::true_type
+{
+};
+
+//------------------------------------------------------------------------------
+
+/** A buffer sequence containing a chunk-encoding header
+*/
+class chunk_size
+{
+ template<class OutIter>
+ static
+ OutIter
+ to_hex(OutIter last, std::size_t n)
+ {
+ if(n == 0)
+ {
+ *--last = '0';
+ return last;
+ }
+ while(n)
+ {
+ *--last = "0123456789abcdef"[n&0xf];
+ n>>=4;
+ }
+ return last;
+ }
+
+ struct sequence
+ {
+ boost::asio::const_buffer b;
+ char data[1 + 2 * sizeof(std::size_t)];
+
+ explicit
+ sequence(std::size_t n)
+ {
+ char* it0 = data + sizeof(data);
+ auto it = to_hex(it0, n);
+ b = {it,
+ static_cast<std::size_t>(it0 - it)};
+ }
+ };
+
+ std::shared_ptr<sequence> sp_;
+
+public:
+ using value_type = boost::asio::const_buffer;
+
+ using const_iterator = value_type const*;
+
+ chunk_size(chunk_size const& other) = default;
+
+ /** Construct a chunk header
+
+ @param n The number of octets in this chunk.
+ */
+ chunk_size(std::size_t n)
+ : sp_(std::make_shared<sequence>(n))
+ {
+ }
+
+ const_iterator
+ begin() const
+ {
+ return &sp_->b;
+ }
+
+ const_iterator
+ end() const
+ {
+ return begin() + 1;
+ }
+};
+
+//------------------------------------------------------------------------------
+
+/// Returns a buffer sequence holding a CRLF for chunk encoding
+inline
+boost::asio::const_buffer
+chunk_crlf()
+{
+ return {"\r\n", 2};
+}
+
+/// Returns a buffer sequence holding a final chunk header
+inline
+boost::asio::const_buffer
+chunk_last()
+{
+ return {"0\r\n", 3};
+}
+
+//------------------------------------------------------------------------------
+
+template<class = void>
+struct chunk_crlf_iter_type
+{
+ class value_type
+ {
+ char const s[2] = {'\r', '\n'};
+
+ public:
+ value_type() = default;
+
+ operator
+ boost::asio::const_buffer() const
+ {
+ return {s, sizeof(s)};
+ }
+ };
+ static value_type value;
+};
+
+template<class T>
+typename chunk_crlf_iter_type<T>::value_type
+chunk_crlf_iter_type<T>::value;
+
+using chunk_crlf_iter = chunk_crlf_iter_type<void>;
+
+//------------------------------------------------------------------------------
+
+template<class = void>
+struct chunk_size0_iter_type
+{
+ class value_type
+ {
+ char const s[3] = {'0', '\r', '\n'};
+
+ public:
+ value_type() = default;
+
+ operator
+ boost::asio::const_buffer() const
+ {
+ return {s, sizeof(s)};
+ }
+ };
+ static value_type value;
+};
+
+template<class T>
+typename chunk_size0_iter_type<T>::value_type
+chunk_size0_iter_type<T>::value;
+
+using chunk_size0_iter = chunk_size0_iter_type<void>;
+
+struct chunk_size0
+{
+ using value_type = chunk_size0_iter::value_type;
+
+ using const_iterator = value_type const*;
+
+ const_iterator
+ begin() const
+ {
+ return &chunk_size0_iter::value;
+ }
+
+ const_iterator
+ end() const
+ {
+ return begin() + 1;
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<class T,
+ bool = is_fields<T>::value>
+struct buffers_or_fields
+{
+ using type = typename
+ T::writer::const_buffers_type;
+};
+
+template<class T>
+struct buffers_or_fields<T, false>
+{
+ using type = T;
+};
+
+} // detail
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/detail/rfc7230.hpp b/boost/beast/http/detail/rfc7230.hpp
new file mode 100644
index 0000000000..fdabc1a4d6
--- /dev/null
+++ b/boost/beast/http/detail/rfc7230.hpp
@@ -0,0 +1,473 @@
+//
+// 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_HTTP_DETAIL_RFC7230_HPP
+#define BOOST_BEAST_HTTP_DETAIL_RFC7230_HPP
+
+#include <boost/beast/core/string.hpp>
+#include <iterator>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+namespace detail {
+
+inline
+bool
+is_digit(char c)
+{
+ return c >= '0' && c <= '9';
+}
+
+inline
+char
+is_alpha(char c)
+{
+ static char constexpr tab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
+ };
+ BOOST_STATIC_ASSERT(sizeof(tab) == 256);
+ return tab[static_cast<unsigned char>(c)];
+}
+
+inline
+char
+is_text(char c)
+{
+ // TEXT = <any OCTET except CTLs, but including LWS>
+ static char constexpr tab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
+ };
+ BOOST_STATIC_ASSERT(sizeof(tab) == 256);
+ return tab[static_cast<unsigned char>(c)];
+}
+
+inline
+char
+is_token_char(char c)
+{
+ /*
+ tchar = "!" | "#" | "$" | "%" | "&" |
+ "'" | "*" | "+" | "-" | "." |
+ "^" | "_" | "`" | "|" | "~" |
+ DIGIT | ALPHA
+ */
+ static char constexpr tab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
+ };
+ BOOST_STATIC_ASSERT(sizeof(tab) == 256);
+ return tab[static_cast<unsigned char>(c)];
+}
+
+inline
+char
+is_qdchar(char c)
+{
+ /*
+ qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
+ */
+ static char constexpr tab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
+ };
+ BOOST_STATIC_ASSERT(sizeof(tab) == 256);
+ return tab[static_cast<unsigned char>(c)];
+}
+
+inline
+char
+is_qpchar(char c)
+{
+ /*
+ quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
+ obs-text = %x80-FF
+ */
+ static char constexpr tab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
+ };
+ BOOST_STATIC_ASSERT(sizeof(tab) == 256);
+ return tab[static_cast<unsigned char>(c)];
+}
+
+// converts to lower case,
+// returns 0 if not a valid text char
+//
+inline
+char
+to_value_char(char c)
+{
+ // TEXT = <any OCTET except CTLs, but including LWS>
+ static unsigned char constexpr tab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
+ 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
+ 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
+ 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
+ };
+ BOOST_STATIC_ASSERT(sizeof(tab) == 256);
+ return static_cast<char>(tab[static_cast<unsigned char>(c)]);
+}
+
+// VFALCO TODO Make this return unsigned?
+inline
+std::int8_t
+unhex(char c)
+{
+ static signed char constexpr tab[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240
+ };
+ BOOST_STATIC_ASSERT(sizeof(tab) == 256);
+ return tab[static_cast<unsigned char>(c)];
+}
+
+template<class FwdIt>
+inline
+void
+skip_ows(FwdIt& it, FwdIt const& end)
+{
+ while(it != end)
+ {
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ }
+}
+
+template<class RanIt>
+inline
+void
+skip_ows_rev(
+ RanIt& it, RanIt const& first)
+{
+ while(it != first)
+ {
+ auto const c = it[-1];
+ if(c != ' ' && c != '\t')
+ break;
+ --it;
+ }
+}
+
+// obs-fold = CRLF 1*( SP / HTAB )
+// return `false` on parse error
+//
+template<class FwdIt>
+inline
+bool
+skip_obs_fold(
+ FwdIt& it, FwdIt const& last)
+{
+ for(;;)
+ {
+ if(*it != '\r')
+ return true;
+ if(++it == last)
+ return false;
+ if(*it != '\n')
+ return false;
+ if(++it == last)
+ return false;
+ if(*it != ' ' && *it != '\t')
+ return false;
+ for(;;)
+ {
+ if(++it == last)
+ return true;
+ if(*it != ' ' && *it != '\t')
+ return true;
+ }
+ }
+}
+
+template<class FwdIt>
+void
+skip_token(FwdIt& it, FwdIt const& last)
+{
+ while(it != last && is_token_char(*it))
+ ++it;
+}
+
+inline
+string_view
+trim(string_view s)
+{
+ auto first = s.begin();
+ auto last = s.end();
+ skip_ows(first, last);
+ while(first != last)
+ {
+ auto const c = *std::prev(last);
+ if(c != ' ' && c != '\t')
+ break;
+ --last;
+ }
+ if(first == last)
+ return {};
+ return {&*first,
+ static_cast<std::size_t>(last - first)};
+}
+
+struct param_iter
+{
+ using iter_type = string_view::const_iterator;
+
+ iter_type it;
+ iter_type first;
+ iter_type last;
+ std::pair<string_view, string_view> v;
+
+ bool
+ empty() const
+ {
+ return first == it;
+ }
+
+ template<class = void>
+ void
+ increment();
+};
+
+template<class>
+void
+param_iter::
+increment()
+{
+/*
+ param-list = *( OWS ";" OWS param )
+ param = token OWS [ "=" OWS ( token / quoted-string ) ]
+ quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
+ qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
+ quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
+ obs-text = %x80-FF
+*/
+ auto const err =
+ [&]
+ {
+ it = first;
+ };
+ v.first.clear();
+ v.second.clear();
+ detail::skip_ows(it, last);
+ first = it;
+ if(it == last)
+ return err();
+ if(*it != ';')
+ return err();
+ ++it;
+ detail::skip_ows(it, last);
+ if(it == last)
+ return err();
+ // param
+ if(! detail::is_token_char(*it))
+ return err();
+ auto const p0 = it;
+ skip_token(++it, last);
+ auto const p1 = it;
+ v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
+ detail::skip_ows(it, last);
+ if(it == last)
+ return;
+ if(*it == ';')
+ return;
+ if(*it != '=')
+ return err();
+ ++it;
+ detail::skip_ows(it, last);
+ if(it == last)
+ return;
+ if(*it == '"')
+ {
+ // quoted-string
+ auto const p2 = it;
+ ++it;
+ for(;;)
+ {
+ if(it == last)
+ return err();
+ auto c = *it++;
+ if(c == '"')
+ break;
+ if(detail::is_qdchar(c))
+ continue;
+ if(c != '\\')
+ return err();
+ if(it == last)
+ return err();
+ c = *it++;
+ if(! detail::is_qpchar(c))
+ return err();
+ }
+ v.second = { &*p2, static_cast<std::size_t>(it - p2) };
+ }
+ else
+ {
+ // token
+ if(! detail::is_token_char(*it))
+ return err();
+ auto const p2 = it;
+ skip_token(++it, last);
+ v.second = { &*p2, static_cast<std::size_t>(it - p2) };
+ }
+}
+
+/*
+ #token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
+*/
+struct opt_token_list_policy
+{
+ using value_type = string_view;
+
+ bool
+ operator()(value_type& v,
+ char const*& it, string_view s) const
+ {
+ v = {};
+ auto need_comma = it != s.begin();
+ for(;;)
+ {
+ detail::skip_ows(it, s.end());
+ if(it == s.end())
+ {
+ it = nullptr;
+ return true;
+ }
+ auto const c = *it;
+ if(detail::is_token_char(c))
+ {
+ if(need_comma)
+ return false;
+ auto const p0 = it;
+ for(;;)
+ {
+ ++it;
+ if(it == s.end())
+ break;
+ if(! detail::is_token_char(*it))
+ break;
+ }
+ v = string_view{&*p0,
+ static_cast<std::size_t>(it - p0)};
+ return true;
+ }
+ if(c != ',')
+ return false;
+ need_comma = false;
+ ++it;
+ }
+ }
+};
+
+} // detail
+} // http
+} // beast
+} // boost
+
+#endif
+
diff --git a/boost/beast/http/detail/type_traits.hpp b/boost/beast/http/detail/type_traits.hpp
new file mode 100644
index 0000000000..c9a06f9784
--- /dev/null
+++ b/boost/beast/http/detail/type_traits.hpp
@@ -0,0 +1,202 @@
+//
+// 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_HTTP_DETAIL_TYPE_TRAITS_HPP
+#define BOOST_BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
+
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/optional.hpp>
+#include <cstdint>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<bool isRequest, class Fields>
+struct header;
+
+template<bool, class, class>
+struct message;
+
+template<bool isRequest,class Body, class Fields>
+class parser;
+
+namespace detail {
+
+template<class T>
+class is_header_impl
+{
+ template<bool b, class F>
+ static std::true_type check(
+ header<b, F> const*);
+ static std::false_type check(...);
+public:
+ using type = decltype(check((T*)0));
+};
+
+template<class T>
+using is_header = typename is_header_impl<T>::type;
+
+template<class T>
+struct is_parser : std::false_type {};
+
+template<bool isRequest, class Body, class Fields>
+struct is_parser<parser<isRequest, Body, Fields>> : std::true_type {};
+
+struct fields_model
+{
+ struct writer;
+
+ string_view method() const;
+ string_view reason() const;
+ string_view target() const;
+
+protected:
+ string_view get_method_impl() const;
+ string_view get_target_impl() const;
+ string_view get_reason_impl() const;
+ bool get_chunked_impl() const;
+ bool get_keep_alive_impl(unsigned) const;
+ bool has_content_length_impl() const;
+ void set_method_impl(string_view);
+ void set_target_impl(string_view);
+ void set_reason_impl(string_view);
+ void set_chunked_impl(bool);
+ void set_content_length_impl(boost::optional<std::uint64_t>);
+ void set_keep_alive_impl(unsigned, bool);
+};
+
+template<class T, class = beast::detail::void_t<>>
+struct has_value_type : std::false_type {};
+
+template<class T>
+struct has_value_type<T, beast::detail::void_t<
+ typename T::value_type
+ > > : std::true_type {};
+
+/** Determine if a @b Body type has a size
+
+ This metafunction is equivalent to `std::true_type` if
+ Body contains a static member function called `size`.
+*/
+template<class T, class = void>
+struct is_body_sized : std::false_type {};
+
+template<class T>
+struct is_body_sized<T, beast::detail::void_t<
+ typename T::value_type,
+ decltype(
+ std::declval<std::uint64_t&>() =
+ T::size(std::declval<typename T::value_type const&>()),
+ (void)0)>> : std::true_type {};
+
+template<class T>
+struct is_fields_helper : T
+{
+ template<class U = is_fields_helper>
+ static auto f1(int) -> decltype(
+ std::declval<string_view&>() = std::declval<U const&>().get_method_impl(),
+ std::true_type());
+ static auto f1(...) -> std::false_type;
+ using t1 = decltype(f1(0));
+
+ template<class U = is_fields_helper>
+ static auto f2(int) -> decltype(
+ std::declval<string_view&>() = std::declval<U const&>().get_target_impl(),
+ std::true_type());
+ static auto f2(...) -> std::false_type;
+ using t2 = decltype(f2(0));
+
+ template<class U = is_fields_helper>
+ static auto f3(int) -> decltype(
+ std::declval<string_view&>() = std::declval<U const&>().get_reason_impl(),
+ std::true_type());
+ static auto f3(...) -> std::false_type;
+ using t3 = decltype(f3(0));
+
+ template<class U = is_fields_helper>
+ static auto f4(int) -> decltype(
+ std::declval<bool&>() = std::declval<U const&>().get_chunked_impl(),
+ std::true_type());
+ static auto f4(...) -> std::false_type;
+ using t4 = decltype(f4(0));
+
+ template<class U = is_fields_helper>
+ static auto f5(int) -> decltype(
+ std::declval<bool&>() = std::declval<U const&>().get_keep_alive_impl(
+ std::declval<unsigned>()),
+ std::true_type());
+ static auto f5(...) -> std::false_type;
+ using t5 = decltype(f5(0));
+
+ template<class U = is_fields_helper>
+ static auto f6(int) -> decltype(
+ std::declval<bool&>() = std::declval<U const&>().has_content_length_impl(),
+ std::true_type());
+ static auto f6(...) -> std::false_type;
+ using t6 = decltype(f6(0));
+
+ template<class U = is_fields_helper>
+ static auto f7(int) -> decltype(
+ void(std::declval<U&>().set_method_impl(std::declval<string_view>())),
+ std::true_type());
+ static auto f7(...) -> std::false_type;
+ using t7 = decltype(f7(0));
+
+ template<class U = is_fields_helper>
+ static auto f8(int) -> decltype(
+ void(std::declval<U&>().set_target_impl(std::declval<string_view>())),
+ std::true_type());
+ static auto f8(...) -> std::false_type;
+ using t8 = decltype(f8(0));
+
+ template<class U = is_fields_helper>
+ static auto f9(int) -> decltype(
+ void(std::declval<U&>().set_reason_impl(std::declval<string_view>())),
+ std::true_type());
+ static auto f9(...) -> std::false_type;
+ using t9 = decltype(f9(0));
+
+ template<class U = is_fields_helper>
+ static auto f10(int) -> decltype(
+ void(std::declval<U&>().set_chunked_impl(std::declval<bool>())),
+ std::true_type());
+ static auto f10(...) -> std::false_type;
+ using t10 = decltype(f10(0));
+
+ template<class U = is_fields_helper>
+ static auto f11(int) -> decltype(
+ void(std::declval<U&>().set_content_length_impl(
+ std::declval<boost::optional<std::uint64_t>>())),
+ std::true_type());
+ static auto f11(...) -> std::false_type;
+ using t11 = decltype(f11(0));
+
+ template<class U = is_fields_helper>
+ static auto f12(int) -> decltype(
+ void(std::declval<U&>().set_keep_alive_impl(
+ std::declval<unsigned>(),
+ std::declval<bool>())),
+ std::true_type());
+ static auto f12(...) -> std::false_type;
+ using t12 = decltype(f12(0));
+
+ using type = std::integral_constant<bool,
+ t1::value && t2::value && t3::value &&
+ t4::value && t5::value && t6::value &&
+ t7::value && t8::value && t9::value &&
+ t10::value && t11::value && t12::value>;
+};
+
+} // detail
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/dynamic_body.hpp b/boost/beast/http/dynamic_body.hpp
new file mode 100644
index 0000000000..9c3df8a1d2
--- /dev/null
+++ b/boost/beast/http/dynamic_body.hpp
@@ -0,0 +1,30 @@
+//
+// 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_HTTP_DYNAMIC_BODY_HPP
+#define BOOST_BEAST_HTTP_DYNAMIC_BODY_HPP
+
+#include <boost/beast/core/multi_buffer.hpp>
+#include <boost/beast/http/basic_dynamic_body.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A dynamic message body represented by a @ref multi_buffer
+
+ Meets the requirements of @b Body.
+*/
+using dynamic_body = basic_dynamic_body<multi_buffer>;
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/empty_body.hpp b/boost/beast/http/empty_body.hpp
new file mode 100644
index 0000000000..1845a22992
--- /dev/null
+++ b/boost/beast/http/empty_body.hpp
@@ -0,0 +1,132 @@
+//
+// 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_HTTP_EMPTY_BODY_HPP
+#define BOOST_BEAST_HTTP_EMPTY_BODY_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/optional.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** An empty @b Body
+
+ This body is used to represent messages which do not have a
+ message body. If this body is used with a parser, and the
+ parser encounters octets corresponding to a message body,
+ the parser will fail with the error @ref http::unexpected_body.
+
+ The Content-Length of this body is always 0.
+*/
+struct empty_body
+{
+ /** The type of container used for the body
+
+ This determines the type of @ref message::body
+ when this body type is used with a message container.
+ */
+ struct value_type
+ {
+ };
+
+ /** Returns the payload size of the body
+
+ When this body is used with @ref message::prepare_payload,
+ the Content-Length will be set to the payload size, and
+ any chunked Transfer-Encoding will be removed.
+ */
+ static
+ std::uint64_t
+ size(value_type)
+ {
+ return 0;
+ }
+
+ /** The algorithm for parsing the body
+
+ Meets the requirements of @b BodyReader.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using reader = implementation_defined;
+#else
+ struct reader
+ {
+ template<bool isRequest, class Fields>
+ explicit
+ reader(message<isRequest, empty_body, Fields>&)
+ {
+ }
+
+ void
+ init(boost::optional<std::uint64_t> const&, error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const&,
+ error_code& ec)
+ {
+ ec = error::unexpected_body;
+ return 0;
+ }
+
+ void
+ finish(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+ };
+#endif
+
+ /** The algorithm for serializing the body
+
+ Meets the requirements of @b BodyWriter.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using writer = implementation_defined;
+#else
+ struct writer
+ {
+ using const_buffers_type =
+ boost::asio::null_buffers;
+
+ template<bool isRequest, class Fields>
+ explicit
+ writer(message<isRequest,
+ empty_body, Fields> const&)
+ {
+ }
+
+ void
+ init(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ boost::optional<std::pair<const_buffers_type, bool>>
+ get(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ return boost::none;
+ }
+ };
+#endif
+};
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/error.hpp b/boost/beast/http/error.hpp
new file mode 100644
index 0000000000..841a99de25
--- /dev/null
+++ b/boost/beast/http/error.hpp
@@ -0,0 +1,161 @@
+//
+// 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_HTTP_ERROR_HPP
+#define BOOST_BEAST_HTTP_ERROR_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/// Error codes returned from HTTP algorithms and operations.
+enum class error
+{
+ /** The end of the stream was reached.
+
+ This error is returned under the following conditions:
+
+ @li When attempting to read HTTP data from a stream and the stream
+ read returns the error `boost::asio::error::eof` before any new octets
+ have been received.
+
+ @li When sending a complete HTTP message at once and the semantics of
+ the message are that the connection should be closed to indicate the
+ end of the message.
+ */
+ end_of_stream = 1,
+
+ /** The incoming message is incomplete.
+
+ This happens when the end of stream is reached during
+ parsing and some octets have been received, but not the
+ entire message.
+ */
+ partial_message,
+
+ /** Additional buffers are required.
+
+ This error is returned during parsing when additional
+ octets are needed. The caller should append more data
+ to the existing buffer and retry the parse operaetion.
+ */
+ need_more,
+
+ /** An unexpected body was encountered during parsing.
+
+ This error is returned when attempting to parse body
+ octets into a message container which has the
+ @ref empty_body body type.
+
+ @see @ref empty_body
+ */
+ unexpected_body,
+
+ /** Additional buffers are required.
+
+ This error is returned under the following conditions:
+
+ @li During serialization when using @ref buffer_body.
+ The caller should update the body to point to a new
+ buffer or indicate that there are no more octets in
+ the body.
+
+ @li During parsing when using @ref buffer_body.
+ The caller should update the body to point to a new
+ storage area to receive additional body octets.
+ */
+ need_buffer,
+
+ /** The end of a chunk was reached
+ */
+ end_of_chunk,
+
+ /** Buffer maximum exceeded.
+
+ This error is returned when reading HTTP content
+ into a dynamic buffer, and the operation would
+ exceed the maximum size of the buffer.
+ */
+ buffer_overflow,
+
+ /** Header limit exceeded.
+
+ The parser detected an incoming message header which
+ exceeded a configured limit.
+ */
+ header_limit,
+
+ /** Body limit exceeded.
+
+ The parser detected an incoming message body which
+ exceeded a configured limit.
+ */
+ body_limit,
+
+ /** A memory allocation failed.
+
+ When basic_fields throws std::bad_alloc, it is
+ converted into this error by @ref parser.
+ */
+ bad_alloc,
+
+ //
+ // (parser errors)
+ //
+
+ /// The line ending was malformed
+ bad_line_ending,
+
+ /// The method is invalid.
+ bad_method,
+
+ /// The request-target is invalid.
+ bad_target,
+
+ /// The HTTP-version is invalid.
+ bad_version,
+
+ /// The status-code is invalid.
+ bad_status,
+
+ /// The reason-phrase is invalid.
+ bad_reason,
+
+ /// The field name is invalid.
+ bad_field,
+
+ /// The field value is invalid.
+ bad_value,
+
+ /// The Content-Length is invalid.
+ bad_content_length,
+
+ /// The Transfer-Encoding is invalid.
+ bad_transfer_encoding,
+
+ /// The chunk syntax is invalid.
+ bad_chunk,
+
+ /// The chunk extension is invalid.
+ bad_chunk_extension,
+
+ /// An obs-fold exceeded an internal limit.
+ bad_obs_fold
+};
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/error.ipp>
+
+#endif
diff --git a/boost/beast/http/field.hpp b/boost/beast/http/field.hpp
new file mode 100644
index 0000000000..3d1446addc
--- /dev/null
+++ b/boost/beast/http/field.hpp
@@ -0,0 +1,409 @@
+//
+// 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_HTTP_FIELD_HPP
+#define BOOST_BEAST_HTTP_FIELD_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/string.hpp>
+#include <iosfwd>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+enum class field : unsigned short
+{
+ unknown = 0,
+
+ a_im,
+ accept,
+ accept_additions,
+ accept_charset,
+ accept_datetime,
+ accept_encoding,
+ accept_features,
+ accept_language,
+ accept_patch,
+ accept_post,
+ accept_ranges,
+ access_control,
+ access_control_allow_credentials,
+ access_control_allow_headers,
+ access_control_allow_methods,
+ access_control_allow_origin,
+ access_control_max_age,
+ access_control_request_headers,
+ access_control_request_method,
+ age,
+ allow,
+ alpn,
+ also_control,
+ alt_svc,
+ alt_used,
+ alternate_recipient,
+ alternates,
+ apparently_to,
+ apply_to_redirect_ref,
+ approved,
+ archive,
+ archived_at,
+ article_names,
+ article_updates,
+ authentication_control,
+ authentication_info,
+ authentication_results,
+ authorization,
+ auto_submitted,
+ autoforwarded,
+ autosubmitted,
+ base,
+ bcc,
+ body,
+ c_ext,
+ c_man,
+ c_opt,
+ c_pep,
+ c_pep_info,
+ cache_control,
+ caldav_timezones,
+ cancel_key,
+ cancel_lock,
+ cc,
+ close,
+ comments,
+ compliance,
+ connection,
+ content_alternative,
+ content_base,
+ content_description,
+ content_disposition,
+ content_duration,
+ content_encoding,
+ content_features,
+ content_id,
+ content_identifier,
+ content_language,
+ content_length,
+ content_location,
+ content_md5,
+ content_range,
+ content_return,
+ content_script_type,
+ content_style_type,
+ content_transfer_encoding,
+ content_type,
+ content_version,
+ control,
+ conversion,
+ conversion_with_loss,
+ cookie,
+ cookie2,
+ cost,
+ dasl,
+ date,
+ date_received,
+ dav,
+ default_style,
+ deferred_delivery,
+ delivery_date,
+ delta_base,
+ depth,
+ derived_from,
+ destination,
+ differential_id,
+ digest,
+ discarded_x400_ipms_extensions,
+ discarded_x400_mts_extensions,
+ disclose_recipients,
+ disposition_notification_options,
+ disposition_notification_to,
+ distribution,
+ dkim_signature,
+ dl_expansion_history,
+ downgraded_bcc,
+ downgraded_cc,
+ downgraded_disposition_notification_to,
+ downgraded_final_recipient,
+ downgraded_from,
+ downgraded_in_reply_to,
+ downgraded_mail_from,
+ downgraded_message_id,
+ downgraded_original_recipient,
+ downgraded_rcpt_to,
+ downgraded_references,
+ downgraded_reply_to,
+ downgraded_resent_bcc,
+ downgraded_resent_cc,
+ downgraded_resent_from,
+ downgraded_resent_reply_to,
+ downgraded_resent_sender,
+ downgraded_resent_to,
+ downgraded_return_path,
+ downgraded_sender,
+ downgraded_to,
+ ediint_features,
+ eesst_version,
+ encoding,
+ encrypted,
+ errors_to,
+ etag,
+ expect,
+ expires,
+ expiry_date,
+ ext,
+ followup_to,
+ forwarded,
+ from,
+ generate_delivery_report,
+ getprofile,
+ hobareg,
+ host,
+ http2_settings,
+ if_,
+ if_match,
+ if_modified_since,
+ if_none_match,
+ if_range,
+ if_schedule_tag_match,
+ if_unmodified_since,
+ im,
+ importance,
+ in_reply_to,
+ incomplete_copy,
+ injection_date,
+ injection_info,
+ jabber_id,
+ keep_alive,
+ keywords,
+ label,
+ language,
+ last_modified,
+ latest_delivery_time,
+ lines,
+ link,
+ list_archive,
+ list_help,
+ list_id,
+ list_owner,
+ list_post,
+ list_subscribe,
+ list_unsubscribe,
+ list_unsubscribe_post,
+ location,
+ lock_token,
+ man,
+ max_forwards,
+ memento_datetime,
+ message_context,
+ message_id,
+ message_type,
+ meter,
+ method_check,
+ method_check_expires,
+ mime_version,
+ mmhs_acp127_message_identifier,
+ mmhs_authorizing_users,
+ mmhs_codress_message_indicator,
+ mmhs_copy_precedence,
+ mmhs_exempted_address,
+ mmhs_extended_authorisation_info,
+ mmhs_handling_instructions,
+ mmhs_message_instructions,
+ mmhs_message_type,
+ mmhs_originator_plad,
+ mmhs_originator_reference,
+ mmhs_other_recipients_indicator_cc,
+ mmhs_other_recipients_indicator_to,
+ mmhs_primary_precedence,
+ mmhs_subject_indicator_codes,
+ mt_priority,
+ negotiate,
+ newsgroups,
+ nntp_posting_date,
+ nntp_posting_host,
+ non_compliance,
+ obsoletes,
+ opt,
+ optional,
+ optional_www_authenticate,
+ ordering_type,
+ organization,
+ origin,
+ original_encoded_information_types,
+ original_from,
+ original_message_id,
+ original_recipient,
+ original_sender,
+ original_subject,
+ originator_return_address,
+ overwrite,
+ p3p,
+ path,
+ pep,
+ pep_info,
+ pics_label,
+ position,
+ posting_version,
+ pragma,
+ prefer,
+ preference_applied,
+ prevent_nondelivery_report,
+ priority,
+ privicon,
+ profileobject,
+ protocol,
+ protocol_info,
+ protocol_query,
+ protocol_request,
+ proxy_authenticate,
+ proxy_authentication_info,
+ proxy_authorization,
+ proxy_connection,
+ proxy_features,
+ proxy_instruction,
+ public_,
+ public_key_pins,
+ public_key_pins_report_only,
+ range,
+ received,
+ received_spf,
+ redirect_ref,
+ references,
+ referer,
+ referer_root,
+ relay_version,
+ reply_by,
+ reply_to,
+ require_recipient_valid_since,
+ resent_bcc,
+ resent_cc,
+ resent_date,
+ resent_from,
+ resent_message_id,
+ resent_reply_to,
+ resent_sender,
+ resent_to,
+ resolution_hint,
+ resolver_location,
+ retry_after,
+ return_path,
+ safe,
+ schedule_reply,
+ schedule_tag,
+ sec_websocket_accept,
+ sec_websocket_extensions,
+ sec_websocket_key,
+ sec_websocket_protocol,
+ sec_websocket_version,
+ security_scheme,
+ see_also,
+ sender,
+ sensitivity,
+ server,
+ set_cookie,
+ set_cookie2,
+ setprofile,
+ sio_label,
+ sio_label_history,
+ slug,
+ soapaction,
+ solicitation,
+ status_uri,
+ strict_transport_security,
+ subject,
+ subok,
+ subst,
+ summary,
+ supersedes,
+ surrogate_capability,
+ surrogate_control,
+ tcn,
+ te,
+ timeout,
+ title,
+ to,
+ topic,
+ trailer,
+ transfer_encoding,
+ ttl,
+ ua_color,
+ ua_media,
+ ua_pixels,
+ ua_resolution,
+ ua_windowpixels,
+ upgrade,
+ urgency,
+ uri,
+ user_agent,
+ variant_vary,
+ vary,
+ vbr_info,
+ version,
+ via,
+ want_digest,
+ warning,
+ www_authenticate,
+ x_archived_at,
+ x_device_accept,
+ x_device_accept_charset,
+ x_device_accept_encoding,
+ x_device_accept_language,
+ x_device_user_agent,
+ x_frame_options,
+ x_mittente,
+ x_pgp_sig,
+ x_ricevuta,
+ x_riferimento_message_id,
+ x_tiporicevuta,
+ x_trasporto,
+ x_verificasicurezza,
+ x400_content_identifier,
+ x400_content_return,
+ x400_content_type,
+ x400_mts_identifier,
+ x400_originator,
+ x400_received,
+ x400_recipients,
+ x400_trace,
+ xref,
+};
+
+/** Convert a field enum to a string.
+
+ @param f The field to convert
+*/
+string_view
+to_string(field f);
+
+/** Attempt to convert a string to a field enum.
+
+ The string comparison is case-insensitive.
+
+ @return The corresponding field, or @ref field::unknown
+ if no known field matches.
+*/
+field
+string_to_field(string_view s);
+
+/// Write the text for a field name to an output stream.
+inline
+std::ostream&
+operator<<(std::ostream& os, field f)
+{
+ return os << to_string(f);
+}
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/field.ipp>
+
+#endif
diff --git a/boost/beast/http/fields.hpp b/boost/beast/http/fields.hpp
new file mode 100644
index 0000000000..06c950ee5a
--- /dev/null
+++ b/boost/beast/http/fields.hpp
@@ -0,0 +1,755 @@
+//
+// 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_HTTP_FIELDS_HPP
+#define BOOST_BEAST_HTTP_FIELDS_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/string_param.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/detail/allocator.hpp>
+#include <boost/beast/http/field.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/intrusive/list.hpp>
+#include <boost/intrusive/set.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
+#include <cctype>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A container for storing HTTP header fields.
+
+ This container is designed to store the field value pairs that make
+ up the fields and trailers in an HTTP message. Objects of this type
+ are iterable, with each element holding the field name and field
+ value.
+
+ Field names are stored as-is, but comparisons are case-insensitive.
+ The container behaves as a `std::multiset`; there will be a separate
+ value for each occurrence of the same field name. When the container
+ is iterated the fields are presented in the order of insertion, with
+ fields having the same name following each other consecutively.
+
+ Meets the requirements of @b Fields
+
+ @tparam Allocator The allocator to use. This must meet the
+ requirements of @b Allocator.
+*/
+template<class Allocator>
+class basic_fields
+{
+ friend class fields_test; // for `header`
+
+ static std::size_t constexpr max_static_buffer = 4096;
+
+ using off_t = std::uint16_t;
+
+public:
+ /// The type of allocator used.
+ using allocator_type = Allocator;
+
+ /// The type of element used to represent a field
+ class value_type
+ {
+ friend class basic_fields;
+
+ boost::asio::const_buffer
+ buffer() const;
+
+ value_type(field name,
+ string_view sname, string_view value);
+
+ boost::intrusive::list_member_hook<
+ boost::intrusive::link_mode<
+ boost::intrusive::normal_link>>
+ list_hook_;
+ boost::intrusive::set_member_hook<
+ boost::intrusive::link_mode<
+ boost::intrusive::normal_link>>
+ set_hook_;
+ off_t off_;
+ off_t len_;
+ field f_;
+
+ public:
+ /// Constructor (deleted)
+ value_type(value_type const&) = delete;
+
+ /// Assignment (deleted)
+ value_type& operator=(value_type const&) = delete;
+
+ /// Returns the field enum, which can be @ref field::unknown
+ field const
+ name() const;
+
+ /// Returns the field name as a string
+ string_view const
+ name_string() const;
+
+ /// Returns the value of the field
+ string_view const
+ value() const;
+ };
+
+ /** A strictly less predicate for comparing keys, using a case-insensitive comparison.
+
+ The case-comparison operation is defined only for low-ASCII characters.
+ */
+ struct key_compare : beast::iless
+ {
+ /// Returns `true` if lhs is less than rhs using a strict ordering
+ template<class String>
+ bool
+ operator()(
+ String const& lhs,
+ value_type const& rhs) const noexcept
+ {
+ if(lhs.size() < rhs.name_string().size())
+ return true;
+ if(lhs.size() > rhs.name_string().size())
+ return false;
+ return iless::operator()(lhs, rhs.name_string());
+ }
+
+ /// Returns `true` if lhs is less than rhs using a strict ordering
+ template<class String>
+ bool
+ operator()(
+ value_type const& lhs,
+ String const& rhs) const noexcept
+ {
+ if(lhs.name_string().size() < rhs.size())
+ return true;
+ if(lhs.name_string().size() > rhs.size())
+ return false;
+ return iless::operator()(lhs.name_string(), rhs);
+ }
+
+ /// Returns `true` if lhs is less than rhs using a strict ordering
+ bool
+ operator()(
+ value_type const& lhs,
+ value_type const& rhs) const noexcept
+ {
+ if(lhs.name_string().size() < rhs.name_string().size())
+ return true;
+ if(lhs.name_string().size() > rhs.name_string().size())
+ return false;
+ return iless::operator()(lhs.name_string(), rhs.name_string());
+ }
+ };
+
+ /// The algorithm used to serialize the header
+#if BOOST_BEAST_DOXYGEN
+ using writer = implementation_defined;
+#else
+ class writer;
+#endif
+
+private:
+ using list_t = typename boost::intrusive::make_list<
+ value_type, boost::intrusive::member_hook<
+ value_type, boost::intrusive::list_member_hook<
+ boost::intrusive::link_mode<
+ boost::intrusive::normal_link>>,
+ &value_type::list_hook_>,
+ boost::intrusive::constant_time_size<
+ false>>::type;
+
+ using set_t = typename boost::intrusive::make_multiset<
+ value_type, boost::intrusive::member_hook<value_type,
+ boost::intrusive::set_member_hook<
+ boost::intrusive::link_mode<
+ boost::intrusive::normal_link>>,
+ &value_type::set_hook_>,
+ boost::intrusive::constant_time_size<true>,
+ boost::intrusive::compare<key_compare>>::type;
+
+
+public:
+ /// Destructor
+ ~basic_fields();
+
+ /// Constructor.
+ basic_fields() = default;
+
+ /** Constructor.
+
+ @param alloc The allocator to use.
+ */
+ explicit
+ basic_fields(Allocator const& alloc);
+
+ /** Move constructor.
+
+ The state of the moved-from object is
+ as if constructed using the same allocator.
+ */
+ basic_fields(basic_fields&&);
+
+ /** Move constructor.
+
+ The state of the moved-from object is
+ as if constructed using the same allocator.
+
+ @param alloc The allocator to use.
+ */
+ basic_fields(basic_fields&&, Allocator const& alloc);
+
+ /// Copy constructor.
+ basic_fields(basic_fields const&);
+
+ /** Copy constructor.
+
+ @param alloc The allocator to use.
+ */
+ basic_fields(basic_fields const&, Allocator const& alloc);
+
+ /// Copy constructor.
+ template<class OtherAlloc>
+ basic_fields(basic_fields<OtherAlloc> const&);
+
+ /** Copy constructor.
+
+ @param alloc The allocator to use.
+ */
+ template<class OtherAlloc>
+ basic_fields(basic_fields<OtherAlloc> const&,
+ Allocator const& alloc);
+
+ /** Move assignment.
+
+ The state of the moved-from object is
+ as if constructed using the same allocator.
+ */
+ basic_fields& operator=(basic_fields&&);
+
+ /// Copy assignment.
+ basic_fields& operator=(basic_fields const&);
+
+ /// Copy assignment.
+ template<class OtherAlloc>
+ basic_fields& operator=(basic_fields<OtherAlloc> const&);
+
+public:
+ /// A constant iterator to the field sequence.
+#if BOOST_BEAST_DOXYGEN
+ using const_iterator = implementation_defined;
+#else
+ using const_iterator = typename list_t::const_iterator;
+#endif
+
+ /// A constant iterator to the field sequence.
+ using iterator = const_iterator;
+
+ /// Return a copy of the allocator associated with the container.
+ allocator_type
+ get_allocator() const
+ {
+ return alloc_;
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Element access
+ //
+ //--------------------------------------------------------------------------
+
+ /** Returns the value for a field, or throws an exception.
+
+ If more than one field with the specified name exists, the
+ first field defined by insertion order is returned.
+
+ @param name The name of the field.
+
+ @return The field value.
+
+ @throws std::out_of_range if the field is not found.
+ */
+ string_view const
+ at(field name) const;
+
+ /** Returns the value for a field, or throws an exception.
+
+ If more than one field with the specified name exists, the
+ first field defined by insertion order is returned.
+
+ @param name The name of the field.
+
+ @return The field value.
+
+ @throws std::out_of_range if the field is not found.
+ */
+ string_view const
+ at(string_view name) const;
+
+ /** Returns the value for a field, or `""` if it does not exist.
+
+ If more than one field with the specified name exists, the
+ first field defined by insertion order is returned.
+
+ @param name The name of the field.
+ */
+ string_view const
+ operator[](field name) const;
+
+ /** Returns the value for a case-insensitive matching header, or `""` if it does not exist.
+
+ If more than one field with the specified name exists, the
+ first field defined by insertion order is returned.
+
+ @param name The name of the field.
+ */
+ string_view const
+ operator[](string_view name) const;
+
+ //--------------------------------------------------------------------------
+ //
+ // Iterators
+ //
+ //--------------------------------------------------------------------------
+
+ /// Return a const iterator to the beginning of the field sequence.
+ const_iterator
+ begin() const
+ {
+ return list_.cbegin();
+ }
+
+ /// Return a const iterator to the end of the field sequence.
+ const_iterator
+ end() const
+ {
+ return list_.cend();
+ }
+
+ /// Return a const iterator to the beginning of the field sequence.
+ const_iterator
+ cbegin() const
+ {
+ return list_.cbegin();
+ }
+
+ /// Return a const iterator to the end of the field sequence.
+ const_iterator
+ cend() const
+ {
+ return list_.cend();
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Capacity
+ //
+ //--------------------------------------------------------------------------
+
+private:
+ // VFALCO Since the header and message derive from Fields,
+ // what does the expression m.empty() mean? Its confusing.
+ bool
+ empty() const
+ {
+ return list_.empty();
+ }
+public:
+
+ //--------------------------------------------------------------------------
+ //
+ // Modifiers
+ //
+ //--------------------------------------------------------------------------
+
+ /** Remove all fields from the container
+
+ All references, pointers, or iterators referring to contained
+ elements are invalidated. All past-the-end iterators are also
+ invalidated.
+
+ @par Postconditions:
+ @code
+ std::distance(this->begin(), this->end()) == 0
+ @endcode
+ */
+ void
+ clear();
+
+ /** Insert a field.
+
+ If one or more fields with the same name already exist,
+ the new field will be inserted after the last field with
+ the matching name, in serialization order.
+
+ @param name The field name.
+
+ @param value The value of the field, as a @ref string_param
+ */
+ void
+ insert(field name, string_param const& value);
+
+ /** Insert a field.
+
+ If one or more fields with the same name already exist,
+ the new field will be inserted after the last field with
+ the matching name, in serialization order.
+
+ @param name The field name.
+
+ @param value The value of the field, as a @ref string_param
+ */
+ void
+ insert(string_view name, string_param const& value);
+
+ /** Insert a field.
+
+ If one or more fields with the same name already exist,
+ the new field will be inserted after the last field with
+ the matching name, in serialization order.
+
+ @param name The field name.
+
+ @param name_string The literal text corresponding to the
+ field name. If `name != field::unknown`, then this value
+ must be equal to `to_string(name)` using a case-insensitive
+ comparison, otherwise the behavior is undefined.
+
+ @param value The value of the field, as a @ref string_param
+ */
+ void
+ insert(field name, string_view name_string,
+ string_param const& value);
+
+ /** Set a field value, removing any other instances of that field.
+
+ First removes any values with matching field names, then
+ inserts the new field value.
+
+ @param name The field name.
+
+ @param value The value of the field, as a @ref string_param
+
+ @return The field value.
+ */
+ void
+ set(field name, string_param const& value);
+
+ /** Set a field value, removing any other instances of that field.
+
+ First removes any values with matching field names, then
+ inserts the new field value.
+
+ @param name The field name.
+
+ @param value The value of the field, as a @ref string_param
+ */
+ void
+ set(string_view name, string_param const& value);
+
+ /** Remove a field.
+
+ References and iterators to the erased elements are
+ invalidated. Other references and iterators are not
+ affected.
+
+ @param pos An iterator to the element to remove.
+
+ @return An iterator following the last removed element.
+ If the iterator refers to the last element, the end()
+ iterator is returned.
+ */
+ const_iterator
+ erase(const_iterator pos);
+
+ /** Remove all fields with the specified name.
+
+ All fields with the same field name are erased from the
+ container.
+ References and iterators to the erased elements are
+ invalidated. Other references and iterators are not
+ affected.
+
+ @param name The field name.
+
+ @return The number of fields removed.
+ */
+ std::size_t
+ erase(field name);
+
+ /** Remove all fields with the specified name.
+
+ All fields with the same field name are erased from the
+ container.
+ References and iterators to the erased elements are
+ invalidated. Other references and iterators are not
+ affected.
+
+ @param name The field name.
+
+ @return The number of fields removed.
+ */
+ std::size_t
+ erase(string_view name);
+
+ /** Return a buffer sequence representing the trailers.
+
+ This function returns a buffer sequence holding the
+ serialized representation of the trailer fields promised
+ in the Accept field. Before calling this function the
+ Accept field must contain the exact trailer fields
+ desired. Each field must also exist.
+ */
+
+
+ /// Swap this container with another
+ void
+ swap(basic_fields& other);
+
+ /// Swap two field containers
+ template<class Alloc>
+ friend
+ void
+ swap(basic_fields<Alloc>& lhs, basic_fields<Alloc>& rhs);
+
+ //--------------------------------------------------------------------------
+ //
+ // Lookup
+ //
+ //--------------------------------------------------------------------------
+
+ /** Return the number of fields with the specified name.
+
+ @param name The field name.
+ */
+ std::size_t
+ count(field name) const;
+
+ /** Return the number of fields with the specified name.
+
+ @param name The field name.
+ */
+ std::size_t
+ count(string_view name) const;
+
+ /** Returns an iterator to the case-insensitive matching field.
+
+ If more than one field with the specified name exists, the
+ first field defined by insertion order is returned.
+
+ @param name The field name.
+
+ @return An iterator to the matching field, or `end()` if
+ no match was found.
+ */
+ const_iterator
+ find(field name) const;
+
+ /** Returns an iterator to the case-insensitive matching field name.
+
+ If more than one field with the specified name exists, the
+ first field defined by insertion order is returned.
+
+ @param name The field name.
+
+ @return An iterator to the matching field, or `end()` if
+ no match was found.
+ */
+ const_iterator
+ find(string_view name) const;
+
+ /** Returns a range of iterators to the fields with the specified name.
+
+ @param name The field name.
+
+ @return A range of iterators to fields with the same name,
+ otherwise an empty range.
+ */
+ std::pair<const_iterator, const_iterator>
+ equal_range(field name) const;
+
+ /** Returns a range of iterators to the fields with the specified name.
+
+ @param name The field name.
+
+ @return A range of iterators to fields with the same name,
+ otherwise an empty range.
+ */
+ std::pair<const_iterator, const_iterator>
+ equal_range(string_view name) const;
+
+ //--------------------------------------------------------------------------
+ //
+ // Observers
+ //
+ //--------------------------------------------------------------------------
+
+ /// Returns a copy of the key comparison function
+ key_compare
+ key_comp() const
+ {
+ return key_compare{};
+ }
+
+protected:
+ /** Returns the request-method string.
+
+ @note Only called for requests.
+ */
+ string_view
+ get_method_impl() const;
+
+ /** Returns the request-target string.
+
+ @note Only called for requests.
+ */
+ string_view
+ get_target_impl() const;
+
+ /** Returns the response reason-phrase string.
+
+ @note Only called for responses.
+ */
+ string_view
+ get_reason_impl() const;
+
+ /** Returns the chunked Transfer-Encoding setting
+ */
+ bool
+ get_chunked_impl() const;
+
+ /** Returns the keep-alive setting
+ */
+ bool
+ get_keep_alive_impl(unsigned version) const;
+
+ /** Returns `true` if the Content-Length field is present.
+ */
+ bool
+ has_content_length_impl() const;
+
+ /** Set or clear the method string.
+
+ @note Only called for requests.
+ */
+ void
+ set_method_impl(string_view s);
+
+ /** Set or clear the target string.
+
+ @note Only called for requests.
+ */
+ void
+ set_target_impl(string_view s);
+
+ /** Set or clear the reason string.
+
+ @note Only called for responses.
+ */
+ void
+ set_reason_impl(string_view s);
+
+ /** Adjusts the chunked Transfer-Encoding value
+ */
+ void
+ set_chunked_impl(bool value);
+
+ /** Sets or clears the Content-Length field
+ */
+ void
+ set_content_length_impl(
+ boost::optional<std::uint64_t> const& value);
+
+ /** Adjusts the Connection field
+ */
+ void
+ set_keep_alive_impl(
+ unsigned version, bool keep_alive);
+
+private:
+ template<class OtherAlloc>
+ friend class basic_fields;
+
+ using base_alloc_type = typename
+ beast::detail::allocator_traits<Allocator>::
+ template rebind_alloc<value_type>;
+
+ using alloc_traits =
+ beast::detail::allocator_traits<base_alloc_type>;
+
+ using size_type = typename
+ beast::detail::allocator_traits<Allocator>::size_type;
+
+ value_type&
+ new_element(field name,
+ string_view sname, string_view value);
+
+ void
+ delete_element(value_type& e);
+
+ void
+ set_element(value_type& e);
+
+ void
+ realloc_string(string_view& dest, string_view s);
+
+ void
+ realloc_target(
+ string_view& dest, string_view s);
+
+ template<class OtherAlloc>
+ void
+ copy_all(basic_fields<OtherAlloc> const&);
+
+ void
+ clear_all();
+
+ void
+ delete_list();
+
+ void
+ move_assign(basic_fields&, std::true_type);
+
+ void
+ move_assign(basic_fields&, std::false_type);
+
+ void
+ copy_assign(basic_fields const&, std::true_type);
+
+ void
+ copy_assign(basic_fields const&, std::false_type);
+
+ void
+ swap(basic_fields& other, std::true_type);
+
+ void
+ swap(basic_fields& other, std::false_type);
+
+ base_alloc_type alloc_;
+ set_t set_;
+ list_t list_;
+ string_view method_;
+ string_view target_or_reason_;
+};
+
+/// A typical HTTP header fields container
+using fields = basic_fields<std::allocator<char>>;
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/fields.ipp>
+
+#endif
diff --git a/boost/beast/http/file_body.hpp b/boost/beast/http/file_body.hpp
new file mode 100644
index 0000000000..42e7a15330
--- /dev/null
+++ b/boost/beast/http/file_body.hpp
@@ -0,0 +1,35 @@
+//
+// 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_HTTP_FILE_BODY_HPP
+#define BOOST_BEAST_HTTP_FILE_BODY_HPP
+
+#include <boost/beast/core/file.hpp>
+#include <boost/beast/http/basic_file_body.hpp>
+#include <boost/assert.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
+#include <cstdio>
+#include <cstdint>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/// A message body represented by a file on the filesystem.
+using file_body = basic_file_body<file>;
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/file_body_win32.ipp>
+
+#endif
diff --git a/boost/beast/http/impl/basic_parser.ipp b/boost/beast/http/impl/basic_parser.ipp
new file mode 100644
index 0000000000..355a76fb16
--- /dev/null
+++ b/boost/beast/http/impl/basic_parser.ipp
@@ -0,0 +1,928 @@
+//
+// 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_HTTP_IMPL_BASIC_PARSER_IPP
+#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
+
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/make_unique.hpp>
+#include <algorithm>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<bool isRequest, class Derived>
+template<class OtherDerived>
+basic_parser<isRequest, Derived>::
+basic_parser(basic_parser<
+ isRequest, OtherDerived>&& other)
+ : body_limit_(other.body_limit_)
+ , len_(other.len_)
+ , buf_(std::move(other.buf_))
+ , buf_len_(other.buf_len_)
+ , skip_(other.skip_)
+ , header_limit_(other.header_limit_)
+ , status_(other.status_)
+ , state_(other.state_)
+ , f_(other.f_)
+{
+}
+
+template<bool isRequest, class Derived>
+bool
+basic_parser<isRequest, Derived>::
+keep_alive() const
+{
+ BOOST_ASSERT(is_header_done());
+ if(f_ & flagHTTP11)
+ {
+ if(f_ & flagConnectionClose)
+ return false;
+ }
+ else
+ {
+ if(! (f_ & flagConnectionKeepAlive))
+ return false;
+ }
+ return (f_ & flagNeedEOF) == 0;
+}
+
+template<bool isRequest, class Derived>
+boost::optional<std::uint64_t>
+basic_parser<isRequest, Derived>::
+content_length() const
+{
+ BOOST_ASSERT(is_header_done());
+ if(! (f_ & flagContentLength))
+ return boost::none;
+ return len_;
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+skip(bool v)
+{
+ BOOST_ASSERT(! got_some());
+ if(v)
+ f_ |= flagSkipBody;
+ else
+ f_ &= ~flagSkipBody;
+}
+
+template<bool isRequest, class Derived>
+template<class ConstBufferSequence>
+std::size_t
+basic_parser<isRequest, Derived>::
+put(ConstBufferSequence const& buffers,
+ error_code& ec)
+{
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ auto const p = boost::asio::buffer_sequence_begin(buffers);
+ auto const last = boost::asio::buffer_sequence_end(buffers);
+ if(p == last)
+ {
+ ec.assign(0, ec.category());
+ return 0;
+ }
+ if(std::next(p) == last)
+ {
+ // single buffer
+ return put(boost::asio::const_buffer(*p), ec);
+ }
+ auto const size = buffer_size(buffers);
+ if(size <= max_stack_buffer)
+ return put_from_stack(size, buffers, ec);
+ if(size > buf_len_)
+ {
+ // reallocate
+ buf_ = boost::make_unique_noinit<char[]>(size);
+ buf_len_ = size;
+ }
+ // flatten
+ buffer_copy(boost::asio::buffer(
+ buf_.get(), buf_len_), buffers);
+ return put(boost::asio::const_buffer{
+ buf_.get(), buf_len_}, ec);
+}
+
+template<bool isRequest, class Derived>
+std::size_t
+basic_parser<isRequest, Derived>::
+put(boost::asio::const_buffer const& buffer,
+ error_code& ec)
+{
+ BOOST_ASSERT(state_ != state::complete);
+ using boost::asio::buffer_size;
+ auto p = reinterpret_cast<
+ char const*>(buffer.data());
+ auto n = buffer.size();
+ auto const p0 = p;
+ auto const p1 = p0 + n;
+ ec.assign(0, ec.category());
+loop:
+ switch(state_)
+ {
+ case state::nothing_yet:
+ if(n == 0)
+ {
+ ec = error::need_more;
+ return 0;
+ }
+ state_ = state::start_line;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case state::start_line:
+ {
+ maybe_need_more(p, n, ec);
+ if(ec)
+ goto done;
+ parse_start_line(p, p + (std::min<std::size_t>)(
+ header_limit_, n), ec, is_request{});
+ if(ec)
+ {
+ if(ec == error::need_more)
+ {
+ if(n >= header_limit_)
+ {
+ ec = error::header_limit;
+ goto done;
+ }
+ if(p + 3 <= p1)
+ skip_ = static_cast<
+ std::size_t>(p1 - p - 3);
+ }
+ goto done;
+ }
+ BOOST_ASSERT(! is_done());
+ n = static_cast<std::size_t>(p1 - p);
+ if(p >= p1)
+ {
+ ec = error::need_more;
+ goto done;
+ }
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case state::fields:
+ maybe_need_more(p, n, ec);
+ if(ec)
+ goto done;
+ parse_fields(p, p + (std::min<std::size_t>)(
+ header_limit_, n), ec);
+ if(ec)
+ {
+ if(ec == error::need_more)
+ {
+ if(n >= header_limit_)
+ {
+ ec = error::header_limit;
+ goto done;
+ }
+ if(p + 3 <= p1)
+ skip_ = static_cast<
+ std::size_t>(p1 - p - 3);
+ }
+ goto done;
+ }
+ finish_header(ec, is_request{});
+ break;
+
+ case state::body0:
+ BOOST_ASSERT(! skip_);
+ impl().on_body_init_impl(content_length(), ec);
+ if(ec)
+ goto done;
+ state_ = state::body;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case state::body:
+ BOOST_ASSERT(! skip_);
+ parse_body(p, n, ec);
+ if(ec)
+ goto done;
+ break;
+
+ case state::body_to_eof0:
+ BOOST_ASSERT(! skip_);
+ impl().on_body_init_impl(content_length(), ec);
+ if(ec)
+ goto done;
+ state_ = state::body_to_eof;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case state::body_to_eof:
+ BOOST_ASSERT(! skip_);
+ parse_body_to_eof(p, n, ec);
+ if(ec)
+ goto done;
+ break;
+
+ case state::chunk_header0:
+ impl().on_body_init_impl(content_length(), ec);
+ if(ec)
+ goto done;
+ state_ = state::chunk_header;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case state::chunk_header:
+ parse_chunk_header(p, n, ec);
+ if(ec)
+ goto done;
+ break;
+
+ case state::chunk_body:
+ parse_chunk_body(p, n, ec);
+ if(ec)
+ goto done;
+ break;
+
+ case state::complete:
+ ec.assign(0, ec.category());
+ goto done;
+ }
+ if(p < p1 && ! is_done() && eager())
+ {
+ n = static_cast<std::size_t>(p1 - p);
+ goto loop;
+ }
+done:
+ return static_cast<std::size_t>(p - p0);
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+put_eof(error_code& ec)
+{
+ BOOST_ASSERT(got_some());
+ if( state_ == state::start_line ||
+ state_ == state::fields)
+ {
+ ec = error::partial_message;
+ return;
+ }
+ if(f_ & (flagContentLength | flagChunked))
+ {
+ if(state_ != state::complete)
+ {
+ ec = error::partial_message;
+ return;
+ }
+ ec.assign(0, ec.category());
+ return;
+ }
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ state_ = state::complete;
+}
+
+template<bool isRequest, class Derived>
+template<class ConstBufferSequence>
+std::size_t
+basic_parser<isRequest, Derived>::
+put_from_stack(std::size_t size,
+ ConstBufferSequence const& buffers,
+ error_code& ec)
+{
+ char buf[max_stack_buffer];
+ using boost::asio::buffer;
+ using boost::asio::buffer_copy;
+ buffer_copy(buffer(buf, sizeof(buf)), buffers);
+ return put(boost::asio::const_buffer{
+ buf, size}, ec);
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+maybe_need_more(
+ char const* p, std::size_t n,
+ error_code& ec)
+{
+ if(skip_ == 0)
+ return;
+ if( n > header_limit_)
+ n = header_limit_;
+ if(n < skip_ + 4)
+ {
+ ec = error::need_more;
+ return;
+ }
+ auto const term =
+ find_eom(p + skip_, p + n);
+ if(! term)
+ {
+ skip_ = n - 3;
+ if(skip_ + 4 > header_limit_)
+ {
+ ec = error::header_limit;
+ return;
+ }
+ ec = error::need_more;
+ return;
+ }
+ skip_ = 0;
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_start_line(
+ char const*& in, char const* last,
+ error_code& ec, std::true_type)
+{
+/*
+ request-line = method SP request-target SP HTTP-version CRLF
+ method = token
+*/
+ auto p = in;
+
+ string_view method;
+ parse_method(p, last, method, ec);
+ if(ec)
+ return;
+
+ string_view target;
+ parse_target(p, last, target, ec);
+ if(ec)
+ return;
+
+ int version = 0;
+ parse_version(p, last, version, ec);
+ if(ec)
+ return;
+ if(version < 10 || version > 11)
+ {
+ ec = error::bad_version;
+ return;
+ }
+
+ if(p + 2 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(p[0] != '\r' || p[1] != '\n')
+ {
+ ec = error::bad_version;
+ return;
+ }
+ p += 2;
+
+ if(version >= 11)
+ f_ |= flagHTTP11;
+
+ impl().on_request_impl(string_to_verb(method),
+ method, target, version, ec);
+ if(ec)
+ return;
+
+ in = p;
+ state_ = state::fields;
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_start_line(
+ char const*& in, char const* last,
+ error_code& ec, std::false_type)
+{
+/*
+ status-line = HTTP-version SP status-code SP reason-phrase CRLF
+ status-code = 3*DIGIT
+ reason-phrase = *( HTAB / SP / VCHAR / obs-text )
+*/
+ auto p = in;
+
+ int version = 0;
+ parse_version(p, last, version, ec);
+ if(ec)
+ return;
+ if(version < 10 || version > 11)
+ {
+ ec = error::bad_version;
+ return;
+ }
+
+ // SP
+ if(p + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*p++ != ' ')
+ {
+ ec = error::bad_version;
+ return;
+ }
+
+ parse_status(p, last, status_, ec);
+ if(ec)
+ return;
+
+ // parse reason CRLF
+ string_view reason;
+ parse_reason(p, last, reason, ec);
+ if(ec)
+ return;
+
+ if(version >= 11)
+ f_ |= flagHTTP11;
+
+ impl().on_response_impl(
+ status_, reason, version, ec);
+ if(ec)
+ return;
+
+ in = p;
+ state_ = state::fields;
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+parse_fields(char const*& in,
+ char const* last, error_code& ec)
+{
+ string_view name;
+ string_view value;
+ // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
+ static_string<max_obs_fold> buf;
+ auto p = in;
+ for(;;)
+ {
+ if(p + 2 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(p[0] == '\r')
+ {
+ if(p[1] != '\n')
+ ec = error::bad_line_ending;
+ in = p + 2;
+ return;
+ }
+ parse_field(p, last, name, value, buf, ec);
+ if(ec)
+ return;
+ auto const f = string_to_field(name);
+ do_field(f, value, ec);
+ if(ec)
+ return;
+ impl().on_field_impl(f, name, value, ec);
+ if(ec)
+ return;
+ in = p;
+ }
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+finish_header(error_code& ec, std::true_type)
+{
+ // RFC 7230 section 3.3
+ // https://tools.ietf.org/html/rfc7230#section-3.3
+
+ if(f_ & flagSkipBody)
+ {
+ state_ = state::complete;
+ }
+ else if(f_ & flagContentLength)
+ {
+ if(len_ > body_limit_)
+ {
+ ec = error::body_limit;
+ return;
+ }
+ if(len_ > 0)
+ {
+ f_ |= flagHasBody;
+ state_ = state::body0;
+ }
+ else
+ {
+ state_ = state::complete;
+ }
+ }
+ else if(f_ & flagChunked)
+ {
+ f_ |= flagHasBody;
+ state_ = state::chunk_header0;
+ }
+ else
+ {
+ len_ = 0;
+ state_ = state::complete;
+ }
+
+ impl().on_header_impl(ec);
+ if(ec)
+ return;
+ if(state_ == state::complete)
+ {
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ }
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+finish_header(error_code& ec, std::false_type)
+{
+ // RFC 7230 section 3.3
+ // https://tools.ietf.org/html/rfc7230#section-3.3
+
+ if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
+ status_ / 100 == 1 || // 1xx e.g. Continue
+ status_ == 204 || // No Content
+ status_ == 304) // Not Modified
+ {
+ // VFALCO Content-Length may be present, but we
+ // treat the message as not having a body.
+ // https://github.com/boostorg/beast/issues/692
+ state_ = state::complete;
+ }
+ else if(f_ & flagContentLength)
+ {
+ if(len_ > body_limit_)
+ {
+ ec = error::body_limit;
+ return;
+ }
+ if(len_ > 0)
+ {
+ f_ |= flagHasBody;
+ state_ = state::body0;
+ }
+ else
+ {
+ state_ = state::complete;
+ }
+ }
+ else if(f_ & flagChunked)
+ {
+ f_ |= flagHasBody;
+ state_ = state::chunk_header0;
+ }
+ else
+ {
+ f_ |= flagHasBody;
+ f_ |= flagNeedEOF;
+ state_ = state::body_to_eof0;
+ }
+
+ impl().on_header_impl(ec);
+ if(ec)
+ return;
+ if(state_ == state::complete)
+ {
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ }
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_body(char const*& p,
+ std::size_t n, error_code& ec)
+{
+ n = impl().on_body_impl(string_view{p,
+ beast::detail::clamp(len_, n)}, ec);
+ p += n;
+ len_ -= n;
+ if(ec)
+ return;
+ if(len_ > 0)
+ return;
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ state_ = state::complete;
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_body_to_eof(char const*& p,
+ std::size_t n, error_code& ec)
+{
+ if(n > body_limit_)
+ {
+ ec = error::body_limit;
+ return;
+ }
+ body_limit_ = body_limit_ - n;
+ n = impl().on_body_impl(string_view{p, n}, ec);
+ p += n;
+ if(ec)
+ return;
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+parse_chunk_header(char const*& p0,
+ std::size_t n, error_code& ec)
+{
+/*
+ chunked-body = *chunk last-chunk trailer-part CRLF
+
+ chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
+ last-chunk = 1*("0") [ chunk-ext ] CRLF
+ trailer-part = *( header-field CRLF )
+
+ chunk-size = 1*HEXDIG
+ chunk-data = 1*OCTET ; a sequence of chunk-size octets
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+*/
+
+ auto p = p0;
+ auto const pend = p + n;
+ char const* eol;
+
+ if(! (f_ & flagFinalChunk))
+ {
+ if(n < skip_ + 2)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(f_ & flagExpectCRLF)
+ {
+ // Treat the last CRLF in a chunk as
+ // part of the next chunk, so p can
+ // be parsed in one call instead of two.
+ if(! parse_crlf(p))
+ {
+ ec = error::bad_chunk;
+ return;
+ }
+ }
+ eol = find_eol(p0 + skip_, pend, ec);
+ if(ec)
+ return;
+ if(! eol)
+ {
+ ec = error::need_more;
+ skip_ = n - 1;
+ return;
+ }
+ skip_ = static_cast<
+ std::size_t>(eol - 2 - p0);
+
+ std::uint64_t size;
+ if(! parse_hex(p, size))
+ {
+ ec = error::bad_chunk;
+ return;
+ }
+ if(size != 0)
+ {
+ if(size > body_limit_)
+ {
+ ec = error::body_limit;
+ return;
+ }
+ body_limit_ -= size;
+ auto const start = p;
+ parse_chunk_extensions(p, pend, ec);
+ if(ec)
+ return;
+ if(p != eol -2 )
+ {
+ ec = error::bad_chunk_extension;
+ return;
+ }
+ auto const ext = make_string(start, p);
+ impl().on_chunk_header_impl(size, ext, ec);
+ if(ec)
+ return;
+ len_ = size;
+ skip_ = 2;
+ p0 = eol;
+ f_ |= flagExpectCRLF;
+ state_ = state::chunk_body;
+ return;
+ }
+
+ f_ |= flagFinalChunk;
+ }
+ else
+ {
+ BOOST_ASSERT(n >= 5);
+ if(f_ & flagExpectCRLF)
+ BOOST_VERIFY(parse_crlf(p));
+ std::uint64_t size;
+ BOOST_VERIFY(parse_hex(p, size));
+ eol = find_eol(p, pend, ec);
+ BOOST_ASSERT(! ec);
+ }
+
+ auto eom = find_eom(p0 + skip_, pend);
+ if(! eom)
+ {
+ BOOST_ASSERT(n >= 3);
+ skip_ = n - 3;
+ ec = error::need_more;
+ return;
+ }
+
+ auto const start = p;
+ parse_chunk_extensions(p, pend, ec);
+ if(ec)
+ return;
+ if(p != eol - 2)
+ {
+ ec = error::bad_chunk_extension;
+ return;
+ }
+ auto const ext = make_string(start, p);
+ impl().on_chunk_header_impl(0, ext, ec);
+ if(ec)
+ return;
+ p = eol;
+ parse_fields(p, eom, ec);
+ if(ec)
+ return;
+ BOOST_ASSERT(p == eom);
+ p0 = eom;
+
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ state_ = state::complete;
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_chunk_body(char const*& p,
+ std::size_t n, error_code& ec)
+{
+ n = impl().on_chunk_body_impl(
+ len_, string_view{p,
+ beast::detail::clamp(len_, n)}, ec);
+ p += n;
+ len_ -= n;
+ if(len_ == 0)
+ state_ = state::chunk_header;
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+do_field(field f,
+ string_view value, error_code& ec)
+{
+ // Connection
+ if(f == field::connection ||
+ f == field::proxy_connection)
+ {
+ auto const list = opt_token_list{value};
+ if(! validate_list(list))
+ {
+ // VFALCO Should this be a field specific error?
+ ec = error::bad_value;
+ return;
+ }
+ for(auto const& s : list)
+ {
+ if(iequals({"close", 5}, s))
+ {
+ f_ |= flagConnectionClose;
+ continue;
+ }
+
+ if(iequals({"keep-alive", 10}, s))
+ {
+ f_ |= flagConnectionKeepAlive;
+ continue;
+ }
+
+ if(iequals({"upgrade", 7}, s))
+ {
+ f_ |= flagConnectionUpgrade;
+ continue;
+ }
+ }
+ ec.assign(0, ec.category());
+ return;
+ }
+
+ // Content-Length
+ if(f == field::content_length)
+ {
+ if(f_ & flagContentLength)
+ {
+ // duplicate
+ ec = error::bad_content_length;
+ return;
+ }
+
+ if(f_ & flagChunked)
+ {
+ // conflicting field
+ ec = error::bad_content_length;
+ return;
+ }
+
+ std::uint64_t v;
+ if(! parse_dec(
+ value.begin(), value.end(), v))
+ {
+ ec = error::bad_content_length;
+ return;
+ }
+
+ ec.assign(0, ec.category());
+ len_ = v;
+ f_ |= flagContentLength;
+ return;
+ }
+
+ // Transfer-Encoding
+ if(f == field::transfer_encoding)
+ {
+ if(f_ & flagChunked)
+ {
+ // duplicate
+ ec = error::bad_transfer_encoding;
+ return;
+ }
+
+ if(f_ & flagContentLength)
+ {
+ // conflicting field
+ ec = error::bad_transfer_encoding;
+ return;
+ }
+
+ ec.assign(0, ec.category());
+ auto const v = token_list{value};
+ auto const p = std::find_if(v.begin(), v.end(),
+ [&](typename token_list::value_type const& s)
+ {
+ return iequals({"chunked", 7}, s);
+ });
+ if(p == v.end())
+ return;
+ if(std::next(p) != v.end())
+ return;
+ len_ = 0;
+ f_ |= flagChunked;
+ return;
+ }
+
+ // Upgrade
+ if(f == field::upgrade)
+ {
+ ec.assign(0, ec.category());
+ f_ |= flagUpgrade;
+ return;
+ }
+
+ ec.assign(0, ec.category());
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/chunk_encode.ipp b/boost/beast/http/impl/chunk_encode.ipp
new file mode 100644
index 0000000000..51296041f7
--- /dev/null
+++ b/boost/beast/http/impl/chunk_encode.ipp
@@ -0,0 +1,707 @@
+//
+// 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_HTTP_IMPL_CHUNK_ENCODE_IPP
+#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP
+
+#include <boost/beast/core/detail/varint.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/detail/rfc7230.hpp>
+#include <algorithm>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+inline
+chunk_header::
+chunk_header(std::size_t size)
+ : view_(
+ size,
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{})
+{
+ BOOST_ASSERT(size > 0);
+}
+
+inline
+chunk_header::
+chunk_header(
+ std::size_t size,
+ string_view extensions)
+ : view_(
+ size,
+ boost::asio::const_buffer{
+ extensions.data(), extensions.size()},
+ chunk_crlf{})
+{
+ BOOST_ASSERT(size > 0);
+}
+
+template<class ChunkExtensions, class>
+chunk_header::
+chunk_header(
+ std::size_t size,
+ ChunkExtensions&& extensions)
+ : exts_(std::make_shared<detail::chunk_extensions_impl<
+ typename std::decay<ChunkExtensions>::type>>(
+ std::forward<ChunkExtensions>(extensions)))
+ , view_(
+ size,
+ exts_->str(),
+ chunk_crlf{})
+{
+ static_assert(
+ detail::is_chunk_extensions<ChunkExtensions>::value,
+ "ChunkExtensions requirements not met");
+ BOOST_ASSERT(size > 0);
+}
+
+template<class ChunkExtensions, class Allocator, class>
+chunk_header::
+chunk_header(
+ std::size_t size,
+ ChunkExtensions&& extensions,
+ Allocator const& allocator)
+ : exts_(std::allocate_shared<detail::chunk_extensions_impl<
+ typename std::decay<ChunkExtensions>::type>>(allocator,
+ std::forward<ChunkExtensions>(extensions)))
+ , view_(
+ size,
+ exts_->str(),
+ chunk_crlf{})
+{
+ static_assert(
+ detail::is_chunk_extensions<ChunkExtensions>::value,
+ "ChunkExtensions requirements not met");
+ BOOST_ASSERT(size > 0);
+}
+
+//------------------------------------------------------------------------------
+
+template<class ConstBufferSequence>
+chunk_body<ConstBufferSequence>::
+chunk_body(ConstBufferSequence const& buffers)
+ : view_(
+ boost::asio::buffer_size(buffers),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ buffers,
+ chunk_crlf{})
+{
+}
+
+template<class ConstBufferSequence>
+chunk_body<ConstBufferSequence>::
+chunk_body(
+ ConstBufferSequence const& buffers,
+ string_view extensions)
+ : view_(
+ boost::asio::buffer_size(buffers),
+ boost::asio::const_buffer{
+ extensions.data(), extensions.size()},
+ chunk_crlf{},
+ buffers,
+ chunk_crlf{})
+{
+}
+
+template<class ConstBufferSequence>
+template<class ChunkExtensions, class>
+chunk_body<ConstBufferSequence>::
+chunk_body(
+ ConstBufferSequence const& buffers,
+ ChunkExtensions&& extensions)
+ : exts_(std::make_shared<detail::chunk_extensions_impl<
+ typename std::decay<ChunkExtensions>::type>>(
+ std::forward<ChunkExtensions>(extensions)))
+ , view_(
+ boost::asio::buffer_size(buffers),
+ exts_->str(),
+ chunk_crlf{},
+ buffers,
+ chunk_crlf{})
+{
+}
+
+template<class ConstBufferSequence>
+template<class ChunkExtensions, class Allocator, class>
+chunk_body<ConstBufferSequence>::
+chunk_body(
+ ConstBufferSequence const& buffers,
+ ChunkExtensions&& extensions,
+ Allocator const& allocator)
+ : exts_(std::allocate_shared<detail::chunk_extensions_impl<
+ typename std::decay<ChunkExtensions>::type>>(allocator,
+ std::forward<ChunkExtensions>(extensions)))
+ , view_(
+ boost::asio::buffer_size(buffers),
+ exts_->str(),
+ chunk_crlf{},
+ buffers,
+ chunk_crlf{})
+{
+}
+
+//------------------------------------------------------------------------------
+
+template<class Trailer>
+template<class Allocator>
+auto
+chunk_last<Trailer>::
+prepare(Trailer const& trailer, Allocator const& allocator) ->
+ buffers_type
+{
+ auto sp = std::allocate_shared<typename
+ Trailer::writer>(allocator, trailer);
+ sp_ = sp;
+ return sp->get();
+}
+
+template<class Trailer>
+auto
+chunk_last<Trailer>::
+prepare(Trailer const& trailer, std::true_type) ->
+ buffers_type
+{
+ auto sp = std::make_shared<
+ typename Trailer::writer>(trailer);
+ sp_ = sp;
+ return sp->get();
+}
+
+template<class Trailer>
+auto
+chunk_last<Trailer>::
+prepare(Trailer const& trailer, std::false_type) ->
+ buffers_type
+{
+ return trailer;
+}
+
+template<class Trailer>
+chunk_last<Trailer>::
+chunk_last()
+ : view_(
+ detail::chunk_size0{},
+ Trailer{})
+{
+}
+
+template<class Trailer>
+chunk_last<Trailer>::
+chunk_last(Trailer const& trailer)
+ : view_(
+ detail::chunk_size0{},
+ prepare(trailer, is_fields<Trailer>{}))
+{
+}
+
+template<class Trailer>
+template<class DeducedTrailer, class Allocator, class>
+chunk_last<Trailer>::
+chunk_last(
+ DeducedTrailer const& trailer, Allocator const& allocator)
+ : view_(
+ detail::chunk_size0{},
+ prepare(trailer, allocator))
+{
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+class basic_chunk_extensions<Allocator>::const_iterator
+{
+ friend class basic_chunk_extensions;
+
+ using iter_type = char const*;
+
+ iter_type it_;
+ typename basic_chunk_extensions::value_type value_;
+
+ explicit
+ const_iterator(iter_type it)
+ : it_(it)
+ {
+ }
+
+ void
+ increment();
+
+public:
+ using value_type = typename
+ basic_chunk_extensions::value_type;
+ using pointer = value_type const*;
+ using reference = value_type const&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::forward_iterator_tag;
+
+ const_iterator() = default;
+ const_iterator(const_iterator&& other) = default;
+ const_iterator(const_iterator const& other) = default;
+ const_iterator& operator=(const_iterator&& other) = default;
+ const_iterator& operator=(const_iterator const& other) = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return it_ == other.it_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*();
+
+ pointer
+ operator->()
+ {
+ return &(**this);
+ }
+
+ const_iterator&
+ operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ increment();
+ return temp;
+ }
+};
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+const_iterator::
+increment()
+{
+ using beast::detail::varint_read;
+ auto n = varint_read(it_);
+ it_ += n;
+ n = varint_read(it_);
+ it_ += n;
+}
+
+template<class Allocator>
+auto
+basic_chunk_extensions<Allocator>::
+const_iterator::
+operator*() ->
+ reference
+{
+ using beast::detail::varint_read;
+ auto it = it_;
+ auto n = varint_read(it);
+ value_.first = string_view{it, n};
+ it += n;
+ n = varint_read(it);
+ value_.second = string_view{it, n};
+ return value_;
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+template<class FwdIt>
+FwdIt
+basic_chunk_extensions<Allocator>::
+do_parse(FwdIt it, FwdIt last, error_code& ec)
+{
+/*
+ chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
+ BWS = *( SP / HTAB ) ; "Bad White Space"
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+ token = 1*tchar
+ quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
+ qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
+ quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
+ obs-text = %x80-FF
+
+ https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
+*/
+ using beast::detail::varint_size;
+ using beast::detail::varint_write;
+ using CharT = char;
+ using Traits = std::char_traits<CharT>;
+ range_.reserve(static_cast<std::size_t>(
+ std::distance(it, last) * 1.2));
+ range_.resize(0);
+ auto const emit_string =
+ [this](FwdIt from, FwdIt to)
+ {
+ auto const len =
+ std::distance(from, to);
+ auto const offset = range_.size();
+ range_.resize(
+ offset +
+ varint_size(len) +
+ len);
+ auto dest = &range_[offset];
+ varint_write(dest, len);
+ Traits::copy(dest, from, len);
+ };
+ auto const emit_string_plus_empty =
+ [this](FwdIt from, FwdIt to)
+ {
+ auto const len =
+ std::distance(from, to);
+ auto const offset = range_.size();
+ range_.resize(
+ offset +
+ varint_size(len) +
+ len +
+ varint_size(0));
+ auto dest = &range_[offset];
+ varint_write(dest, len);
+ Traits::copy(dest, from, len);
+ dest += len;
+ varint_write(dest, 0);
+ };
+ auto const emit_empty_string =
+ [this]
+ {
+ auto const offset = range_.size();
+ range_.resize(offset + varint_size(0));
+ auto dest = &range_[offset];
+ varint_write(dest, 0);
+ };
+loop:
+ if(it == last)
+ {
+ ec.assign(0, ec.category());
+ return it;
+ }
+ // BWS
+ if(*it == ' ' || *it == '\t')
+ {
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ }
+ }
+ // ';'
+ if(*it != ';')
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+semi:
+ ++it; // skip ';'
+ // BWS
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ }
+ // chunk-ext-name
+ {
+ if(! detail::is_token_char(*it))
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ auto const first = it;
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ emit_string_plus_empty(first, it);
+ return it;
+ }
+ if(! detail::is_token_char(*it))
+ break;
+ }
+ emit_string(first, it);
+ }
+ // BWS [ ";" / "=" ]
+ for(;;)
+ {
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ }
+ if(*it == ';')
+ {
+ emit_empty_string();
+ goto semi;
+ }
+ if(*it != '=')
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ ++it; // skip '='
+ // BWS
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ }
+ // chunk-ext-val
+ if(*it != '"')
+ {
+ // token
+ if(! detail::is_token_char(*it))
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ auto const first = it;
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ break;
+ if(! detail::is_token_char(*it))
+ break;
+ }
+ emit_string(first, it);
+ if(it == last)
+ return it;
+ }
+ else
+ {
+ // quoted-string
+ auto const first = ++it; // skip DQUOTE
+ // first pass, count chars
+ std::size_t len = 0;
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ if(*it == '"')
+ break;
+ if(*it == '\\')
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ }
+ ++len;
+ ++it;
+ }
+ // now build the string
+ auto const offset = range_.size();
+ range_.resize(
+ offset +
+ varint_size(len) +
+ len);
+ auto dest = &range_[offset];
+ varint_write(dest, len);
+ it = first;
+ for(;;)
+ {
+ BOOST_ASSERT(it != last);
+ if(*it == '"')
+ break;
+ if(*it == '\\')
+ {
+ ++it;
+ BOOST_ASSERT(it != last);
+ }
+ Traits::assign(*dest++, *it++);
+ }
+ ++it; // skip DQUOTE
+ }
+ goto loop;
+}
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+do_insert(string_view name, string_view value)
+{
+/*
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+ token = 1*tchar
+ quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
+ qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
+ quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
+ obs-text = %x80-FF
+*/
+ if(value.empty())
+ {
+ s_.reserve(1 + name.size());
+ s_.push_back(';');
+ s_.append(name.data(), name.size());
+ return;
+ }
+
+ bool is_token = true;
+ for(auto const c : value)
+ {
+ if(! detail::is_token_char(c))
+ {
+ is_token = false;
+ break;
+ }
+ }
+ if(is_token)
+ {
+ // token
+ s_.reserve(1 + name.size() + 1 + value.size());
+ s_.push_back(';');
+ s_.append(name.data(), name.size());
+ s_.push_back('=');
+ s_.append(value.data(), value.size());
+ }
+ else
+ {
+ // quoted-string
+ s_.reserve(
+ 1 + name.size() + 1 +
+ 1 + value.size() + 20 + 1);
+ s_.push_back(';');
+ s_.append(name.data(), name.size());
+ s_.append("=\"", 2);
+ for(auto const c : value)
+ {
+ if(c == '\\')
+ s_.append(R"(\\)", 2);
+ else if(c == '\"')
+ s_.append(R"(\")", 2);
+ else
+ s_.push_back(c);
+ }
+ s_.push_back('"');
+ }
+}
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+parse(string_view s, error_code& ec)
+{
+ do_parse(s.data(), s.data() + s.size(), ec);
+ if(! ec)
+ {
+ s_.clear();
+ for(auto const& v : *this)
+ do_insert(v.first, v.second);
+ }
+}
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+insert(string_view name)
+{
+ do_insert(name, {});
+
+ using beast::detail::varint_size;
+ using beast::detail::varint_write;
+ auto const offset = range_.size();
+ range_.resize(
+ offset +
+ varint_size(name.size()) +
+ name.size() +
+ varint_size(0));
+ auto dest = &range_[offset];
+ varint_write(dest, name.size());
+ std::memcpy(dest, name.data(), name.size());
+ dest += name.size();
+ varint_write(dest, 0);
+}
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+insert(string_view name, string_view value)
+{
+ do_insert(name, value);
+
+ using beast::detail::varint_size;
+ using beast::detail::varint_write;
+ auto const offset = range_.size();
+ range_.resize(
+ offset +
+ varint_size(name.size()) +
+ name.size() +
+ varint_size(value.size()) +
+ value.size());
+ auto dest = &range_[offset];
+ varint_write(dest, name.size());
+ std::memcpy(dest, name.data(), name.size());
+ dest += name.size();
+ varint_write(dest, value.size());
+ std::memcpy(dest, value.data(), value.size());
+}
+
+template<class Allocator>
+inline
+auto
+basic_chunk_extensions<Allocator>::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{range_.data()};
+}
+
+template<class Allocator>
+inline
+auto
+basic_chunk_extensions<Allocator>::
+end() const ->
+ const_iterator
+{
+ return const_iterator{
+ range_.data() + range_.size()};
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/error.ipp b/boost/beast/http/impl/error.ipp
new file mode 100644
index 0000000000..45075f5c18
--- /dev/null
+++ b/boost/beast/http/impl/error.ipp
@@ -0,0 +1,120 @@
+//
+// 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_HTTP_IMPL_ERROR_IPP
+#define BOOST_BEAST_HTTP_IMPL_ERROR_IPP
+
+#include <type_traits>
+
+namespace boost {
+
+namespace system {
+template<>
+struct is_error_code_enum<beast::http::error>
+{
+ static bool const value = true;
+};
+} // system
+
+namespace beast {
+namespace http {
+namespace detail {
+
+class http_error_category : public error_category
+{
+public:
+ const char*
+ name() const noexcept override
+ {
+ return "beast.http";
+ }
+
+ std::string
+ message(int ev) const override
+ {
+ switch(static_cast<error>(ev))
+ {
+ case error::end_of_stream: return "end of stream";
+ case error::partial_message: return "partial message";
+ case error::need_more: return "need more";
+ case error::unexpected_body: return "unexpected body";
+ case error::need_buffer: return "need buffer";
+ case error::end_of_chunk: return "end of chunk";
+ case error::buffer_overflow: return "buffer overflow";
+ case error::header_limit: return "header limit exceeded";
+ case error::body_limit: return "body limit exceeded";
+ case error::bad_alloc: return "bad alloc";
+ case error::bad_line_ending: return "bad line ending";
+ case error::bad_method: return "bad method";
+ case error::bad_target: return "bad target";
+ case error::bad_version: return "bad version";
+ case error::bad_status: return "bad status";
+ case error::bad_reason: return "bad reason";
+ case error::bad_field: return "bad field";
+ case error::bad_value: return "bad value";
+ case error::bad_content_length: return "bad Content-Length";
+ case error::bad_transfer_encoding: return "bad Transfer-Encoding";
+ case error::bad_chunk: return "bad chunk";
+ case error::bad_chunk_extension: return "bad chunk extension";
+ case error::bad_obs_fold: return "bad obs-fold";
+
+ default:
+ return "beast.http error";
+ }
+ }
+
+ error_condition
+ default_error_condition(
+ int ev) const noexcept override
+ {
+ return error_condition{ev, *this};
+ }
+
+ bool
+ equivalent(int ev,
+ error_condition const& condition
+ ) const noexcept override
+ {
+ return condition.value() == ev &&
+ &condition.category() == this;
+ }
+
+ bool
+ equivalent(error_code const& error,
+ int ev) const noexcept override
+ {
+ return error.value() == ev &&
+ &error.category() == this;
+ }
+};
+
+inline
+error_category const&
+get_http_error_category()
+{
+ static http_error_category const cat{};
+ return cat;
+}
+
+} // detail
+
+inline
+error_code
+make_error_code(error ev)
+{
+ return error_code{
+ static_cast<std::underlying_type<error>::type>(ev),
+ detail::get_http_error_category()};
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/field.ipp b/boost/beast/http/impl/field.ipp
new file mode 100644
index 0000000000..8da470ff24
--- /dev/null
+++ b/boost/beast/http/impl/field.ipp
@@ -0,0 +1,561 @@
+//
+// 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_HTTP_IMPL_FIELD_IPP
+#define BOOST_BEAST_HTTP_IMPL_FIELD_IPP
+
+#include <boost/beast/core/string.hpp>
+#include <algorithm>
+#include <array>
+#include <unordered_map>
+#include <vector>
+#include <boost/assert.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+
+struct field_table
+{
+ using array_type =
+ std::array<string_view, 352>;
+
+ struct hash
+ {
+ std::size_t
+ operator()(string_view s) const
+ {
+ auto const n = s.size();
+ return
+ beast::detail::ascii_tolower(s[0]) *
+ beast::detail::ascii_tolower(s[n/2]) ^
+ beast::detail::ascii_tolower(s[n-1]); // hist[] = 331, 10, max_load_factor = 0.15f
+ }
+ };
+
+ struct iequal
+ {
+ // assumes inputs have equal length
+ bool
+ operator()(
+ string_view lhs,
+ string_view rhs) const
+ {
+ auto p1 = lhs.data();
+ auto p2 = rhs.data();
+ auto pend = lhs.end();
+ char a, b;
+ while(p1 < pend)
+ {
+ a = *p1++;
+ b = *p2++;
+ if(a != b)
+ goto slow;
+ }
+ return true;
+
+ while(p1 < pend)
+ {
+ slow:
+ if( beast::detail::ascii_tolower(a) !=
+ beast::detail::ascii_tolower(b))
+ return false;
+ a = *p1++;
+ b = *p2++;
+ }
+ return true;
+ }
+ };
+
+ using map_type = std::unordered_map<
+ string_view, field, hash, iequal>;
+
+ array_type by_name_;
+ std::vector<map_type> by_size_;
+/*
+ From:
+
+ https://www.iana.org/assignments/message-headers/message-headers.xhtml
+*/
+ field_table()
+ : by_name_({{
+ "<unknown-field>",
+ "A-IM",
+ "Accept",
+ "Accept-Additions",
+ "Accept-Charset",
+ "Accept-Datetime",
+ "Accept-Encoding",
+ "Accept-Features",
+ "Accept-Language",
+ "Accept-Patch",
+ "Accept-Post",
+ "Accept-Ranges",
+ "Access-Control",
+ "Access-Control-Allow-Credentials",
+ "Access-Control-Allow-Headers",
+ "Access-Control-Allow-Methods",
+ "Access-Control-Allow-Origin",
+ "Access-Control-Max-Age",
+ "Access-Control-Request-Headers",
+ "Access-Control-Request-Method",
+ "Age",
+ "Allow",
+ "ALPN",
+ "Also-Control",
+ "Alt-Svc",
+ "Alt-Used",
+ "Alternate-Recipient",
+ "Alternates",
+ "Apparently-To",
+ "Apply-To-Redirect-Ref",
+ "Approved",
+ "Archive",
+ "Archived-At",
+ "Article-Names",
+ "Article-Updates",
+ "Authentication-Control",
+ "Authentication-Info",
+ "Authentication-Results",
+ "Authorization",
+ "Auto-Submitted",
+ "Autoforwarded",
+ "Autosubmitted",
+ "Base",
+ "Bcc",
+ "Body",
+ "C-Ext",
+ "C-Man",
+ "C-Opt",
+ "C-PEP",
+ "C-PEP-Info",
+ "Cache-Control",
+ "CalDAV-Timezones",
+ "Cancel-Key",
+ "Cancel-Lock",
+ "Cc",
+ "Close",
+ "Comments",
+ "Compliance",
+ "Connection",
+ "Content-Alternative",
+ "Content-Base",
+ "Content-Description",
+ "Content-Disposition",
+ "Content-Duration",
+ "Content-Encoding",
+ "Content-features",
+ "Content-ID",
+ "Content-Identifier",
+ "Content-Language",
+ "Content-Length",
+ "Content-Location",
+ "Content-MD5",
+ "Content-Range",
+ "Content-Return",
+ "Content-Script-Type",
+ "Content-Style-Type",
+ "Content-Transfer-Encoding",
+ "Content-Type",
+ "Content-Version",
+ "Control",
+ "Conversion",
+ "Conversion-With-Loss",
+ "Cookie",
+ "Cookie2",
+ "Cost",
+ "DASL",
+ "Date",
+ "Date-Received",
+ "DAV",
+ "Default-Style",
+ "Deferred-Delivery",
+ "Delivery-Date",
+ "Delta-Base",
+ "Depth",
+ "Derived-From",
+ "Destination",
+ "Differential-ID",
+ "Digest",
+ "Discarded-X400-IPMS-Extensions",
+ "Discarded-X400-MTS-Extensions",
+ "Disclose-Recipients",
+ "Disposition-Notification-Options",
+ "Disposition-Notification-To",
+ "Distribution",
+ "DKIM-Signature",
+ "DL-Expansion-History",
+ "Downgraded-Bcc",
+ "Downgraded-Cc",
+ "Downgraded-Disposition-Notification-To",
+ "Downgraded-Final-Recipient",
+ "Downgraded-From",
+ "Downgraded-In-Reply-To",
+ "Downgraded-Mail-From",
+ "Downgraded-Message-Id",
+ "Downgraded-Original-Recipient",
+ "Downgraded-Rcpt-To",
+ "Downgraded-References",
+ "Downgraded-Reply-To",
+ "Downgraded-Resent-Bcc",
+ "Downgraded-Resent-Cc",
+ "Downgraded-Resent-From",
+ "Downgraded-Resent-Reply-To",
+ "Downgraded-Resent-Sender",
+ "Downgraded-Resent-To",
+ "Downgraded-Return-Path",
+ "Downgraded-Sender",
+ "Downgraded-To",
+ "EDIINT-Features",
+ "Eesst-Version",
+ "Encoding",
+ "Encrypted",
+ "Errors-To",
+ "ETag",
+ "Expect",
+ "Expires",
+ "Expiry-Date",
+ "Ext",
+ "Followup-To",
+ "Forwarded",
+ "From",
+ "Generate-Delivery-Report",
+ "GetProfile",
+ "Hobareg",
+ "Host",
+ "HTTP2-Settings",
+ "If",
+ "If-Match",
+ "If-Modified-Since",
+ "If-None-Match",
+ "If-Range",
+ "If-Schedule-Tag-Match",
+ "If-Unmodified-Since",
+ "IM",
+ "Importance",
+ "In-Reply-To",
+ "Incomplete-Copy",
+ "Injection-Date",
+ "Injection-Info",
+ "Jabber-ID",
+ "Keep-Alive",
+ "Keywords",
+ "Label",
+ "Language",
+ "Last-Modified",
+ "Latest-Delivery-Time",
+ "Lines",
+ "Link",
+ "List-Archive",
+ "List-Help",
+ "List-ID",
+ "List-Owner",
+ "List-Post",
+ "List-Subscribe",
+ "List-Unsubscribe",
+ "List-Unsubscribe-Post",
+ "Location",
+ "Lock-Token",
+ "Man",
+ "Max-Forwards",
+ "Memento-Datetime",
+ "Message-Context",
+ "Message-ID",
+ "Message-Type",
+ "Meter",
+ "Method-Check",
+ "Method-Check-Expires",
+ "MIME-Version",
+ "MMHS-Acp127-Message-Identifier",
+ "MMHS-Authorizing-Users",
+ "MMHS-Codress-Message-Indicator",
+ "MMHS-Copy-Precedence",
+ "MMHS-Exempted-Address",
+ "MMHS-Extended-Authorisation-Info",
+ "MMHS-Handling-Instructions",
+ "MMHS-Message-Instructions",
+ "MMHS-Message-Type",
+ "MMHS-Originator-PLAD",
+ "MMHS-Originator-Reference",
+ "MMHS-Other-Recipients-Indicator-CC",
+ "MMHS-Other-Recipients-Indicator-To",
+ "MMHS-Primary-Precedence",
+ "MMHS-Subject-Indicator-Codes",
+ "MT-Priority",
+ "Negotiate",
+ "Newsgroups",
+ "NNTP-Posting-Date",
+ "NNTP-Posting-Host",
+ "Non-Compliance",
+ "Obsoletes",
+ "Opt",
+ "Optional",
+ "Optional-WWW-Authenticate",
+ "Ordering-Type",
+ "Organization",
+ "Origin",
+ "Original-Encoded-Information-Types",
+ "Original-From",
+ "Original-Message-ID",
+ "Original-Recipient",
+ "Original-Sender",
+ "Original-Subject",
+ "Originator-Return-Address",
+ "Overwrite",
+ "P3P",
+ "Path",
+ "PEP",
+ "Pep-Info",
+ "PICS-Label",
+ "Position",
+ "Posting-Version",
+ "Pragma",
+ "Prefer",
+ "Preference-Applied",
+ "Prevent-NonDelivery-Report",
+ "Priority",
+ "Privicon",
+ "ProfileObject",
+ "Protocol",
+ "Protocol-Info",
+ "Protocol-Query",
+ "Protocol-Request",
+ "Proxy-Authenticate",
+ "Proxy-Authentication-Info",
+ "Proxy-Authorization",
+ "Proxy-Connection",
+ "Proxy-Features",
+ "Proxy-Instruction",
+ "Public",
+ "Public-Key-Pins",
+ "Public-Key-Pins-Report-Only",
+ "Range",
+ "Received",
+ "Received-SPF",
+ "Redirect-Ref",
+ "References",
+ "Referer",
+ "Referer-Root",
+ "Relay-Version",
+ "Reply-By",
+ "Reply-To",
+ "Require-Recipient-Valid-Since",
+ "Resent-Bcc",
+ "Resent-Cc",
+ "Resent-Date",
+ "Resent-From",
+ "Resent-Message-ID",
+ "Resent-Reply-To",
+ "Resent-Sender",
+ "Resent-To",
+ "Resolution-Hint",
+ "Resolver-Location",
+ "Retry-After",
+ "Return-Path",
+ "Safe",
+ "Schedule-Reply",
+ "Schedule-Tag",
+ "Sec-WebSocket-Accept",
+ "Sec-WebSocket-Extensions",
+ "Sec-WebSocket-Key",
+ "Sec-WebSocket-Protocol",
+ "Sec-WebSocket-Version",
+ "Security-Scheme",
+ "See-Also",
+ "Sender",
+ "Sensitivity",
+ "Server",
+ "Set-Cookie",
+ "Set-Cookie2",
+ "SetProfile",
+ "SIO-Label",
+ "SIO-Label-History",
+ "SLUG",
+ "SoapAction",
+ "Solicitation",
+ "Status-URI",
+ "Strict-Transport-Security",
+ "Subject",
+ "SubOK",
+ "Subst",
+ "Summary",
+ "Supersedes",
+ "Surrogate-Capability",
+ "Surrogate-Control",
+ "TCN",
+ "TE",
+ "Timeout",
+ "Title",
+ "To",
+ "Topic",
+ "Trailer",
+ "Transfer-Encoding",
+ "TTL",
+ "UA-Color",
+ "UA-Media",
+ "UA-Pixels",
+ "UA-Resolution",
+ "UA-Windowpixels",
+ "Upgrade",
+ "Urgency",
+ "URI",
+ "User-Agent",
+ "Variant-Vary",
+ "Vary",
+ "VBR-Info",
+ "Version",
+ "Via",
+ "Want-Digest",
+ "Warning",
+ "WWW-Authenticate",
+ "X-Archived-At",
+ "X-Device-Accept",
+ "X-Device-Accept-Charset",
+ "X-Device-Accept-Encoding",
+ "X-Device-Accept-Language",
+ "X-Device-User-Agent",
+ "X-Frame-Options",
+ "X-Mittente",
+ "X-PGP-Sig",
+ "X-Ricevuta",
+ "X-Riferimento-Message-ID",
+ "X-TipoRicevuta",
+ "X-Trasporto",
+ "X-VerificaSicurezza",
+ "X400-Content-Identifier",
+ "X400-Content-Return",
+ "X400-Content-Type",
+ "X400-MTS-Identifier",
+ "X400-Originator",
+ "X400-Received",
+ "X400-Recipients",
+ "X400-Trace",
+ "Xref"
+ }})
+ {
+ // find the longest field length
+ std::size_t high = 0;
+ for(auto const& s : by_name_)
+ if(high < s.size())
+ high = s.size();
+ // build by_size map
+ // skip field::unknown
+ by_size_.resize(high + 1);
+ for(auto& map : by_size_)
+ map.max_load_factor(.15f);
+ for(std::size_t i = 1;
+ i < by_name_.size(); ++i)
+ {
+ auto const& s = by_name_[i];
+ by_size_[s.size()].emplace(
+ s, static_cast<field>(i));
+ }
+
+#if 0
+ // This snippet calculates the performance
+ // of the hash function and map settings
+ {
+ std::vector<std::size_t> hist;
+ for(auto const& map : by_size_)
+ {
+ for(std::size_t i = 0; i < map.bucket_count(); ++i)
+ {
+ auto const n = map.bucket_size(i);
+ if(n > 0)
+ {
+ if(hist.size() < n)
+ hist.resize(n);
+ ++hist[n-1];
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ field
+ string_to_field(string_view s) const
+ {
+ if(s.size() >= by_size_.size())
+ return field::unknown;
+ auto const& map = by_size_[s.size()];
+ if(map.empty())
+ return field::unknown;
+ auto it = map.find(s);
+ if(it == map.end())
+ return field::unknown;
+ return it->second;
+ }
+
+ //
+ // Deprecated
+ //
+
+ using const_iterator =
+ array_type::const_iterator;
+
+ std::size_t
+ size() const
+ {
+ return by_name_.size();
+ }
+
+ const_iterator
+ begin() const
+ {
+ return by_name_.begin();
+ }
+
+ const_iterator
+ end() const
+ {
+ return by_name_.end();
+ }
+};
+
+inline
+field_table const&
+get_field_table()
+{
+ static field_table const tab;
+ return tab;
+}
+
+template<class = void>
+string_view
+to_string(field f)
+{
+ auto const& v = get_field_table();
+ BOOST_ASSERT(static_cast<unsigned>(f) < v.size());
+ return v.begin()[static_cast<unsigned>(f)];
+}
+
+} // detail
+
+inline
+string_view
+to_string(field f)
+{
+ return detail::to_string(f);
+}
+
+inline
+field
+string_to_field(string_view s)
+{
+ return detail::get_field_table().string_to_field(s);
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/fields.ipp b/boost/beast/http/impl/fields.ipp
new file mode 100644
index 0000000000..67d7cc45b4
--- /dev/null
+++ b/boost/beast/http/impl/fields.ipp
@@ -0,0 +1,1380 @@
+//
+// 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_HTTP_IMPL_FIELDS_IPP
+#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
+
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/detail/buffers_ref.hpp>
+#include <boost/beast/http/verb.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#include <boost/beast/http/status.hpp>
+#include <boost/beast/http/chunk_encode.hpp>
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+#include <string>
+
+#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
+#ifndef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+#define BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+#endif
+#endif
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<class Allocator>
+class basic_fields<Allocator>::writer
+{
+public:
+ using iter_type = typename list_t::const_iterator;
+
+ struct field_iterator
+ {
+ iter_type it_;
+
+ using value_type = boost::asio::const_buffer;
+ using pointer = value_type const*;
+ using reference = value_type const;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ field_iterator() = default;
+ field_iterator(field_iterator&& other) = default;
+ field_iterator(field_iterator const& other) = default;
+ field_iterator& operator=(field_iterator&& other) = default;
+ field_iterator& operator=(field_iterator const& other) = default;
+
+ explicit
+ field_iterator(iter_type it)
+ : it_(it)
+ {
+ }
+
+ bool
+ operator==(field_iterator const& other) const
+ {
+ return it_ == other.it_;
+ }
+
+ bool
+ operator!=(field_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return it_->buffer();
+ }
+
+ field_iterator&
+ operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ field_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ field_iterator&
+ operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ field_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ --(*this);
+ return temp;
+ }
+ };
+
+ class field_range
+ {
+ field_iterator first_;
+ field_iterator last_;
+
+ public:
+ using const_iterator =
+ field_iterator;
+
+ using value_type =
+ typename const_iterator::value_type;
+
+ field_range(iter_type first, iter_type last)
+ : first_(first)
+ , last_(last)
+ {
+ }
+
+ const_iterator
+ begin() const
+ {
+ return first_;
+ }
+
+ const_iterator
+ end() const
+ {
+ return last_;
+ }
+ };
+
+ using view_type = buffers_cat_view<
+ boost::asio::const_buffer,
+ boost::asio::const_buffer,
+ boost::asio::const_buffer,
+ field_range,
+ chunk_crlf>;
+
+ basic_fields const& f_;
+ boost::optional<view_type> view_;
+ char buf_[13];
+
+public:
+ using const_buffers_type =
+ beast::detail::buffers_ref<view_type>;
+
+ writer(basic_fields const& f,
+ unsigned version, verb v);
+
+ writer(basic_fields const& f,
+ unsigned version, unsigned code);
+
+ writer(basic_fields const& f);
+
+ const_buffers_type
+ get() const
+ {
+ return const_buffers_type(*view_);
+ }
+};
+
+template<class Allocator>
+basic_fields<Allocator>::writer::
+writer(basic_fields const& f)
+ : f_(f)
+{
+ view_.emplace(
+ boost::asio::const_buffer{nullptr, 0},
+ boost::asio::const_buffer{nullptr, 0},
+ boost::asio::const_buffer{nullptr, 0},
+ field_range(f_.list_.begin(), f_.list_.end()),
+ chunk_crlf());
+}
+
+template<class Allocator>
+basic_fields<Allocator>::writer::
+writer(basic_fields const& f,
+ unsigned version, verb v)
+ : f_(f)
+{
+/*
+ request
+ "<method>"
+ " <target>"
+ " HTTP/X.Y\r\n" (11 chars)
+*/
+ string_view sv;
+ if(v == verb::unknown)
+ sv = f_.get_method_impl();
+ else
+ sv = to_string(v);
+
+ // target_or_reason_ has a leading SP
+
+ buf_[0] = ' ';
+ buf_[1] = 'H';
+ buf_[2] = 'T';
+ buf_[3] = 'T';
+ buf_[4] = 'P';
+ buf_[5] = '/';
+ buf_[6] = '0' + static_cast<char>(version / 10);
+ buf_[7] = '.';
+ buf_[8] = '0' + static_cast<char>(version % 10);
+ buf_[9] = '\r';
+ buf_[10]= '\n';
+
+ view_.emplace(
+ boost::asio::const_buffer{sv.data(), sv.size()},
+ boost::asio::const_buffer{
+ f_.target_or_reason_.data(),
+ f_.target_or_reason_.size()},
+ boost::asio::const_buffer{buf_, 11},
+ field_range(f_.list_.begin(), f_.list_.end()),
+ chunk_crlf());
+}
+
+template<class Allocator>
+basic_fields<Allocator>::writer::
+writer(basic_fields const& f,
+ unsigned version, unsigned code)
+ : f_(f)
+{
+/*
+ response
+ "HTTP/X.Y ### " (13 chars)
+ "<reason>"
+ "\r\n"
+*/
+ buf_[0] = 'H';
+ buf_[1] = 'T';
+ buf_[2] = 'T';
+ buf_[3] = 'P';
+ buf_[4] = '/';
+ buf_[5] = '0' + static_cast<char>(version / 10);
+ buf_[6] = '.';
+ buf_[7] = '0' + static_cast<char>(version % 10);
+ buf_[8] = ' ';
+ buf_[9] = '0' + static_cast<char>(code / 100);
+ buf_[10]= '0' + static_cast<char>((code / 10) % 10);
+ buf_[11]= '0' + static_cast<char>(code % 10);
+ buf_[12]= ' ';
+
+ string_view sv;
+ if(! f_.target_or_reason_.empty())
+ sv = f_.target_or_reason_;
+ else
+ sv = obsolete_reason(static_cast<status>(code));
+
+ view_.emplace(
+ boost::asio::const_buffer{buf_, 13},
+ boost::asio::const_buffer{sv.data(), sv.size()},
+ boost::asio::const_buffer{"\r\n", 2},
+ field_range(f_.list_.begin(), f_.list_.end()),
+ chunk_crlf{});
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+basic_fields<Allocator>::
+value_type::
+value_type(field name,
+ string_view sname, string_view value)
+ : off_(static_cast<off_t>(sname.size() + 2))
+ , len_(static_cast<off_t>(value.size()))
+ , f_(name)
+{
+ //BOOST_ASSERT(name == field::unknown ||
+ // iequals(sname, to_string(name)));
+ char* p = reinterpret_cast<char*>(this + 1);
+ p[off_-2] = ':';
+ p[off_-1] = ' ';
+ p[off_ + len_] = '\r';
+ p[off_ + len_ + 1] = '\n';
+ std::memcpy(p, sname.data(), sname.size());
+ std::memcpy(p + off_, value.data(), value.size());
+}
+
+template<class Allocator>
+inline
+field const
+basic_fields<Allocator>::
+value_type::
+name() const
+{
+ return f_;
+}
+
+template<class Allocator>
+inline
+string_view const
+basic_fields<Allocator>::
+value_type::
+name_string() const
+{
+ return {reinterpret_cast<
+ char const*>(this + 1),
+ static_cast<std::size_t>(off_ - 2)};
+}
+
+template<class Allocator>
+inline
+string_view const
+basic_fields<Allocator>::
+value_type::
+value() const
+{
+ return {reinterpret_cast<
+ char const*>(this + 1) + off_,
+ static_cast<std::size_t>(len_)};
+}
+
+template<class Allocator>
+inline
+boost::asio::const_buffer
+basic_fields<Allocator>::
+value_type::
+buffer() const
+{
+ return boost::asio::const_buffer{
+ reinterpret_cast<char const*>(this + 1),
+ static_cast<std::size_t>(off_) + len_ + 2};
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+basic_fields<Allocator>::
+~basic_fields()
+{
+ delete_list();
+ realloc_string(method_, {});
+ realloc_string(
+ target_or_reason_, {});
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(Allocator const& alloc)
+ : alloc_(alloc)
+{
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(basic_fields&& other)
+ : alloc_(std::move(other.alloc_))
+ , set_(std::move(other.set_))
+ , list_(std::move(other.list_))
+ , method_(other.method_)
+ , target_or_reason_(other.target_or_reason_)
+{
+ other.method_.clear();
+ other.target_or_reason_.clear();
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(basic_fields&& other, Allocator const& alloc)
+ : alloc_(alloc)
+{
+ if(alloc_ != other.alloc_)
+ {
+ copy_all(other);
+ other.clear_all();
+ }
+ else
+ {
+ set_ = std::move(other.set_);
+ list_ = std::move(other.list_);
+ method_ = other.method_;
+ target_or_reason_ = other.target_or_reason_;
+ }
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(basic_fields const& other)
+ : alloc_(alloc_traits::
+ select_on_container_copy_construction(other.alloc_))
+{
+ copy_all(other);
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(basic_fields const& other,
+ Allocator const& alloc)
+ : alloc_(alloc)
+{
+ copy_all(other);
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+basic_fields<Allocator>::
+basic_fields(basic_fields<OtherAlloc> const& other)
+{
+ copy_all(other);
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+basic_fields<Allocator>::
+basic_fields(basic_fields<OtherAlloc> const& other,
+ Allocator const& alloc)
+ : alloc_(alloc)
+{
+ copy_all(other);
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+operator=(basic_fields&& other) ->
+ basic_fields&
+{
+ if(this == &other)
+ return *this;
+ move_assign(other, std::integral_constant<bool,
+ alloc_traits:: propagate_on_container_move_assignment::value>{});
+ return *this;
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+operator=(basic_fields const& other) ->
+ basic_fields&
+{
+ copy_assign(other, std::integral_constant<bool,
+ alloc_traits::propagate_on_container_copy_assignment::value>{});
+ return *this;
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+auto
+basic_fields<Allocator>::
+operator=(basic_fields<OtherAlloc> const& other) ->
+ basic_fields&
+{
+ clear_all();
+ copy_all(other);
+ return *this;
+}
+
+//------------------------------------------------------------------------------
+//
+// Element access
+//
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+string_view const
+basic_fields<Allocator>::
+at(field name) const
+{
+ BOOST_ASSERT(name != field::unknown);
+ auto const it = find(name);
+ if(it == end())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "field not found"});
+ return it->value();
+}
+
+template<class Allocator>
+string_view const
+basic_fields<Allocator>::
+at(string_view name) const
+{
+ auto const it = find(name);
+ if(it == end())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "field not found"});
+ return it->value();
+}
+
+template<class Allocator>
+string_view const
+basic_fields<Allocator>::
+operator[](field name) const
+{
+ BOOST_ASSERT(name != field::unknown);
+ auto const it = find(name);
+ if(it == end())
+ return {};
+ return it->value();
+}
+
+template<class Allocator>
+string_view const
+basic_fields<Allocator>::
+operator[](string_view name) const
+{
+ auto const it = find(name);
+ if(it == end())
+ return {};
+ return it->value();
+}
+
+//------------------------------------------------------------------------------
+//
+// Modifiers
+//
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+clear()
+{
+ delete_list();
+ set_.clear();
+ list_.clear();
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+insert(field name, string_param const& value)
+{
+ BOOST_ASSERT(name != field::unknown);
+ insert(name, to_string(name), value);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+insert(string_view sname, string_param const& value)
+{
+ auto const name =
+ string_to_field(sname);
+ insert(name, sname, value);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+insert(field name,
+ string_view sname, string_param const& value)
+{
+ auto& e = new_element(name, sname,
+ static_cast<string_view>(value));
+ auto const before =
+ set_.upper_bound(sname, key_compare{});
+ if(before == set_.begin())
+ {
+ BOOST_ASSERT(count(sname) == 0);
+ set_.insert_before(before, e);
+ list_.push_back(e);
+ return;
+ }
+ auto const last = std::prev(before);
+ // VFALCO is it worth comparing `field name` first?
+ if(! iequals(sname, last->name_string()))
+ {
+ BOOST_ASSERT(count(sname) == 0);
+ set_.insert_before(before, e);
+ list_.push_back(e);
+ return;
+ }
+ // keep duplicate fields together in the list
+ set_.insert_before(before, e);
+ list_.insert(++list_.iterator_to(*last), e);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set(field name, string_param const& value)
+{
+ BOOST_ASSERT(name != field::unknown);
+ set_element(new_element(name, to_string(name),
+ static_cast<string_view>(value)));
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set(string_view sname, string_param const& value)
+{
+ set_element(new_element(
+ string_to_field(sname), sname,
+ static_cast<string_view>(value)));
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+erase(const_iterator pos) ->
+ const_iterator
+{
+ auto next = pos.iter();
+ auto& e = *next++;
+ set_.erase(e);
+ list_.erase(e);
+ delete_element(e);
+ return next;
+}
+
+template<class Allocator>
+std::size_t
+basic_fields<Allocator>::
+erase(field name)
+{
+ BOOST_ASSERT(name != field::unknown);
+ return erase(to_string(name));
+}
+
+template<class Allocator>
+std::size_t
+basic_fields<Allocator>::
+erase(string_view name)
+{
+ std::size_t n =0;
+ set_.erase_and_dispose(name, key_compare{},
+ [&](value_type* e)
+ {
+ ++n;
+ list_.erase(list_.iterator_to(*e));
+ delete_element(*e);
+ });
+ return n;
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+swap(basic_fields<Allocator>& other)
+{
+ swap(other, std::integral_constant<bool,
+ alloc_traits::propagate_on_container_swap::value>{});
+}
+
+template<class Allocator>
+void
+swap(
+ basic_fields<Allocator>& lhs,
+ basic_fields<Allocator>& rhs)
+{
+ lhs.swap(rhs);
+}
+
+//------------------------------------------------------------------------------
+//
+// Lookup
+//
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+inline
+std::size_t
+basic_fields<Allocator>::
+count(field name) const
+{
+ BOOST_ASSERT(name != field::unknown);
+ return count(to_string(name));
+}
+
+template<class Allocator>
+std::size_t
+basic_fields<Allocator>::
+count(string_view name) const
+{
+ return set_.count(name, key_compare{});
+}
+
+template<class Allocator>
+inline
+auto
+basic_fields<Allocator>::
+find(field name) const ->
+ const_iterator
+{
+ BOOST_ASSERT(name != field::unknown);
+ return find(to_string(name));
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+find(string_view name) const ->
+ const_iterator
+{
+ auto const it = set_.find(
+ name, key_compare{});
+ if(it == set_.end())
+ return list_.end();
+ return list_.iterator_to(*it);
+}
+
+template<class Allocator>
+inline
+auto
+basic_fields<Allocator>::
+equal_range(field name) const ->
+ std::pair<const_iterator, const_iterator>
+{
+ BOOST_ASSERT(name != field::unknown);
+ return equal_range(to_string(name));
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+equal_range(string_view name) const ->
+ std::pair<const_iterator, const_iterator>
+{
+ auto result =
+ set_.equal_range(name, key_compare{});
+ if(result.first == result.second)
+ return {list_.end(), list_.end()};
+ return {
+ list_.iterator_to(*result.first),
+ ++list_.iterator_to(*(--result.second))};
+}
+
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+// Filter a token list
+//
+template<class String, class Pred>
+void
+filter_token_list(
+ String& s,
+ string_view value,
+ Pred&& pred)
+{
+ token_list te{value};
+ auto it = te.begin();
+ auto last = te.end();
+ if(it == last)
+ return;
+ while(pred(*it))
+ if(++it == last)
+ return;
+ s.append(it->data(), it->size());
+ while(++it != last)
+ {
+ if(! pred(*it))
+ {
+ s.append(", ");
+ s.append(it->data(), it->size());
+ }
+ }
+}
+
+// Filter the last item in a token list
+template<class String, class Pred>
+void
+filter_token_list_last(
+ String& s,
+ string_view value,
+ Pred&& pred)
+{
+ token_list te{value};
+ if(te.begin() != te.end())
+ {
+ auto it = te.begin();
+ auto next = std::next(it);
+ if(next == te.end())
+ {
+ if(! pred(*it))
+ s.append(it->data(), it->size());
+ return;
+ }
+ s.append(it->data(), it->size());
+ for(;;)
+ {
+ it = next;
+ next = std::next(it);
+ if(next == te.end())
+ {
+ if(! pred(*it))
+ {
+ s.append(", ");
+ s.append(it->data(), it->size());
+ }
+ return;
+ }
+ s.append(", ");
+ s.append(it->data(), it->size());
+ }
+ }
+}
+
+template<class String>
+void
+keep_alive_impl(
+ String& s, string_view value,
+ unsigned version, bool keep_alive)
+{
+ if(version < 11)
+ {
+ if(keep_alive)
+ {
+ // remove close
+ filter_token_list(s, value,
+ [](string_view s)
+ {
+ return iequals(s, "close");
+ });
+ // add keep-alive
+ if(s.empty())
+ s.append("keep-alive");
+ else if(! token_list{value}.exists("keep-alive"))
+ s.append(", keep-alive");
+ }
+ else
+ {
+ // remove close and keep-alive
+ filter_token_list(s, value,
+ [](string_view s)
+ {
+ return
+ iequals(s, "close") ||
+ iequals(s, "keep-alive");
+ });
+ }
+ }
+ else
+ {
+ if(keep_alive)
+ {
+ // remove close and keep-alive
+ filter_token_list(s, value,
+ [](string_view s)
+ {
+ return
+ iequals(s, "close") ||
+ iequals(s, "keep-alive");
+ });
+ }
+ else
+ {
+ // remove keep-alive
+ filter_token_list(s, value,
+ [](string_view s)
+ {
+ return iequals(s, "keep-alive");
+ });
+ // add close
+ if(s.empty())
+ s.append("close");
+ else if(! token_list{value}.exists("close"))
+ s.append(", close");
+ }
+ }
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+// Fields
+
+template<class Allocator>
+inline
+string_view
+basic_fields<Allocator>::
+get_method_impl() const
+{
+ return method_;
+}
+
+template<class Allocator>
+inline
+string_view
+basic_fields<Allocator>::
+get_target_impl() const
+{
+ if(target_or_reason_.empty())
+ return target_or_reason_;
+ return {
+ target_or_reason_.data() + 1,
+ target_or_reason_.size() - 1};
+}
+
+template<class Allocator>
+inline
+string_view
+basic_fields<Allocator>::
+get_reason_impl() const
+{
+ return target_or_reason_;
+}
+
+template<class Allocator>
+bool
+basic_fields<Allocator>::
+get_chunked_impl() const
+{
+ auto const te = token_list{
+ (*this)[field::transfer_encoding]};
+ for(auto it = te.begin(); it != te.end();)
+ {
+ auto const next = std::next(it);
+ if(next == te.end())
+ return iequals(*it, "chunked");
+ it = next;
+ }
+ return false;
+}
+
+template<class Allocator>
+bool
+basic_fields<Allocator>::
+get_keep_alive_impl(unsigned version) const
+{
+ auto const it = find(field::connection);
+ if(version < 11)
+ {
+ if(it == end())
+ return false;
+ return token_list{
+ it->value()}.exists("keep-alive");
+ }
+ if(it == end())
+ return true;
+ return ! token_list{
+ it->value()}.exists("close");
+}
+
+template<class Allocator>
+bool
+basic_fields<Allocator>::
+has_content_length_impl() const
+{
+ return count(field::content_length) > 0;
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+set_method_impl(string_view s)
+{
+ realloc_string(method_, s);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+set_target_impl(string_view s)
+{
+ realloc_target(
+ target_or_reason_, s);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+set_reason_impl(string_view s)
+{
+ realloc_string(
+ target_or_reason_, s);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set_chunked_impl(bool value)
+{
+ auto it = find(field::transfer_encoding);
+ if(value)
+ {
+ // append "chunked"
+ if(it == end())
+ {
+ set(field::transfer_encoding, "chunked");
+ return;
+ }
+ auto const te = token_list{it->value()};
+ for(auto itt = te.begin();;)
+ {
+ auto const next = std::next(itt);
+ if(next == te.end())
+ {
+ if(iequals(*itt, "chunked"))
+ return; // already set
+ break;
+ }
+ itt = next;
+ }
+ static_string<max_static_buffer> buf;
+ if(it->value().size() <= buf.size() + 9)
+ {
+ buf.append(it->value().data(), it->value().size());
+ buf.append(", chunked", 9);
+ set(field::transfer_encoding, buf);
+ }
+ else
+ {
+ #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
+ std::string s;
+ #else
+ using rebind_type =
+ typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<char>;
+ std::basic_string<
+ char,
+ std::char_traits<char>,
+ rebind_type> s{rebind_type{alloc_}};
+ #endif
+ s.reserve(it->value().size() + 9);
+ s.append(it->value().data(), it->value().size());
+ s.append(", chunked", 9);
+ set(field::transfer_encoding, s);
+ }
+ return;
+ }
+ // filter "chunked"
+ if(it == end())
+ return;
+ try
+ {
+ static_string<max_static_buffer> buf;
+ detail::filter_token_list_last(buf, it->value(),
+ [](string_view s)
+ {
+ return iequals(s, "chunked");
+ });
+ if(! buf.empty())
+ set(field::transfer_encoding, buf);
+ else
+ erase(field::transfer_encoding);
+ }
+ catch(std::length_error const&)
+ {
+ #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
+ std::string s;
+ #else
+ using rebind_type =
+ typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<char>;
+ std::basic_string<
+ char,
+ std::char_traits<char>,
+ rebind_type> s{rebind_type{alloc_}};
+ #endif
+ s.reserve(it->value().size());
+ detail::filter_token_list_last(s, it->value(),
+ [](string_view s)
+ {
+ return iequals(s, "chunked");
+ });
+ if(! s.empty())
+ set(field::transfer_encoding, s);
+ else
+ erase(field::transfer_encoding);
+ }
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set_content_length_impl(
+ boost::optional<std::uint64_t> const& value)
+{
+ if(! value)
+ erase(field::content_length);
+ else
+ set(field::content_length, *value);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set_keep_alive_impl(
+ unsigned version, bool keep_alive)
+{
+ // VFALCO What about Proxy-Connection ?
+ auto const value = (*this)[field::connection];
+ try
+ {
+ static_string<max_static_buffer> buf;
+ detail::keep_alive_impl(
+ buf, value, version, keep_alive);
+ if(buf.empty())
+ erase(field::connection);
+ else
+ set(field::connection, buf);
+ }
+ catch(std::length_error const&)
+ {
+ #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
+ std::string s;
+ #else
+ using rebind_type =
+ typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<char>;
+ std::basic_string<
+ char,
+ std::char_traits<char>,
+ rebind_type> s{rebind_type{alloc_}};
+ #endif
+ s.reserve(value.size());
+ detail::keep_alive_impl(
+ s, value, version, keep_alive);
+ if(s.empty())
+ erase(field::connection);
+ else
+ set(field::connection, s);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+new_element(field name,
+ string_view sname, string_view value) ->
+ value_type&
+{
+ if(sname.size() + 2 >
+ (std::numeric_limits<off_t>::max)())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "field name too large"});
+ if(value.size() + 2 >
+ (std::numeric_limits<off_t>::max)())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "field value too large"});
+ value = detail::trim(value);
+ std::uint16_t const off =
+ static_cast<off_t>(sname.size() + 2);
+ std::uint16_t const len =
+ static_cast<off_t>(value.size());
+ auto const p = alloc_traits::allocate(alloc_,
+ 1 + (off + len + 2 + sizeof(value_type) - 1) /
+ sizeof(value_type));
+ // VFALCO allocator can't call the constructor because its private
+ //alloc_traits::construct(alloc_, p, name, sname, value);
+ new(p) value_type{name, sname, value};
+ return *p;
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+delete_element(value_type& e)
+{
+ auto const n = 1 + (e.off_ + e.len_ + 2 +
+ sizeof(value_type) - 1) / sizeof(value_type);
+ alloc_traits::destroy(alloc_, &e);
+ alloc_traits::deallocate(alloc_, &e, n);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set_element(value_type& e)
+{
+ auto it = set_.lower_bound(
+ e.name_string(), key_compare{});
+ if(it == set_.end() || ! iequals(
+ e.name_string(), it->name_string()))
+ {
+ set_.insert_before(it, e);
+ list_.push_back(e);
+ return;
+ }
+ for(;;)
+ {
+ auto next = it;
+ ++next;
+ set_.erase(it);
+ list_.erase(list_.iterator_to(*it));
+ delete_element(*it);
+ it = next;
+ if(it == set_.end() ||
+ ! iequals(e.name_string(), it->name_string()))
+ break;
+ }
+ set_.insert_before(it, e);
+ list_.push_back(e);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+realloc_string(string_view& dest, string_view s)
+{
+ if(dest.empty() && s.empty())
+ return;
+ auto a = typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<
+ char>(alloc_);
+ if(! dest.empty())
+ {
+ a.deallocate(const_cast<char*>(
+ dest.data()), dest.size());
+ dest.clear();
+ }
+ if(! s.empty())
+ {
+ auto const p = a.allocate(s.size());
+ std::memcpy(p, s.data(), s.size());
+ dest = {p, s.size()};
+ }
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+realloc_target(
+ string_view& dest, string_view s)
+{
+ // The target string are stored with an
+ // extra space at the beginning to help
+ // the writer class.
+ if(dest.empty() && s.empty())
+ return;
+ auto a = typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<
+ char>(alloc_);
+ if(! dest.empty())
+ {
+ a.deallocate(const_cast<char*>(
+ dest.data()), dest.size());
+ dest.clear();
+ }
+ if(! s.empty())
+ {
+ auto const p = a.allocate(1 + s.size());
+ p[0] = ' ';
+ std::memcpy(p + 1, s.data(), s.size());
+ dest = {p, 1 + s.size()};
+ }
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+void
+basic_fields<Allocator>::
+copy_all(basic_fields<OtherAlloc> const& other)
+{
+ for(auto const& e : other.list_)
+ insert(e.name(), e.name_string(), e.value());
+ realloc_string(method_, other.method_);
+ realloc_string(target_or_reason_,
+ other.target_or_reason_);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+clear_all()
+{
+ clear();
+ realloc_string(method_, {});
+ realloc_string(target_or_reason_, {});
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+delete_list()
+{
+ for(auto it = list_.begin(); it != list_.end();)
+ delete_element(*it++);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+move_assign(basic_fields& other, std::true_type)
+{
+ clear_all();
+ set_ = std::move(other.set_);
+ list_ = std::move(other.list_);
+ method_ = other.method_;
+ target_or_reason_ = other.target_or_reason_;
+ other.method_.clear();
+ other.target_or_reason_.clear();
+ alloc_ = other.alloc_;
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+move_assign(basic_fields& other, std::false_type)
+{
+ clear_all();
+ if(alloc_ != other.alloc_)
+ {
+ copy_all(other);
+ other.clear_all();
+ }
+ else
+ {
+ set_ = std::move(other.set_);
+ list_ = std::move(other.list_);
+ method_ = other.method_;
+ target_or_reason_ = other.target_or_reason_;
+ other.method_.clear();
+ other.target_or_reason_.clear();
+ }
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+copy_assign(basic_fields const& other, std::true_type)
+{
+ clear_all();
+ alloc_ = other.alloc_;
+ copy_all(other);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+copy_assign(basic_fields const& other, std::false_type)
+{
+ clear_all();
+ copy_all(other);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+swap(basic_fields& other, std::true_type)
+{
+ using std::swap;
+ swap(alloc_, other.alloc_);
+ swap(set_, other.set_);
+ swap(list_, other.list_);
+ swap(method_, other.method_);
+ swap(target_or_reason_, other.target_or_reason_);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+swap(basic_fields& other, std::false_type)
+{
+ BOOST_ASSERT(alloc_ == other.alloc_);
+ using std::swap;
+ swap(set_, other.set_);
+ swap(list_, other.list_);
+ swap(method_, other.method_);
+ swap(target_or_reason_, other.target_or_reason_);
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/file_body_win32.ipp b/boost/beast/http/impl/file_body_win32.ipp
new file mode 100644
index 0000000000..e11c443ed2
--- /dev/null
+++ b/boost/beast/http/impl/file_body_win32.ipp
@@ -0,0 +1,584 @@
+//
+// 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_HTTP_IMPL_FILE_BODY_WIN32_IPP
+#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
+
+#if BOOST_BEAST_USE_WIN32_FILE
+
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/http/serializer.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/basic_stream_socket.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/windows/overlapped_ptr.hpp>
+#include <boost/make_unique.hpp>
+#include <boost/smart_ptr/make_shared_array.hpp>
+#include <boost/winapi/basic_types.hpp>
+#include <algorithm>
+#include <cstring>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+template<class, class, bool, class>
+class write_some_win32_op;
+} // detail
+
+template<>
+struct basic_file_body<file_win32>
+{
+ using file_type = file_win32;
+
+ class writer;
+ class reader;
+
+ //--------------------------------------------------------------------------
+
+ class value_type
+ {
+ friend class writer;
+ friend class reader;
+ friend struct basic_file_body<file_win32>;
+
+ template<class, class, bool, class>
+ friend class detail::write_some_win32_op;
+ template<
+ class Protocol, bool isRequest, class Fields>
+ friend
+ std::size_t
+ write_some(
+ boost::asio::basic_stream_socket<Protocol>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr,
+ error_code& ec);
+
+ file_win32 file_;
+ std::uint64_t size_ = 0; // cached file size
+ std::uint64_t first_; // starting offset of the range
+ std::uint64_t last_; // ending offset of the range
+
+ public:
+ ~value_type() = default;
+ value_type() = default;
+ value_type(value_type&& other) = default;
+ value_type& operator=(value_type&& other) = default;
+
+ bool
+ is_open() const
+ {
+ return file_.is_open();
+ }
+
+ std::uint64_t
+ size() const
+ {
+ return size_;
+ }
+
+ void
+ close();
+
+ void
+ open(char const* path, file_mode mode, error_code& ec);
+
+ void
+ reset(file_win32&& file, error_code& ec);
+ };
+
+ //--------------------------------------------------------------------------
+
+ class writer
+ {
+ template<class, class, bool, class>
+ friend class detail::write_some_win32_op;
+ template<
+ class Protocol, bool isRequest, class Fields>
+ friend
+ std::size_t
+ write_some(
+ boost::asio::basic_stream_socket<Protocol>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr,
+ error_code& ec);
+
+ value_type& body_; // The body we are reading from
+ std::uint64_t pos_; // The current position in the file
+ char buf_[4096]; // Small buffer for reading
+
+ public:
+ using const_buffers_type =
+ boost::asio::const_buffer;
+
+ template<bool isRequest, class Fields>
+ writer(message<isRequest,
+ basic_file_body<file_win32>, Fields>& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(error_code&)
+ {
+ BOOST_ASSERT(body_.file_.is_open());
+ pos_ = body_.first_;
+ }
+
+ boost::optional<std::pair<const_buffers_type, bool>>
+ get(error_code& ec)
+ {
+ std::size_t const n = (std::min)(sizeof(buf_),
+ beast::detail::clamp(body_.last_ - pos_));
+ if(n == 0)
+ {
+ ec.assign(0, ec.category());
+ return boost::none;
+ }
+ auto const nread = body_.file_.read(buf_, n, ec);
+ if(ec)
+ return boost::none;
+ BOOST_ASSERT(nread != 0);
+ pos_ += nread;
+ ec.assign(0, ec.category());
+ return {{
+ {buf_, nread}, // buffer to return.
+ pos_ < body_.last_}}; // `true` if there are more buffers.
+ }
+ };
+
+ //--------------------------------------------------------------------------
+
+ class reader
+ {
+ value_type& body_;
+
+ public:
+ template<bool isRequest, class Fields>
+ explicit
+ reader(message<isRequest, basic_file_body, Fields>& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(boost::optional<
+ std::uint64_t> const& content_length,
+ error_code& ec)
+ {
+ // VFALCO We could reserve space in the file
+ boost::ignore_unused(content_length);
+ BOOST_ASSERT(body_.file_.is_open());
+ ec.assign(0, ec.category());
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers,
+ error_code& ec)
+ {
+ std::size_t nwritten = 0;
+ for(auto buffer : beast::detail::buffers_range(buffers))
+ {
+ nwritten += body_.file_.write(
+ buffer.data(), buffer.size(), ec);
+ if(ec)
+ return nwritten;
+ }
+ ec.assign(0, ec.category());
+ return nwritten;
+ }
+
+ void
+ finish(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+ };
+
+ //--------------------------------------------------------------------------
+
+ static
+ std::uint64_t
+ size(value_type const& body)
+ {
+ return body.size();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+inline
+void
+basic_file_body<file_win32>::
+value_type::
+close()
+{
+ error_code ignored;
+ file_.close(ignored);
+}
+
+inline
+void
+basic_file_body<file_win32>::
+value_type::
+open(char const* path, file_mode mode, error_code& ec)
+{
+ file_.open(path, mode, ec);
+ if(ec)
+ return;
+ size_ = file_.size(ec);
+ if(ec)
+ {
+ close();
+ return;
+ }
+ first_ = 0;
+ last_ = size_;
+}
+
+inline
+void
+basic_file_body<file_win32>::
+value_type::
+reset(file_win32&& file, error_code& ec)
+{
+ if(file_.is_open())
+ {
+ error_code ignored;
+ file_.close(ignored);
+ }
+ file_ = std::move(file);
+ if(file_.is_open())
+ {
+ size_ = file_.size(ec);
+ if(ec)
+ {
+ close();
+ return;
+ }
+ first_ = 0;
+ last_ = size_;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+template<class Unsigned>
+inline
+boost::winapi::DWORD_
+lowPart(Unsigned n)
+{
+ return static_cast<
+ boost::winapi::DWORD_>(
+ n & 0xffffffff);
+}
+
+template<class Unsigned>
+inline
+boost::winapi::DWORD_
+highPart(Unsigned n, std::true_type)
+{
+ return static_cast<
+ boost::winapi::DWORD_>(
+ (n>>32)&0xffffffff);
+}
+
+template<class Unsigned>
+inline
+boost::winapi::DWORD_
+highPart(Unsigned, std::false_type)
+{
+ return 0;
+}
+
+template<class Unsigned>
+inline
+boost::winapi::DWORD_
+highPart(Unsigned n)
+{
+ return highPart(n, std::integral_constant<
+ bool, (sizeof(Unsigned)>4)>{});
+}
+
+class null_lambda
+{
+public:
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code&,
+ ConstBufferSequence const&) const
+ {
+ BOOST_ASSERT(false);
+ }
+};
+
+//------------------------------------------------------------------------------
+
+#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
+
+template<
+ class Protocol, class Handler,
+ bool isRequest, class Fields>
+class write_some_win32_op
+{
+ boost::asio::basic_stream_socket<Protocol>& sock_;
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr_;
+ std::size_t bytes_transferred_ = 0;
+ Handler h_;
+ bool header_ = false;
+
+public:
+ write_some_win32_op(write_some_win32_op&&) = default;
+ write_some_win32_op(write_some_win32_op const&) = default;
+
+ template<class DeducedHandler>
+ write_some_win32_op(
+ DeducedHandler&& h,
+ boost::asio::basic_stream_socket<Protocol>& s,
+ serializer<isRequest,
+ basic_file_body<file_win32>,Fields>& sr)
+ : sock_(s)
+ , sr_(sr)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type =
+ boost::asio::associated_executor_t<Handler, decltype(std::declval<
+ boost::asio::basic_stream_socket<Protocol>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, sock_.get_executor());
+ }
+
+ void
+ operator()();
+
+ void
+ operator()(
+ error_code ec,
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(write_some_win32_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<
+ class Protocol, class Handler,
+ bool isRequest, class Fields>
+void
+write_some_win32_op<
+ Protocol, Handler, isRequest, Fields>::
+operator()()
+{
+ if(! sr_.is_header_done())
+ {
+ header_ = true;
+ sr_.split(true);
+ return detail::async_write_some(
+ sock_, sr_, std::move(*this));
+ }
+ if(sr_.get().chunked())
+ {
+ return detail::async_write_some(
+ sock_, sr_, std::move(*this));
+ }
+ auto& r = sr_.reader_impl();
+ boost::winapi::DWORD_ const nNumberOfBytesToWrite =
+ static_cast<boost::winapi::DWORD_>(
+ (std::min<std::uint64_t>)(
+ (std::min<std::uint64_t>)(r.body_.last_ - r.pos_, sr_.limit()),
+ (std::numeric_limits<boost::winapi::DWORD_>::max)()));
+ boost::asio::windows::overlapped_ptr overlapped{
+ sock_.get_executor().context(), *this};
+ auto& ov = *overlapped.get();
+ ov.Offset = lowPart(r.pos_);
+ ov.OffsetHigh = highPart(r.pos_);
+ auto const bSuccess = ::TransmitFile(
+ sock_.native_handle(),
+ sr_.get().body().file_.native_handle(),
+ nNumberOfBytesToWrite,
+ 0,
+ overlapped.get(),
+ nullptr,
+ 0);
+ auto const dwError = ::GetLastError();
+ if(! bSuccess && dwError !=
+ boost::winapi::ERROR_IO_PENDING_)
+ {
+ // VFALCO This needs review, is 0 the right number?
+ // completed immediately (with error?)
+ overlapped.complete(error_code{static_cast<int>(
+ boost::winapi::GetLastError()),
+ system_category()}, 0);
+ return;
+ }
+ overlapped.release();
+}
+
+template<
+ class Protocol, class Handler,
+ bool isRequest, class Fields>
+void
+write_some_win32_op<
+ Protocol, Handler, isRequest, Fields>::
+operator()(
+ error_code ec, std::size_t bytes_transferred)
+{
+ bytes_transferred_ += bytes_transferred;
+ if(! ec)
+ {
+ if(header_)
+ {
+ header_ = false;
+ return (*this)();
+ }
+ auto& r = sr_.reader_impl();
+ r.pos_ += bytes_transferred;
+ BOOST_ASSERT(r.pos_ <= r.body_.last_);
+ if(r.pos_ >= r.body_.last_)
+ {
+ sr_.next(ec, null_lambda{});
+ BOOST_ASSERT(! ec);
+ BOOST_ASSERT(sr_.is_done());
+ }
+ }
+ h_(ec, bytes_transferred_);
+}
+
+#endif
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+template<class Protocol, bool isRequest, class Fields>
+std::size_t
+write_some(
+ boost::asio::basic_stream_socket<Protocol>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr,
+ error_code& ec)
+{
+ if(! sr.is_header_done())
+ {
+ sr.split(true);
+ auto const bytes_transferred =
+ detail::write_some(sock, sr, ec);
+ if(ec)
+ return bytes_transferred;
+ return bytes_transferred;
+ }
+ if(sr.get().chunked())
+ {
+ auto const bytes_transferred =
+ detail::write_some(sock, sr, ec);
+ if(ec)
+ return bytes_transferred;
+ return bytes_transferred;
+ }
+ auto& r = sr.reader_impl();
+ r.body_.file_.seek(r.pos_, ec);
+ if(ec)
+ return 0;
+ boost::winapi::DWORD_ const nNumberOfBytesToWrite =
+ static_cast<boost::winapi::DWORD_>(
+ (std::min<std::uint64_t>)(
+ (std::min<std::uint64_t>)(r.body_.last_ - r.pos_, sr.limit()),
+ (std::numeric_limits<boost::winapi::DWORD_>::max)()));
+ auto const bSuccess = ::TransmitFile(
+ sock.native_handle(),
+ r.body_.file_.native_handle(),
+ nNumberOfBytesToWrite,
+ 0,
+ nullptr,
+ nullptr,
+ 0);
+ if(! bSuccess)
+ {
+ ec.assign(static_cast<int>(
+ boost::winapi::GetLastError()),
+ system_category());
+ return 0;
+ }
+ r.pos_ += nNumberOfBytesToWrite;
+ BOOST_ASSERT(r.pos_ <= r.body_.last_);
+ if(r.pos_ < r.body_.last_)
+ {
+ ec.assign(0, ec.category());
+ }
+ else
+ {
+ sr.next(ec, detail::null_lambda{});
+ BOOST_ASSERT(! ec);
+ BOOST_ASSERT(sr.is_done());
+ }
+ return nNumberOfBytesToWrite;
+}
+
+#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
+
+template<
+ class Protocol,
+ bool isRequest, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_some(
+ boost::asio::basic_stream_socket<Protocol>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr,
+ WriteHandler&& handler)
+{
+ boost::asio::async_completion<WriteHandler,
+ void(error_code)> init{handler};
+ detail::write_some_win32_op<
+ Protocol,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ isRequest, Fields>{
+ init.completion_handler, sock, sr}();
+ return init.result.get();
+}
+
+#endif
+
+} // http
+} // beast
+} // boost
+
+#endif
+
+#endif
diff --git a/boost/beast/http/impl/message.ipp b/boost/beast/http/impl/message.ipp
new file mode 100644
index 0000000000..64d96eb6f1
--- /dev/null
+++ b/boost/beast/http/impl/message.ipp
@@ -0,0 +1,437 @@
+//
+// 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_HTTP_IMPL_MESSAGE_IPP
+#define BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP
+
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<class Fields>
+template<class Arg1, class... ArgN, class>
+header<true, Fields>::
+header(Arg1&& arg1, ArgN&&... argn)
+ : Fields(std::forward<Arg1>(arg1),
+ std::forward<ArgN>(argn)...)
+{
+}
+
+template<class Fields>
+inline
+verb
+header<true, Fields>::
+method() const
+{
+ return method_;
+}
+
+template<class Fields>
+void
+header<true, Fields>::
+method(verb v)
+{
+ if(v == verb::unknown)
+ BOOST_THROW_EXCEPTION(
+ std::invalid_argument{"unknown method"});
+ method_ = v;
+ this->set_method_impl({});
+}
+
+template<class Fields>
+string_view
+header<true, Fields>::
+method_string() const
+{
+ if(method_ != verb::unknown)
+ return to_string(method_);
+ return this->get_method_impl();
+}
+
+template<class Fields>
+void
+header<true, Fields>::
+method_string(string_view s)
+{
+ method_ = string_to_verb(s);
+ if(method_ != verb::unknown)
+ this->set_method_impl({});
+ else
+ this->set_method_impl(s);
+}
+
+template<class Fields>
+inline
+string_view
+header<true, Fields>::
+target() const
+{
+ return this->get_target_impl();
+}
+
+template<class Fields>
+inline
+void
+header<true, Fields>::
+target(string_view s)
+{
+ this->set_target_impl(s);
+}
+
+template<class Fields>
+void
+swap(
+ header<true, Fields>& h1,
+ header<true, Fields>& h2)
+{
+ using std::swap;
+ swap(
+ static_cast<Fields&>(h1),
+ static_cast<Fields&>(h2));
+ swap(h1.version_, h2.version_);
+ swap(h1.method_, h2.method_);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Fields>
+template<class Arg1, class... ArgN, class>
+header<false, Fields>::
+header(Arg1&& arg1, ArgN&&... argn)
+ : Fields(std::forward<Arg1>(arg1),
+ std::forward<ArgN>(argn)...)
+{
+}
+
+template<class Fields>
+inline
+status
+header<false, Fields>::
+result() const
+{
+ return int_to_status(
+ static_cast<int>(result_));
+}
+
+template<class Fields>
+inline
+void
+header<false, Fields>::
+result(status v)
+{
+ result_ = v;
+}
+
+template<class Fields>
+inline
+void
+header<false, Fields>::
+result(unsigned v)
+{
+ if(v > 999)
+ BOOST_THROW_EXCEPTION(
+ std::invalid_argument{
+ "invalid status-code"});
+ result_ = static_cast<status>(v);
+}
+
+template<class Fields>
+inline
+unsigned
+header<false, Fields>::
+result_int() const
+{
+ return static_cast<unsigned>(result_);
+}
+
+template<class Fields>
+string_view
+header<false, Fields>::
+reason() const
+{
+ auto const s = this->get_reason_impl();
+ if(! s.empty())
+ return s;
+ return obsolete_reason(result_);
+}
+
+template<class Fields>
+inline
+void
+header<false, Fields>::
+reason(string_view s)
+{
+ this->set_reason_impl(s);
+}
+
+template<class Fields>
+void
+swap(
+ header<false, Fields>& h1,
+ header<false, Fields>& h2)
+{
+ using std::swap;
+ swap(
+ static_cast<Fields&>(h1),
+ static_cast<Fields&>(h2));
+ swap(h1.version_, h2.version_);
+ swap(h1.result_, h2.result_);
+}
+
+//------------------------------------------------------------------------------
+
+template<bool isRequest, class Body, class Fields>
+template<class... BodyArgs>
+message<isRequest, Body, Fields>::
+message(header_type&& h, BodyArgs&&... body_args)
+ : header_type(std::move(h))
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArgs>(body_args)...)
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class... BodyArgs>
+message<isRequest, Body, Fields>::
+message(header_type const& h, BodyArgs&&... body_args)
+ : header_type(h)
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArgs>(body_args)...)
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class Version, class>
+message<isRequest, Body, Fields>::
+message(verb method, string_view target, Version version)
+ : header_type(method, target, version)
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class Version, class BodyArg, class>
+message<isRequest, Body, Fields>::
+message(verb method, string_view target,
+ Version version, BodyArg&& body_arg)
+ : header_type(method, target, version)
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArg>(body_arg))
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class Version, class BodyArg, class FieldsArg, class>
+message<isRequest, Body, Fields>::
+message(
+ verb method, string_view target, Version version,
+ BodyArg&& body_arg,
+ FieldsArg&& fields_arg)
+ : header_type(method, target, version,
+ std::forward<FieldsArg>(fields_arg))
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArg>(body_arg))
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class Version, class>
+message<isRequest, Body, Fields>::
+message(status result, Version version)
+ : header_type(result, version)
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class Version, class BodyArg, class>
+message<isRequest, Body, Fields>::
+message(status result, Version version,
+ BodyArg&& body_arg)
+ : header_type(result, version)
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArg>(body_arg))
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class Version, class BodyArg, class FieldsArg, class>
+message<isRequest, Body, Fields>::
+message(status result, Version version,
+ BodyArg&& body_arg, FieldsArg&& fields_arg)
+ : header_type(result, version,
+ std::forward<FieldsArg>(fields_arg))
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArg>(body_arg))
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+message<isRequest, Body, Fields>::
+message(std::piecewise_construct_t)
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class... BodyArgs>
+message<isRequest, Body, Fields>::
+message(std::piecewise_construct_t,
+ std::tuple<BodyArgs...> body_args)
+ : message(std::piecewise_construct,
+ body_args,
+ beast::detail::make_index_sequence<
+ sizeof...(BodyArgs)>{})
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class... BodyArgs, class... FieldsArgs>
+message<isRequest, Body, Fields>::
+message(std::piecewise_construct_t,
+ std::tuple<BodyArgs...> body_args,
+ std::tuple<FieldsArgs...> fields_args)
+ : message(std::piecewise_construct,
+ body_args,
+ fields_args,
+ beast::detail::make_index_sequence<
+ sizeof...(BodyArgs)>{},
+ beast::detail::make_index_sequence<
+ sizeof...(FieldsArgs)>{})
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+void
+message<isRequest, Body, Fields>::
+chunked(bool value)
+{
+ this->set_chunked_impl(value);
+ this->set_content_length_impl(boost::none);
+}
+
+template<bool isRequest, class Body, class Fields>
+void
+message<isRequest, Body, Fields>::
+content_length(
+ boost::optional<std::uint64_t> const& value)
+{
+ this->set_content_length_impl(value);
+ this->set_chunked_impl(false);
+}
+
+template<bool isRequest, class Body, class Fields>
+boost::optional<std::uint64_t>
+message<isRequest, Body, Fields>::
+payload_size() const
+{
+ return payload_size(detail::is_body_sized<Body>{});
+}
+
+template<bool isRequest, class Body, class Fields>
+bool
+message<isRequest, Body, Fields>::
+need_eof(std::false_type) const
+{
+ // VFALCO Do we need a way to let the caller say "the body is intentionally skipped"?
+ if( this->result() == status::no_content ||
+ this->result() == status::not_modified ||
+ to_status_class(this->result()) ==
+ status_class::informational ||
+ has_content_length() ||
+ chunked())
+ return ! keep_alive();
+ return true;
+}
+
+template<bool isRequest, class Body, class Fields>
+void
+message<isRequest, Body, Fields>::
+prepare_payload(std::true_type)
+{
+ auto const n = payload_size();
+ if(this->method() == verb::trace && (! n || *n > 0))
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid request body"});
+ if(n)
+ {
+ if(*n > 0 ||
+ this->method() == verb::options ||
+ this->method() == verb::put ||
+ this->method() == verb::post)
+ {
+ this->content_length(n);
+ }
+ else
+ {
+ this->chunked(false);
+ }
+ }
+ else if(this->version() == 11)
+ {
+ this->chunked(true);
+ }
+ else
+ {
+ this->chunked(false);
+ }
+}
+
+template<bool isRequest, class Body, class Fields>
+void
+message<isRequest, Body, Fields>::
+prepare_payload(std::false_type)
+{
+ auto const n = payload_size();
+ if( (! n || *n > 0) && (
+ (status_class(this->result()) == status_class::informational ||
+ this->result() == status::no_content ||
+ this->result() == status::not_modified)))
+ {
+ // The response body MUST be empty for this case
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid response body"});
+ }
+ if(n)
+ this->content_length(n);
+ else if(this->version() == 11)
+ this->chunked(true);
+ else
+ this->chunked(false);
+}
+
+//------------------------------------------------------------------------------
+
+template<bool isRequest, class Body, class Fields>
+void
+swap(
+ message<isRequest, Body, Fields>& m1,
+ message<isRequest, Body, Fields>& m2)
+{
+ using std::swap;
+ swap(
+ static_cast<header<isRequest, Fields>&>(m1),
+ static_cast<header<isRequest, Fields>&>(m2));
+ swap(m1.body(), m2.body());
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/parser.ipp b/boost/beast/http/impl/parser.ipp
new file mode 100644
index 0000000000..99745da3f0
--- /dev/null
+++ b/boost/beast/http/impl/parser.ipp
@@ -0,0 +1,56 @@
+//
+// 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_HTTP_IMPL_PARSER_IPP
+#define BOOST_BEAST_HTTP_IMPL_PARSER_IPP
+
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<bool isRequest, class Body, class Allocator>
+parser<isRequest, Body, Allocator>::
+parser()
+ : wr_(m_)
+{
+}
+
+template<bool isRequest, class Body, class Allocator>
+template<class Arg1, class... ArgN, class>
+parser<isRequest, Body, Allocator>::
+parser(Arg1&& arg1, ArgN&&... argn)
+ : m_(std::forward<Arg1>(arg1),
+ std::forward<ArgN>(argn)...)
+ , wr_(m_)
+{
+ m_.clear();
+}
+
+template<bool isRequest, class Body, class Allocator>
+template<class OtherBody, class... Args, class>
+parser<isRequest, Body, Allocator>::
+parser(parser<isRequest, OtherBody, Allocator>&& other,
+ Args&&... args)
+ : base_type(std::move(other))
+ , m_(other.release(), std::forward<Args>(args)...)
+ , wr_(m_)
+{
+ if(other.rd_inited_)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "moved-from parser has a body"});
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/read.ipp b/boost/beast/http/impl/read.ipp
new file mode 100644
index 0000000000..5ecfae7d8b
--- /dev/null
+++ b/boost/beast/http/impl/read.ipp
@@ -0,0 +1,821 @@
+//
+// 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_HTTP_IMPL_READ_IPP_HPP
+#define BOOST_BEAST_HTTP_IMPL_READ_IPP_HPP
+
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/parser.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/read_size.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/assert.hpp>
+#include <boost/config.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+
+//------------------------------------------------------------------------------
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Derived, class Handler>
+class read_some_op
+{
+ int state_ = 0;
+ Stream& s_;
+ DynamicBuffer& b_;
+ basic_parser<isRequest, Derived>& p_;
+ boost::optional<typename
+ DynamicBuffer::mutable_buffers_type> mb_;
+ std::size_t bytes_transferred_ = 0;
+ Handler h_;
+
+public:
+ read_some_op(read_some_op&&) = default;
+ read_some_op(read_some_op const&) = default;
+
+ template<class DeducedHandler>
+ read_some_op(DeducedHandler&& h, Stream& s,
+ DynamicBuffer& b, basic_parser<isRequest, Derived>& p)
+ : s_(s)
+ , b_(b)
+ , p_(p)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(read_some_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->state_ >= 2 ? true :
+ asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Derived, class Handler>
+void
+read_some_op<Stream, DynamicBuffer,
+ isRequest, Derived, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred)
+{
+ switch(state_)
+ {
+ case 0:
+ state_ = 1;
+ if(b_.size() == 0)
+ goto do_read;
+ goto do_parse;
+
+ case 1:
+ state_ = 2;
+ case 2:
+ if(ec == boost::asio::error::eof)
+ {
+ BOOST_ASSERT(bytes_transferred == 0);
+ if(p_.got_some())
+ {
+ // caller sees EOF on next read
+ ec.assign(0, ec.category());
+ p_.put_eof(ec);
+ if(ec)
+ goto upcall;
+ BOOST_ASSERT(p_.is_done());
+ goto upcall;
+ }
+ ec = error::end_of_stream;
+ goto upcall;
+ }
+ if(ec)
+ goto upcall;
+ b_.commit(bytes_transferred);
+
+ do_parse:
+ {
+ auto const used = p_.put(b_.data(), ec);
+ bytes_transferred_ += used;
+ b_.consume(used);
+ if(! ec || ec != http::error::need_more)
+ goto do_upcall;
+ ec.assign(0, ec.category());
+ }
+
+ do_read:
+ try
+ {
+ mb_.emplace(b_.prepare(
+ read_size_or_throw(b_, 65536)));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ goto do_upcall;
+ }
+ return s_.async_read_some(*mb_, std::move(*this));
+
+ do_upcall:
+ if(state_ >= 2)
+ goto upcall;
+ state_ = 3;
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+
+ case 3:
+ break;
+ }
+upcall:
+ h_(ec, bytes_transferred_);
+}
+
+//------------------------------------------------------------------------------
+
+struct parser_is_done
+{
+ template<bool isRequest, class Derived>
+ bool
+ operator()(basic_parser<
+ isRequest, Derived> const& p) const
+ {
+ return p.is_done();
+ }
+};
+
+struct parser_is_header_done
+{
+ template<bool isRequest, class Derived>
+ bool
+ operator()(basic_parser<
+ isRequest, Derived> const& p) const
+ {
+ return p.is_header_done();
+ }
+};
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Derived, class Condition,
+ class Handler>
+class read_op
+{
+ int state_ = 0;
+ Stream& s_;
+ DynamicBuffer& b_;
+ basic_parser<isRequest, Derived>& p_;
+ std::size_t bytes_transferred_ = 0;
+ Handler h_;
+
+public:
+ read_op(read_op&&) = default;
+ read_op(read_op const&) = default;
+
+ template<class DeducedHandler>
+ read_op(DeducedHandler&& h, Stream& s,
+ DynamicBuffer& b, basic_parser<isRequest,
+ Derived>& p)
+ : s_(s)
+ , b_(b)
+ , p_(p)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(read_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->state_ >= 3 ? true :
+ asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Derived, class Condition,
+ class Handler>
+void
+read_op<Stream, DynamicBuffer,
+ isRequest, Derived, Condition, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred)
+{
+ switch(state_)
+ {
+ case 0:
+ if(Condition{}(p_))
+ {
+ state_ = 1;
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec));
+ }
+ state_ = 2;
+
+ do_read:
+ return async_read_some(
+ s_, b_, p_, std::move(*this));
+
+ case 1:
+ goto upcall;
+
+ case 2:
+ case 3:
+ if(ec)
+ goto upcall;
+ bytes_transferred_ += bytes_transferred;
+ if(Condition{}(p_))
+ goto upcall;
+ state_ = 3;
+ goto do_read;
+ }
+upcall:
+ h_(ec, bytes_transferred_);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Body, class Allocator,
+ class Handler>
+class read_msg_op
+{
+ using parser_type =
+ parser<isRequest, Body, Allocator>;
+
+ using message_type =
+ typename parser_type::value_type;
+
+ struct data
+ {
+ int state = 0;
+ Stream& s;
+ DynamicBuffer& b;
+ message_type& m;
+ parser_type p;
+ std::size_t bytes_transferred = 0;
+
+ data(Handler&, Stream& s_,
+ DynamicBuffer& b_, message_type& m_)
+ : s(s_)
+ , b(b_)
+ , m(m_)
+ , p(std::move(m))
+ {
+ p.eager(true);
+ }
+ };
+
+ handler_ptr<data, Handler> d_;
+
+public:
+ read_msg_op(read_msg_op&&) = default;
+ read_msg_op(read_msg_op const&) = default;
+
+ template<class DeducedHandler, class... Args>
+ read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
+ : d_(std::forward<DeducedHandler>(h),
+ s, std::forward<Args>(args)...)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->s.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(read_msg_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->d_->state >= 2 ? true :
+ asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Body, class Allocator,
+ class Handler>
+void
+read_msg_op<Stream, DynamicBuffer,
+ isRequest, Body, Allocator, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred)
+{
+ auto& d = *d_;
+ switch(d.state)
+ {
+ case 0:
+ d.state = 1;
+
+ do_read:
+ return async_read_some(
+ d.s, d.b, d.p, std::move(*this));
+
+ case 1:
+ case 2:
+ if(ec)
+ goto upcall;
+ d.bytes_transferred +=
+ bytes_transferred;
+ if(d.p.is_done())
+ {
+ d.m = d.p.release();
+ goto upcall;
+ }
+ d.state = 2;
+ goto do_read;
+ }
+upcall:
+ bytes_transferred = d.bytes_transferred;
+ d_.invoke(ec, bytes_transferred);
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_some(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ BOOST_ASSERT(! parser.is_done());
+ error_code ec;
+ auto const bytes_transferred =
+ read_some(stream, buffer, parser, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_some(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ BOOST_ASSERT(! parser.is_done());
+ std::size_t bytes_transferred = 0;
+ if(buffer.size() == 0)
+ goto do_read;
+ for(;;)
+ {
+ // invoke parser
+ {
+ auto const n = parser.put(buffer.data(), ec);
+ bytes_transferred += n;
+ buffer.consume(n);
+ if(! ec)
+ break;
+ if(ec != http::error::need_more)
+ break;
+ }
+ do_read:
+ boost::optional<typename
+ DynamicBuffer::mutable_buffers_type> b;
+ try
+ {
+ b.emplace(buffer.prepare(
+ read_size_or_throw(buffer, 65536)));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ return bytes_transferred;
+ }
+ auto const n = stream.read_some(*b, ec);
+ if(ec == boost::asio::error::eof)
+ {
+ BOOST_ASSERT(n == 0);
+ if(parser.got_some())
+ {
+ // caller sees EOF on next read
+ parser.put_eof(ec);
+ if(ec)
+ break;
+ BOOST_ASSERT(parser.is_done());
+ break;
+ }
+ ec = error::end_of_stream;
+ break;
+ }
+ if(ec)
+ break;
+ buffer.commit(n);
+ }
+ return bytes_transferred;
+}
+
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read_some(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ BOOST_ASSERT(! parser.is_done());
+ boost::asio::async_completion<ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::read_some_op<AsyncReadStream,
+ DynamicBuffer, isRequest, Derived, BOOST_ASIO_HANDLER_TYPE(
+ ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, stream, buffer, parser}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_header(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ read_header(stream, buffer, parser, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_header(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ parser.eager(false);
+ if(parser.is_header_done())
+ {
+ ec.assign(0, ec.category());
+ return 0;
+ }
+ std::size_t bytes_transferred = 0;
+ do
+ {
+ bytes_transferred += read_some(
+ stream, buffer, parser, ec);
+ if(ec)
+ return bytes_transferred;
+ }
+ while(! parser.is_header_done());
+ return bytes_transferred;
+}
+
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read_header(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ parser.eager(false);
+ boost::asio::async_completion<ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::read_op<AsyncReadStream, DynamicBuffer,
+ isRequest, Derived, detail::parser_is_header_done,
+ BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, stream, buffer, parser}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ read(stream, buffer, parser, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ parser.eager(true);
+ if(parser.is_done())
+ {
+ ec.assign(0, ec.category());
+ return 0;
+ }
+ std::size_t bytes_transferred = 0;
+ do
+ {
+ bytes_transferred += read_some(
+ stream, buffer, parser, ec);
+ if(ec)
+ return bytes_transferred;
+ }
+ while(! parser.is_done());
+ return bytes_transferred;
+}
+
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ parser.eager(true);
+ boost::asio::async_completion<
+ ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::read_op<AsyncReadStream, DynamicBuffer,
+ isRequest, Derived, detail::parser_is_done,
+ BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, stream, buffer, parser}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Body, class Allocator>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ message<isRequest, Body, basic_fields<Allocator>>& msg)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_reader<Body>::value,
+ "BodyReader requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ read(stream, buffer, msg, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Body, class Allocator>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ message<isRequest, Body, basic_fields<Allocator>>& msg,
+ error_code& ec)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_reader<Body>::value,
+ "BodyReader requirements not met");
+ parser<isRequest, Body, Allocator> p{std::move(msg)};
+ p.eager(true);
+ auto const bytes_transferred =
+ read(stream, buffer, p.base(), ec);
+ if(ec)
+ return bytes_transferred;
+ msg = p.release();
+ return bytes_transferred;
+}
+
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Body, class Allocator,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ message<isRequest, Body, basic_fields<Allocator>>& msg,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_reader<Body>::value,
+ "BodyReader requirements not met");
+ boost::asio::async_completion<
+ ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::read_msg_op<
+ AsyncReadStream,
+ DynamicBuffer,
+ isRequest, Body, Allocator,
+ BOOST_ASIO_HANDLER_TYPE(
+ ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, stream, buffer, msg}();
+ return init.result.get();
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/rfc7230.ipp b/boost/beast/http/impl/rfc7230.ipp
new file mode 100644
index 0000000000..96ec902ceb
--- /dev/null
+++ b/boost/beast/http/impl/rfc7230.ipp
@@ -0,0 +1,572 @@
+//
+// 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_HTTP_IMPL_RFC7230_IPP
+#define BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
+
+#include <boost/beast/http/detail/rfc7230.hpp>
+#include <iterator>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+class param_list::const_iterator
+{
+ using iter_type = string_view::const_iterator;
+
+ std::string s_;
+ detail::param_iter pi_;
+
+public:
+ using value_type = param_list::value_type;
+ using pointer = value_type const*;
+ using reference = value_type const&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::input_iterator_tag;
+
+ const_iterator() = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return
+ other.pi_.it == pi_.it &&
+ other.pi_.last == pi_.last &&
+ other.pi_.first == pi_.first;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return pi_.v;
+ }
+
+ pointer
+ operator->() const
+ {
+ return &*(*this);
+ }
+
+ const_iterator&
+ operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+private:
+ friend class param_list;
+
+ const_iterator(iter_type first, iter_type last)
+ {
+ pi_.it = first;
+ pi_.first = first;
+ pi_.last = last;
+ increment();
+ }
+
+ template<class = void>
+ static
+ std::string
+ unquote(string_view sr);
+
+ template<class = void>
+ void
+ increment();
+};
+
+inline
+auto
+param_list::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+param_list::
+end() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+inline
+auto
+param_list::
+cbegin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+param_list::
+cend() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+template<class>
+std::string
+param_list::const_iterator::
+unquote(string_view sr)
+{
+ std::string s;
+ s.reserve(sr.size());
+ auto it = sr.begin() + 1;
+ auto end = sr.end() - 1;
+ while(it != end)
+ {
+ if(*it == '\\')
+ ++it;
+ s.push_back(*it);
+ ++it;
+ }
+ return s;
+}
+
+template<class>
+void
+param_list::const_iterator::
+increment()
+{
+ s_.clear();
+ pi_.increment();
+ if(pi_.empty())
+ {
+ pi_.it = pi_.last;
+ pi_.first = pi_.last;
+ }
+ else if(! pi_.v.second.empty() &&
+ pi_.v.second.front() == '"')
+ {
+ s_ = unquote(pi_.v.second);
+ pi_.v.second = string_view{
+ s_.data(), s_.size()};
+ }
+}
+
+//------------------------------------------------------------------------------
+
+class ext_list::const_iterator
+{
+ ext_list::value_type v_;
+ iter_type it_;
+ iter_type first_;
+ iter_type last_;
+
+public:
+ using value_type = ext_list::value_type;
+ using pointer = value_type const*;
+ using reference = value_type const&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::forward_iterator_tag;
+
+ const_iterator() = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return
+ other.it_ == it_ &&
+ other.first_ == first_ &&
+ other.last_ == last_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return v_;
+ }
+
+ pointer
+ operator->() const
+ {
+ return &*(*this);
+ }
+
+ const_iterator&
+ operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+private:
+ friend class ext_list;
+
+ const_iterator(iter_type begin, iter_type end)
+ {
+ it_ = begin;
+ first_ = begin;
+ last_ = end;
+ increment();
+ }
+
+ template<class = void>
+ void
+ increment();
+};
+
+inline
+auto
+ext_list::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+ext_list::
+end() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+inline
+auto
+ext_list::
+cbegin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+ext_list::
+cend() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+template<class T>
+auto
+ext_list::
+find(T const& s) ->
+ const_iterator
+{
+ return std::find_if(begin(), end(),
+ [&s](value_type const& v)
+ {
+ return iequals(s, v.first);
+ });
+}
+
+template<class T>
+bool
+ext_list::
+exists(T const& s)
+{
+ return find(s) != end();
+}
+
+template<class>
+void
+ext_list::const_iterator::
+increment()
+{
+ /*
+ ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
+ ext = token param-list
+ param-list = *( OWS ";" OWS param )
+ param = token OWS "=" OWS ( token / quoted-string )
+
+ chunked;a=b;i=j,gzip;windowBits=12
+ x,y
+ ,,,,,chameleon
+ */
+ auto const err =
+ [&]
+ {
+ it_ = last_;
+ first_ = last_;
+ };
+ auto need_comma = it_ != first_;
+ v_.first = {};
+ first_ = it_;
+ for(;;)
+ {
+ detail::skip_ows(it_, last_);
+ if(it_ == last_)
+ return err();
+ auto const c = *it_;
+ if(detail::is_token_char(c))
+ {
+ if(need_comma)
+ return err();
+ auto const p0 = it_;
+ for(;;)
+ {
+ ++it_;
+ if(it_ == last_)
+ break;
+ if(! detail::is_token_char(*it_))
+ break;
+ }
+ v_.first = string_view{&*p0,
+ static_cast<std::size_t>(it_ - p0)};
+ detail::param_iter pi;
+ pi.it = it_;
+ pi.first = it_;
+ pi.last = last_;
+ for(;;)
+ {
+ pi.increment();
+ if(pi.empty())
+ break;
+ }
+ v_.second = param_list{string_view{&*it_,
+ static_cast<std::size_t>(pi.it - it_)}};
+ it_ = pi.it;
+ return;
+ }
+ if(c != ',')
+ return err();
+ need_comma = false;
+ ++it_;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+class token_list::const_iterator
+{
+ token_list::value_type v_;
+ iter_type it_;
+ iter_type first_;
+ iter_type last_;
+
+public:
+ using value_type = token_list::value_type;
+ using pointer = value_type const*;
+ using reference = value_type const&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::forward_iterator_tag;
+
+ const_iterator() = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return
+ other.it_ == it_ &&
+ other.first_ == first_ &&
+ other.last_ == last_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return v_;
+ }
+
+ pointer
+ operator->() const
+ {
+ return &*(*this);
+ }
+
+ const_iterator&
+ operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+private:
+ friend class token_list;
+
+ const_iterator(iter_type begin, iter_type end)
+ {
+ it_ = begin;
+ first_ = begin;
+ last_ = end;
+ increment();
+ }
+
+ template<class = void>
+ void
+ increment();
+};
+
+inline
+auto
+token_list::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+token_list::
+end() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+inline
+auto
+token_list::
+cbegin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+token_list::
+cend() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+template<class>
+void
+token_list::const_iterator::
+increment()
+{
+ /*
+ token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
+ */
+ auto const err =
+ [&]
+ {
+ it_ = last_;
+ first_ = last_;
+ };
+ auto need_comma = it_ != first_;
+ v_ = {};
+ first_ = it_;
+ for(;;)
+ {
+ detail::skip_ows(it_, last_);
+ if(it_ == last_)
+ return err();
+ auto const c = *it_;
+ if(detail::is_token_char(c))
+ {
+ if(need_comma)
+ return err();
+ auto const p0 = it_;
+ for(;;)
+ {
+ ++it_;
+ if(it_ == last_)
+ break;
+ if(! detail::is_token_char(*it_))
+ break;
+ }
+ v_ = string_view{&*p0,
+ static_cast<std::size_t>(it_ - p0)};
+ return;
+ }
+ if(c != ',')
+ return err();
+ need_comma = false;
+ ++it_;
+ }
+}
+
+template<class T>
+bool
+token_list::
+exists(T const& s)
+{
+ return std::find_if(begin(), end(),
+ [&s](value_type const& v)
+ {
+ return iequals(s, v);
+ }
+ ) != end();
+}
+
+template<class Policy>
+bool
+validate_list(detail::basic_parsed_list<
+ Policy> const& list)
+{
+ auto const last = list.end();
+ auto it = list.begin();
+ if(it.error())
+ return false;
+ while(it != last)
+ {
+ ++it;
+ if(it.error())
+ return false;
+ if(it == last)
+ break;
+ }
+ return true;
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
+
diff --git a/boost/beast/http/impl/serializer.ipp b/boost/beast/http/impl/serializer.ipp
new file mode 100644
index 0000000000..11cf857845
--- /dev/null
+++ b/boost/beast/http/impl/serializer.ipp
@@ -0,0 +1,430 @@
+//
+// 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_HTTP_IMPL_SERIALIZER_IPP
+#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP
+
+#include <boost/beast/core/detail/buffers_ref.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/status.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/assert.hpp>
+#include <ostream>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<
+ bool isRequest, class Body, class Fields>
+void
+serializer<isRequest, Body, Fields>::
+frdinit(std::true_type)
+{
+ frd_.emplace(m_, m_.version(), m_.method());
+}
+
+template<
+ bool isRequest, class Body, class Fields>
+void
+serializer<isRequest, Body, Fields>::
+frdinit(std::false_type)
+{
+ frd_.emplace(m_, m_.version(), m_.result_int());
+}
+
+template<
+ bool isRequest, class Body, class Fields>
+template<std::size_t I, class Visit>
+inline
+void
+serializer<isRequest, Body, Fields>::
+do_visit(error_code& ec, Visit& visit)
+{
+ pv_.template emplace<I>(limit_, v_.template get<I>());
+ visit(ec, beast::detail::make_buffers_ref(
+ pv_.template get<I>()));
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ bool isRequest, class Body, class Fields>
+serializer<isRequest, Body, Fields>::
+serializer(value_type& m)
+ : m_(m)
+ , rd_(m_)
+{
+}
+
+template<
+ bool isRequest, class Body, class Fields>
+template<class Visit>
+void
+serializer<isRequest, Body, Fields>::
+next(error_code& ec, Visit&& visit)
+{
+ using boost::asio::buffer_size;
+ switch(s_)
+ {
+ case do_construct:
+ {
+ frdinit(std::integral_constant<bool,
+ isRequest>{});
+ if(m_.chunked())
+ goto go_init_c;
+ s_ = do_init;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_init:
+ {
+ rd_.init(ec);
+ if(ec)
+ return;
+ if(split_)
+ goto go_header_only;
+ auto result = rd_.get(ec);
+ if(ec == error::need_more)
+ goto go_header_only;
+ if(ec)
+ return;
+ if(! result)
+ goto go_header_only;
+ more_ = result->second;
+ v_.template emplace<2>(
+ boost::in_place_init,
+ frd_->get(),
+ result->first);
+ s_ = do_header;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_header:
+ do_visit<2>(ec, visit);
+ break;
+
+ go_header_only:
+ v_.template emplace<1>(frd_->get());
+ s_ = do_header_only;
+ BOOST_BEAST_FALLTHROUGH;
+ case do_header_only:
+ do_visit<1>(ec, visit);
+ break;
+
+ case do_body:
+ s_ = do_body + 1;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case do_body + 1:
+ {
+ auto result = rd_.get(ec);
+ if(ec)
+ return;
+ if(! result)
+ goto go_complete;
+ more_ = result->second;
+ v_.template emplace<3>(result->first);
+ s_ = do_body + 2;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_body + 2:
+ do_visit<3>(ec, visit);
+ break;
+
+ //----------------------------------------------------------------------
+
+ go_init_c:
+ s_ = do_init_c;
+ BOOST_BEAST_FALLTHROUGH;
+ case do_init_c:
+ {
+ rd_.init(ec);
+ if(ec)
+ return;
+ if(split_)
+ goto go_header_only_c;
+ auto result = rd_.get(ec);
+ if(ec == error::need_more)
+ goto go_header_only_c;
+ if(ec)
+ return;
+ if(! result)
+ goto go_header_only_c;
+ more_ = result->second;
+ if(! more_)
+ {
+ // do it all in one buffer
+ v_.template emplace<7>(
+ boost::in_place_init,
+ frd_->get(),
+ buffer_size(result->first),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ result->first,
+ chunk_crlf{},
+ detail::chunk_last(),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{});
+ goto go_all_c;
+ }
+ v_.template emplace<4>(
+ boost::in_place_init,
+ frd_->get(),
+ buffer_size(result->first),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ result->first,
+ chunk_crlf{});
+ s_ = do_header_c;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_header_c:
+ do_visit<4>(ec, visit);
+ break;
+
+ go_header_only_c:
+ v_.template emplace<1>(frd_->get());
+ s_ = do_header_only_c;
+ case do_header_only_c:
+ do_visit<1>(ec, visit);
+ break;
+
+ case do_body_c:
+ s_ = do_body_c + 1;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case do_body_c + 1:
+ {
+ auto result = rd_.get(ec);
+ if(ec)
+ return;
+ if(! result)
+ goto go_final_c;
+ more_ = result->second;
+ if(! more_)
+ {
+ // do it all in one buffer
+ v_.template emplace<6>(
+ boost::in_place_init,
+ buffer_size(result->first),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ result->first,
+ chunk_crlf{},
+ detail::chunk_last(),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{});
+ goto go_body_final_c;
+ }
+ v_.template emplace<5>(
+ boost::in_place_init,
+ buffer_size(result->first),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ result->first,
+ chunk_crlf{});
+ s_ = do_body_c + 2;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_body_c + 2:
+ do_visit<5>(ec, visit);
+ break;
+
+ go_body_final_c:
+ s_ = do_body_final_c;
+ BOOST_BEAST_FALLTHROUGH;
+ case do_body_final_c:
+ do_visit<6>(ec, visit);
+ break;
+
+ go_all_c:
+ s_ = do_all_c;
+ BOOST_BEAST_FALLTHROUGH;
+ case do_all_c:
+ do_visit<7>(ec, visit);
+ break;
+
+ go_final_c:
+ case do_final_c:
+ v_.template emplace<8>(
+ boost::in_place_init,
+ detail::chunk_last(),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{});
+ s_ = do_final_c + 1;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case do_final_c + 1:
+ do_visit<8>(ec, visit);
+ break;
+
+ //----------------------------------------------------------------------
+
+ default:
+ case do_complete:
+ BOOST_ASSERT(false);
+ break;
+
+ go_complete:
+ s_ = do_complete;
+ break;
+ }
+}
+
+template<
+ bool isRequest, class Body, class Fields>
+void
+serializer<isRequest, Body, Fields>::
+consume(std::size_t n)
+{
+ using boost::asio::buffer_size;
+ switch(s_)
+ {
+ case do_header:
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<2>()));
+ v_.template get<2>().consume(n);
+ if(buffer_size(v_.template get<2>()) > 0)
+ break;
+ header_done_ = true;
+ v_.reset();
+ if(! more_)
+ goto go_complete;
+ s_ = do_body + 1;
+ break;
+
+ case do_header_only:
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<1>()));
+ v_.template get<1>().consume(n);
+ if(buffer_size(v_.template get<1>()) > 0)
+ break;
+ frd_ = boost::none;
+ header_done_ = true;
+ if(! split_)
+ goto go_complete;
+ s_ = do_body;
+ break;
+
+ case do_body + 2:
+ {
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<3>()));
+ v_.template get<3>().consume(n);
+ if(buffer_size(v_.template get<3>()) > 0)
+ break;
+ v_.reset();
+ if(! more_)
+ goto go_complete;
+ s_ = do_body + 1;
+ break;
+ }
+
+ //----------------------------------------------------------------------
+
+ case do_header_c:
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<4>()));
+ v_.template get<4>().consume(n);
+ if(buffer_size(v_.template get<4>()) > 0)
+ break;
+ header_done_ = true;
+ v_.reset();
+ if(more_)
+ s_ = do_body_c + 1;
+ else
+ s_ = do_final_c;
+ break;
+
+ case do_header_only_c:
+ {
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<1>()));
+ v_.template get<1>().consume(n);
+ if(buffer_size(v_.template get<1>()) > 0)
+ break;
+ frd_ = boost::none;
+ header_done_ = true;
+ if(! split_)
+ {
+ s_ = do_final_c;
+ break;
+ }
+ s_ = do_body_c;
+ break;
+ }
+
+ case do_body_c + 2:
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<5>()));
+ v_.template get<5>().consume(n);
+ if(buffer_size(v_.template get<5>()) > 0)
+ break;
+ v_.reset();
+ if(more_)
+ s_ = do_body_c + 1;
+ else
+ s_ = do_final_c;
+ break;
+
+ case do_body_final_c:
+ {
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<6>()));
+ v_.template get<6>().consume(n);
+ if(buffer_size(v_.template get<6>()) > 0)
+ break;
+ v_.reset();
+ s_ = do_complete;
+ break;
+ }
+
+ case do_all_c:
+ {
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<7>()));
+ v_.template get<7>().consume(n);
+ if(buffer_size(v_.template get<7>()) > 0)
+ break;
+ header_done_ = true;
+ v_.reset();
+ s_ = do_complete;
+ break;
+ }
+
+ case do_final_c + 1:
+ BOOST_ASSERT(buffer_size(v_.template get<8>()));
+ v_.template get<8>().consume(n);
+ if(buffer_size(v_.template get<8>()) > 0)
+ break;
+ v_.reset();
+ goto go_complete;
+
+ //----------------------------------------------------------------------
+
+ default:
+ BOOST_ASSERT(false);
+ case do_complete:
+ break;
+
+ go_complete:
+ s_ = do_complete;
+ break;
+ }
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/status.ipp b/boost/beast/http/impl/status.ipp
new file mode 100644
index 0000000000..1cc8f34b63
--- /dev/null
+++ b/boost/beast/http/impl/status.ipp
@@ -0,0 +1,252 @@
+//
+// 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_HTTP_IMPL_STATUS_IPP
+#define BOOST_BEAST_HTTP_IMPL_STATUS_IPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/throw_exception.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+namespace detail {
+
+template<class = void>
+status
+int_to_status(unsigned v)
+{
+ switch(static_cast<status>(v))
+ {
+ // 1xx
+ case status::continue_:
+ case status::switching_protocols:
+ case status::processing:
+ BOOST_BEAST_FALLTHROUGH;
+
+ // 2xx
+ case status::ok:
+ case status::created:
+ case status::accepted:
+ case status::non_authoritative_information:
+ case status::no_content:
+ case status::reset_content:
+ case status::partial_content:
+ case status::multi_status:
+ case status::already_reported:
+ case status::im_used:
+ BOOST_BEAST_FALLTHROUGH;
+
+ // 3xx
+ case status::multiple_choices:
+ case status::moved_permanently:
+ case status::found:
+ case status::see_other:
+ case status::not_modified:
+ case status::use_proxy:
+ case status::temporary_redirect:
+ case status::permanent_redirect:
+ BOOST_BEAST_FALLTHROUGH;
+
+ // 4xx
+ case status::bad_request:
+ case status::unauthorized:
+ case status::payment_required:
+ case status::forbidden:
+ case status::not_found:
+ case status::method_not_allowed:
+ case status::not_acceptable:
+ case status::proxy_authentication_required:
+ case status::request_timeout:
+ case status::conflict:
+ case status::gone:
+ case status::length_required:
+ case status::precondition_failed:
+ case status::payload_too_large:
+ case status::uri_too_long:
+ case status::unsupported_media_type:
+ case status::range_not_satisfiable:
+ case status::expectation_failed:
+ case status::misdirected_request:
+ case status::unprocessable_entity:
+ case status::locked:
+ case status::failed_dependency:
+ case status::upgrade_required:
+ case status::precondition_required:
+ case status::too_many_requests:
+ case status::request_header_fields_too_large:
+ case status::connection_closed_without_response:
+ case status::unavailable_for_legal_reasons:
+ case status::client_closed_request:
+ BOOST_BEAST_FALLTHROUGH;
+
+ // 5xx
+ case status::internal_server_error:
+ case status::not_implemented:
+ case status::bad_gateway:
+ case status::service_unavailable:
+ case status::gateway_timeout:
+ case status::http_version_not_supported:
+ case status::variant_also_negotiates:
+ case status::insufficient_storage:
+ case status::loop_detected:
+ case status::not_extended:
+ case status::network_authentication_required:
+ case status::network_connect_timeout_error:
+ return static_cast<status>(v);
+
+ default:
+ break;
+ }
+ return status::unknown;
+}
+
+template<class = void>
+string_view
+status_to_string(unsigned v)
+{
+ switch(static_cast<status>(v))
+ {
+ // 1xx
+ case status::continue_: return "Continue";
+ case status::switching_protocols: return "Switching Protocols";
+ case status::processing: return "Processing";
+
+ // 2xx
+ case status::ok: return "OK";
+ case status::created: return "Created";
+ case status::accepted: return "Accepted";
+ case status::non_authoritative_information: return "Non-Authoritative Information";
+ case status::no_content: return "No Content";
+ case status::reset_content: return "Reset Content";
+ case status::partial_content: return "Partial Content";
+ case status::multi_status: return "Multi-Status";
+ case status::already_reported: return "Already Reported";
+ case status::im_used: return "IM Used";
+
+ // 3xx
+ case status::multiple_choices: return "Multiple Choices";
+ case status::moved_permanently: return "Moved Permanently";
+ case status::found: return "Found";
+ case status::see_other: return "See Other";
+ case status::not_modified: return "Not Modified";
+ case status::use_proxy: return "Use Proxy";
+ case status::temporary_redirect: return "Temporary Redirect";
+ case status::permanent_redirect: return "Permanent Redirect";
+
+ // 4xx
+ case status::bad_request: return "Bad Request";
+ case status::unauthorized: return "Unauthorized";
+ case status::payment_required: return "Payment Required";
+ case status::forbidden: return "Forbidden";
+ case status::not_found: return "Not Found";
+ case status::method_not_allowed: return "Method Not Allowed";
+ case status::not_acceptable: return "Not Acceptable";
+ case status::proxy_authentication_required: return "Proxy Authentication Required";
+ case status::request_timeout: return "Request Timeout";
+ case status::conflict: return "Conflict";
+ case status::gone: return "Gone";
+ case status::length_required: return "Length Required";
+ case status::precondition_failed: return "Precondition Failed";
+ case status::payload_too_large: return "Payload Too Large";
+ case status::uri_too_long: return "URI Too Long";
+ case status::unsupported_media_type: return "Unsupported Media Type";
+ case status::range_not_satisfiable: return "Range Not Satisfiable";
+ case status::expectation_failed: return "Expectation Failed";
+ case status::misdirected_request: return "Misdirected Request";
+ case status::unprocessable_entity: return "Unprocessable Entity";
+ case status::locked: return "Locked";
+ case status::failed_dependency: return "Failed Dependency";
+ case status::upgrade_required: return "Upgrade Required";
+ case status::precondition_required: return "Precondition Required";
+ case status::too_many_requests: return "Too Many Requests";
+ case status::request_header_fields_too_large: return "Request Header Fields Too Large";
+ case status::connection_closed_without_response: return "Connection Closed Without Response";
+ case status::unavailable_for_legal_reasons: return "Unavailable For Legal Reasons";
+ case status::client_closed_request: return "Client Closed Request";
+ // 5xx
+ case status::internal_server_error: return "Internal Server Error";
+ case status::not_implemented: return "Not Implemented";
+ case status::bad_gateway: return "Bad Gateway";
+ case status::service_unavailable: return "Service Unavailable";
+ case status::gateway_timeout: return "Gateway Timeout";
+ case status::http_version_not_supported: return "HTTP Version Not Supported";
+ case status::variant_also_negotiates: return "Variant Also Negotiates";
+ case status::insufficient_storage: return "Insufficient Storage";
+ case status::loop_detected: return "Loop Detected";
+ case status::not_extended: return "Not Extended";
+ case status::network_authentication_required: return "Network Authentication Required";
+ case status::network_connect_timeout_error: return "Network Connect Timeout Error";
+
+ default:
+ break;
+ }
+ return "<unknown-status>";
+}
+
+template<class = void>
+status_class
+to_status_class(unsigned v)
+{
+ switch(v / 100)
+ {
+ case 1: return status_class::informational;
+ case 2: return status_class::successful;
+ case 3: return status_class::redirection;
+ case 4: return status_class::client_error;
+ case 5: return status_class::server_error;
+ default:
+ break;
+ }
+ return status_class::unknown;
+}
+
+} // detail
+
+inline
+status
+int_to_status(unsigned v)
+{
+ return detail::int_to_status(v);
+}
+
+inline
+status_class
+to_status_class(unsigned v)
+{
+ return detail::to_status_class(v);
+}
+
+inline
+status_class
+to_status_class(status v)
+{
+ return to_status_class(static_cast<int>(v));
+}
+
+inline
+string_view
+obsolete_reason(status v)
+{
+ return detail::status_to_string(
+ static_cast<unsigned>(v));
+}
+
+inline
+std::ostream&
+operator<<(std::ostream& os, status v)
+{
+ return os << obsolete_reason(v);
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/verb.ipp b/boost/beast/http/impl/verb.ipp
new file mode 100644
index 0000000000..820609165b
--- /dev/null
+++ b/boost/beast/http/impl/verb.ipp
@@ -0,0 +1,337 @@
+//
+// 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_HTTP_IMPL_VERB_IPP
+#define BOOST_BEAST_HTTP_IMPL_VERB_IPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+
+template<class = void>
+inline
+string_view
+verb_to_string(verb v)
+{
+ switch(v)
+ {
+ case verb::delete_: return "DELETE";
+ case verb::get: return "GET";
+ case verb::head: return "HEAD";
+ case verb::post: return "POST";
+ case verb::put: return "PUT";
+ case verb::connect: return "CONNECT";
+ case verb::options: return "OPTIONS";
+ case verb::trace: return "TRACE";
+
+ case verb::copy: return "COPY";
+ case verb::lock: return "LOCK";
+ case verb::mkcol: return "MKCOL";
+ case verb::move: return "MOVE";
+ case verb::propfind: return "PROPFIND";
+ case verb::proppatch: return "PROPPATCH";
+ case verb::search: return "SEARCH";
+ case verb::unlock: return "UNLOCK";
+ case verb::bind: return "BIND";
+ case verb::rebind: return "REBIND";
+ case verb::unbind: return "UNBIND";
+ case verb::acl: return "ACL";
+
+ case verb::report: return "REPORT";
+ case verb::mkactivity: return "MKACTIVITY";
+ case verb::checkout: return "CHECKOUT";
+ case verb::merge: return "MERGE";
+
+ case verb::msearch: return "M-SEARCH";
+ case verb::notify: return "NOTIFY";
+ case verb::subscribe: return "SUBSCRIBE";
+ case verb::unsubscribe: return "UNSUBSCRIBE";
+
+ case verb::patch: return "PATCH";
+ case verb::purge: return "PURGE";
+
+ case verb::mkcalendar: return "MKCALENDAR";
+
+ case verb::link: return "LINK";
+ case verb::unlink: return "UNLINK";
+
+ case verb::unknown:
+ return "<unknown>";
+ }
+
+ BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"});
+}
+
+template<class = void>
+verb
+string_to_verb(string_view v)
+{
+/*
+ ACL
+ BIND
+ CHECKOUT
+ CONNECT
+ COPY
+ DELETE
+ GET
+ HEAD
+ LINK
+ LOCK
+ M-SEARCH
+ MERGE
+ MKACTIVITY
+ MKCALENDAR
+ MKCOL
+ MOVE
+ NOTIFY
+ OPTIONS
+ PATCH
+ POST
+ PROPFIND
+ PROPPATCH
+ PURGE
+ PUT
+ REBIND
+ REPORT
+ SEARCH
+ SUBSCRIBE
+ TRACE
+ UNBIND
+ UNLINK
+ UNLOCK
+ UNSUBSCRIBE
+*/
+ if(v.size() < 3)
+ return verb::unknown;
+ // s must be null terminated
+ auto const eq =
+ [](string_view sv, char const* s)
+ {
+ auto p = sv.data();
+ for(;;)
+ {
+ if(*s != *p)
+ return false;
+ ++s;
+ ++p;
+ if(! *s)
+ return p == sv.end();
+ }
+ };
+ auto c = v[0];
+ v.remove_prefix(1);
+ switch(c)
+ {
+ case 'A':
+ if(v == "CL")
+ return verb::acl;
+ break;
+
+ case 'B':
+ if(v == "IND")
+ return verb::bind;
+ break;
+
+ case 'C':
+ c = v[0];
+ v.remove_prefix(1);
+ switch(c)
+ {
+ case 'H':
+ if(eq(v, "ECKOUT"))
+ return verb::checkout;
+ break;
+
+ case 'O':
+ if(eq(v, "NNECT"))
+ return verb::connect;
+ if(eq(v, "PY"))
+ return verb::copy;
+ BOOST_BEAST_FALLTHROUGH;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'D':
+ if(eq(v, "ELETE"))
+ return verb::delete_;
+ break;
+
+ case 'G':
+ if(eq(v, "ET"))
+ return verb::get;
+ break;
+
+ case 'H':
+ if(eq(v, "EAD"))
+ return verb::head;
+ break;
+
+ case 'L':
+ if(eq(v, "INK"))
+ return verb::link;
+ if(eq(v, "OCK"))
+ return verb::lock;
+ break;
+
+ case 'M':
+ c = v[0];
+ v.remove_prefix(1);
+ switch(c)
+ {
+ case '-':
+ if(eq(v, "SEARCH"))
+ return verb::msearch;
+ break;
+
+ case 'E':
+ if(eq(v, "RGE"))
+ return verb::merge;
+ break;
+
+ case 'K':
+ if(eq(v, "ACTIVITY"))
+ return verb::mkactivity;
+ if(v[0] == 'C')
+ {
+ v.remove_prefix(1);
+ if(eq(v, "ALENDAR"))
+ return verb::mkcalendar;
+ if(eq(v, "OL"))
+ return verb::mkcol;
+ break;
+ }
+ break;
+
+ case 'O':
+ if(eq(v, "VE"))
+ return verb::move;
+ BOOST_BEAST_FALLTHROUGH;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'N':
+ if(eq(v, "OTIFY"))
+ return verb::notify;
+ break;
+
+ case 'O':
+ if(eq(v, "PTIONS"))
+ return verb::options;
+ break;
+
+ case 'P':
+ c = v[0];
+ v.remove_prefix(1);
+ switch(c)
+ {
+ case 'A':
+ if(eq(v, "TCH"))
+ return verb::patch;
+ break;
+
+ case 'O':
+ if(eq(v, "ST"))
+ return verb::post;
+ break;
+
+ case 'R':
+ if(eq(v, "OPFIND"))
+ return verb::propfind;
+ if(eq(v, "OPPATCH"))
+ return verb::proppatch;
+ break;
+
+ case 'U':
+ if(eq(v, "RGE"))
+ return verb::purge;
+ if(eq(v, "T"))
+ return verb::put;
+ BOOST_BEAST_FALLTHROUGH;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'R':
+ if(v[0] != 'E')
+ break;
+ v.remove_prefix(1);
+ if(eq(v, "BIND"))
+ return verb::rebind;
+ if(eq(v, "PORT"))
+ return verb::report;
+ break;
+
+ case 'S':
+ if(eq(v, "EARCH"))
+ return verb::search;
+ if(eq(v, "UBSCRIBE"))
+ return verb::subscribe;
+ break;
+
+ case 'T':
+ if(eq(v, "RACE"))
+ return verb::trace;
+ break;
+
+ case 'U':
+ if(v[0] != 'N')
+ break;
+ v.remove_prefix(1);
+ if(eq(v, "BIND"))
+ return verb::unbind;
+ if(eq(v, "LINK"))
+ return verb::unlink;
+ if(eq(v, "LOCK"))
+ return verb::unlock;
+ if(eq(v, "SUBSCRIBE"))
+ return verb::unsubscribe;
+ break;
+
+ default:
+ break;
+ }
+
+ return verb::unknown;
+}
+
+} // detail
+
+inline
+string_view
+to_string(verb v)
+{
+ return detail::verb_to_string(v);
+}
+
+inline
+verb
+string_to_verb(string_view s)
+{
+ return detail::string_to_verb(s);
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/write.ipp b/boost/beast/http/impl/write.ipp
new file mode 100644
index 0000000000..9d5be277ec
--- /dev/null
+++ b/boost/beast/http/impl/write.ipp
@@ -0,0 +1,887 @@
+//
+// 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_HTTP_IMPL_WRITE_IPP
+#define BOOST_BEAST_HTTP_IMPL_WRITE_IPP
+
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/ostream.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/asio/write.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+#include <ostream>
+#include <sstream>
+
+namespace boost {
+namespace beast {
+namespace http {
+namespace detail {
+
+template<
+ class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+class write_some_op
+{
+ Stream& s_;
+ serializer<isRequest,Body, Fields>& sr_;
+ Handler h_;
+
+ class lambda
+ {
+ write_some_op& op_;
+
+ public:
+ bool invoked = false;
+
+ explicit
+ lambda(write_some_op& op)
+ : op_(op)
+ {
+ }
+
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffers)
+ {
+ invoked = true;
+ ec.assign(0, ec.category());
+ return op_.s_.async_write_some(
+ buffers, std::move(op_));
+ }
+ };
+
+public:
+ write_some_op(write_some_op&&) = default;
+ write_some_op(write_some_op const&) = default;
+
+ template<class DeducedHandler>
+ write_some_op(DeducedHandler&& h, Stream& s,
+ serializer<isRequest, Body, Fields>& sr)
+ : s_(s)
+ , sr_(sr)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()();
+
+ void
+ operator()(
+ error_code ec,
+ std::size_t bytes_transferred);
+
+ friend
+ bool asio_handler_is_continuation(write_some_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<
+ class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+void
+write_some_op<
+ Stream, Handler, isRequest, Body, Fields>::
+operator()()
+{
+ error_code ec;
+ if(! sr_.is_done())
+ {
+ lambda f{*this};
+ sr_.next(ec, f);
+ if(ec)
+ {
+ BOOST_ASSERT(! f.invoked);
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+ }
+ if(f.invoked)
+ {
+ // *this has been moved from,
+ // cannot access members here.
+ return;
+ }
+ // What else could it be?
+ BOOST_ASSERT(sr_.is_done());
+ }
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+}
+
+template<
+ class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+void
+write_some_op<
+ Stream, Handler, isRequest, Body, Fields>::
+operator()(
+ error_code ec, std::size_t bytes_transferred)
+{
+ if(! ec)
+ sr_.consume(bytes_transferred);
+ h_(ec, bytes_transferred);
+}
+
+//------------------------------------------------------------------------------
+
+struct serializer_is_header_done
+{
+ template<
+ bool isRequest, class Body, class Fields>
+ bool
+ operator()(
+ serializer<isRequest, Body, Fields>& sr) const
+ {
+ return sr.is_header_done();
+ }
+};
+
+struct serializer_is_done
+{
+ template<
+ bool isRequest, class Body, class Fields>
+ bool
+ operator()(
+ serializer<isRequest, Body, Fields>& sr) const
+ {
+ return sr.is_done();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+template<
+ class Stream, class Handler, class Predicate,
+ bool isRequest, class Body, class Fields>
+class write_op
+{
+ int state_ = 0;
+ Stream& s_;
+ serializer<isRequest, Body, Fields>& sr_;
+ std::size_t bytes_transferred_ = 0;
+ Handler h_;
+
+public:
+ write_op(write_op&&) = default;
+ write_op(write_op const&) = default;
+
+ template<class DeducedHandler>
+ write_op(DeducedHandler&& h, Stream& s,
+ serializer<isRequest, Body, Fields>& sr)
+ : s_(s)
+ , sr_(sr)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(write_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->state_ >= 3 ||
+ asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<
+ class Stream, class Handler, class Predicate,
+ bool isRequest, class Body, class Fields>
+void
+write_op<Stream, Handler, Predicate,
+ isRequest, Body, Fields>::
+operator()(
+ error_code ec, std::size_t bytes_transferred)
+{
+ if(ec)
+ goto upcall;
+ switch(state_)
+ {
+ case 0:
+ {
+ if(Predicate{}(sr_))
+ {
+ state_ = 1;
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+ }
+ state_ = 2;
+ return beast::http::async_write_some(
+ s_, sr_, std::move(*this));
+ }
+
+ case 1:
+ goto upcall;
+
+ case 2:
+ state_ = 3;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case 3:
+ {
+ bytes_transferred_ += bytes_transferred;
+ if(Predicate{}(sr_))
+ goto upcall;
+ return beast::http::async_write_some(
+ s_, sr_, std::move(*this));
+ }
+ }
+upcall:
+ h_(ec, bytes_transferred_);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+class write_msg_op
+{
+ struct data
+ {
+ Stream& s;
+ serializer<isRequest, Body, Fields> sr;
+
+ data(Handler&, Stream& s_, message<
+ isRequest, Body, Fields>& m_)
+ : s(s_)
+ , sr(m_)
+ {
+ }
+ };
+
+ handler_ptr<data, Handler> d_;
+
+public:
+ write_msg_op(write_msg_op&&) = default;
+ write_msg_op(write_msg_op const&) = default;
+
+ template<class DeducedHandler, class... Args>
+ write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
+ : d_(std::forward<DeducedHandler>(h),
+ s, std::forward<Args>(args)...)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->s.get_executor());
+ }
+
+ void
+ operator()();
+
+ void
+ operator()(
+ error_code ec, std::size_t bytes_transferred);
+
+ friend
+ bool asio_handler_is_continuation(write_msg_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+void
+write_msg_op<
+ Stream, Handler, isRequest, Body, Fields>::
+operator()()
+{
+ auto& d = *d_;
+ return async_write(d.s, d.sr, std::move(*this));
+}
+
+template<class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+void
+write_msg_op<
+ Stream, Handler, isRequest, Body, Fields>::
+operator()(error_code ec, std::size_t bytes_transferred)
+{
+ d_.invoke(ec, bytes_transferred);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Stream>
+class write_some_lambda
+{
+ Stream& stream_;
+
+public:
+ bool invoked = false;
+ std::size_t bytes_transferred = 0;
+
+ explicit
+ write_some_lambda(Stream& stream)
+ : stream_(stream)
+ {
+ }
+
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffers)
+ {
+ invoked = true;
+ bytes_transferred =
+ stream_.write_some(buffers, ec);
+ }
+};
+
+template<class Stream>
+class write_lambda
+{
+ Stream& stream_;
+
+public:
+ bool invoked = false;
+ std::size_t bytes_transferred = 0;
+
+ explicit
+ write_lambda(Stream& stream)
+ : stream_(stream)
+ {
+ }
+
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffers)
+ {
+ invoked = true;
+ bytes_transferred = boost::asio::write(
+ stream_, buffers, ec);
+ }
+};
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_some(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ error_code& ec)
+{
+ if(! sr.is_done())
+ {
+ write_some_lambda<SyncWriteStream> f{stream};
+ sr.next(ec, f);
+ if(ec)
+ return f.bytes_transferred;
+ if(f.invoked)
+ sr.consume(f.bytes_transferred);
+ return f.bytes_transferred;
+ }
+ ec.assign(0, ec.category());
+ return 0;
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_some(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler)
+{
+ boost::asio::async_completion<
+ WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::write_some_op<
+ AsyncWriteStream,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ isRequest, Body, Fields>{
+ init.completion_handler, stream, sr}();
+ return init.result.get();
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_some(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ write_some(stream, sr, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_some(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ error_code& ec)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ return detail::write_some(stream, sr, ec);
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_some(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler)
+{
+ static_assert(is_async_write_stream<
+ AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ return detail::async_write_some(stream, sr,
+ std::forward<WriteHandler>(handler));
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_header(SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ write_header(stream, sr, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_header(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ error_code& ec)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ sr.split(true);
+ std::size_t bytes_transferred = 0;
+ if(! sr.is_header_done())
+ {
+ detail::write_lambda<SyncWriteStream> f{stream};
+ do
+ {
+ sr.next(ec, f);
+ bytes_transferred += f.bytes_transferred;
+ if(ec)
+ return bytes_transferred;
+ BOOST_ASSERT(f.invoked);
+ sr.consume(f.bytes_transferred);
+ }
+ while(! sr.is_header_done());
+ }
+ else
+ {
+ ec.assign(0, ec.category());
+ }
+ return bytes_transferred;
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_header(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler)
+{
+ static_assert(is_async_write_stream<
+ AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ sr.split(true);
+ boost::asio::async_completion<
+ WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::write_op<
+ AsyncWriteStream,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ detail::serializer_is_header_done,
+ isRequest, Body, Fields>{
+ init.completion_handler, stream, sr}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ write(stream, sr, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ error_code& ec)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ std::size_t bytes_transferred = 0;
+ sr.split(false);
+ for(;;)
+ {
+ bytes_transferred +=
+ write_some(stream, sr, ec);
+ if(ec)
+ return bytes_transferred;
+ if(sr.is_done())
+ break;
+ }
+ return bytes_transferred;
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler)
+{
+ static_assert(is_async_write_stream<
+ AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ sr.split(false);
+ boost::asio::async_completion<
+ WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::write_op<
+ AsyncWriteStream,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ detail::serializer_is_done,
+ isRequest, Body, Fields>{
+ init.completion_handler, stream, sr}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ message<isRequest, Body, Fields> const& msg)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ write(stream, msg, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ message<isRequest, Body, Fields> const& msg,
+ error_code& ec)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ serializer<isRequest, Body, Fields> sr{msg};
+ return write(stream, sr, ec);
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write(
+ AsyncWriteStream& stream,
+ message<isRequest, Body, Fields>& msg,
+ WriteHandler&& handler)
+{
+ static_assert(
+ is_async_write_stream<AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ boost::asio::async_completion<
+ WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::write_msg_op<
+ AsyncWriteStream,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ isRequest, Body, Fields>{
+ init.completion_handler, stream, msg}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+template<class Serializer>
+class write_ostream_lambda
+{
+ std::ostream& os_;
+ Serializer& sr_;
+
+public:
+ write_ostream_lambda(std::ostream& os,
+ Serializer& sr)
+ : os_(os)
+ , sr_(sr)
+ {
+ }
+
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffers) const
+ {
+ ec.assign(0, ec.category());
+ if(os_.fail())
+ return;
+ std::size_t bytes_transferred = 0;
+ for(auto b : buffers_range(buffers))
+ {
+ os_.write(reinterpret_cast<char const*>(
+ b.data()), b.size());
+ if(os_.fail())
+ return;
+ bytes_transferred += b.size();
+ }
+ sr_.consume(bytes_transferred);
+ }
+};
+
+} // detail
+
+template<class Fields>
+std::ostream&
+operator<<(std::ostream& os,
+ header<true, Fields> const& h)
+{
+ typename Fields::writer fr{
+ h, h.version(), h.method()};
+ return os << buffers(fr.get());
+}
+
+template<class Fields>
+std::ostream&
+operator<<(std::ostream& os,
+ header<false, Fields> const& h)
+{
+ typename Fields::writer fr{
+ h, h.version(), h.result_int()};
+ return os << buffers(fr.get());
+}
+
+template<bool isRequest, class Body, class Fields>
+std::ostream&
+operator<<(std::ostream& os,
+ message<isRequest, Body, Fields> const& msg)
+{
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ serializer<isRequest, Body, Fields> sr{msg};
+ error_code ec;
+ detail::write_ostream_lambda<decltype(sr)> f{os, sr};
+ do
+ {
+ sr.next(ec, f);
+ if(os.fail())
+ break;
+ if(ec)
+ {
+ os.setstate(std::ios::failbit);
+ break;
+ }
+ }
+ while(! sr.is_done());
+ return os;
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/message.hpp b/boost/beast/http/message.hpp
new file mode 100644
index 0000000000..5cf5d94dc9
--- /dev/null
+++ b/boost/beast/http/message.hpp
@@ -0,0 +1,999 @@
+//
+// 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_HTTP_MESSAGE_HPP
+#define BOOST_BEAST_HTTP_MESSAGE_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/fields.hpp>
+#include <boost/beast/http/verb.hpp>
+#include <boost/beast/http/status.hpp>
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/detail/empty_base_optimization.hpp>
+#include <boost/beast/core/detail/integer_sequence.hpp>
+#include <boost/assert.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <tuple>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A container for an HTTP request or response header.
+
+ This container is derived from the `Fields` template type.
+ To understand all of the members of this class it is necessary
+ to view the declaration for the `Fields` type. When using
+ the default fields container, those declarations are in
+ @ref fields.
+
+ Newly constructed header objects have version set to
+ HTTP/1.1. Newly constructed response objects also have
+ result code set to @ref status::ok.
+
+ A `header` includes the start-line and header-fields.
+*/
+#if BOOST_BEAST_DOXYGEN
+template<bool isRequest, class Fields = fields>
+struct header : Fields
+
+#else
+template<bool isRequest, class Fields = fields>
+struct header;
+
+template<class Fields>
+struct header<true, Fields> : Fields
+#endif
+{
+ static_assert(is_fields<Fields>::value,
+ "Fields requirements not met");
+
+ /// Indicates if the header is a request or response.
+#if BOOST_BEAST_DOXYGEN
+ using is_request = std::integral_constant<bool, isRequest>;
+#else
+ using is_request = std::true_type;
+#endif
+
+ /// The type representing the fields.
+ using fields_type = Fields;
+
+ /// Constructor
+ header() = default;
+
+ /// Constructor
+ header(header&&) = default;
+
+ /// Constructor
+ header(header const&) = default;
+
+ /// Assignment
+ header& operator=(header&&) = default;
+
+ /// Assignment
+ header& operator=(header const&) = default;
+
+ /** Return the HTTP-version.
+
+ This holds both the major and minor version numbers,
+ using these formulas:
+ @code
+ unsigned major = version / 10;
+ unsigned minor = version % 10;
+ @endcode
+
+ Newly constructed headers will use HTTP/1.1 by default.
+ */
+ unsigned version() const noexcept
+ {
+ return version_;
+ }
+
+ /** Set the HTTP-version.
+
+ This holds both the major and minor version numbers,
+ using these formulas:
+ @code
+ unsigned major = version / 10;
+ unsigned minor = version % 10;
+ @endcode
+
+ Newly constructed headers will use HTTP/1.1 by default.
+
+ @param value The version number to use
+ */
+ void version(unsigned value) noexcept
+ {
+ BOOST_ASSERT(value > 0 && value < 100);
+ version_ = value;
+ }
+
+ /** Return the request-method verb.
+
+ If the request-method is not one of the recognized verbs,
+ @ref verb::unknown is returned. Callers may use @ref method_string
+ to retrieve the exact text.
+
+ @note This function is only available when `isRequest == true`.
+
+ @see @ref method_string
+ */
+ verb
+ method() const;
+
+ /** Set the request-method.
+
+ This function will set the method for requests to a known verb.
+
+ @param v The request method verb to set.
+ This may not be @ref verb::unknown.
+
+ @throws std::invalid_argument when `v == verb::unknown`.
+
+ @note This function is only available when `isRequest == true`.
+ */
+ void
+ method(verb v);
+
+ /** Return the request-method as a string.
+
+ @note This function is only available when `isRequest == true`.
+
+ @see @ref method
+ */
+ string_view
+ method_string() const;
+
+ /** Set the request-method.
+
+ This function will set the request-method a known verb
+ if the string matches, otherwise it will store a copy of
+ the passed string.
+
+ @param s A string representing the request-method.
+
+ @note This function is only available when `isRequest == true`.
+ */
+ void
+ method_string(string_view s);
+
+ /** Returns the request-target string.
+
+ The request target string returned is the same string which
+ was received from the network or stored. In particular, it will
+ contain url-encoded characters and should follow the syntax
+ rules for URIs used with HTTP.
+
+ @note This function is only available when `isRequest == true`.
+ */
+ string_view
+ target() const;
+
+ /** Set the request-target string.
+
+ It is the caller's responsibility to ensure that the request
+ target string follows the syntax rules for URIs used with
+ HTTP. In particular, reserved or special characters must be
+ url-encoded. The implementation does not perform syntax checking
+ on the passed string.
+
+ @param s A string representing the request-target.
+
+ @note This function is only available when `isRequest == true`.
+ */
+ void
+ target(string_view s);
+
+ // VFALCO Don't rearrange these declarations or
+ // ifdefs, or else the documentation will break.
+
+ /** Constructor
+
+ @param args Arguments forwarded to the `Fields`
+ base class constructor.
+
+ @note This constructor participates in overload
+ resolution if and only if the first parameter is
+ not convertible to @ref header, @ref verb, or
+ @ref status.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class... Args>
+ explicit
+ header(Args&&... args);
+
+#else
+ template<class Arg1, class... ArgN,
+ class = typename std::enable_if<
+ ! std::is_convertible<typename
+ std::decay<Arg1>::type, header>::value &&
+ ! std::is_convertible<typename
+ std::decay<Arg1>::type, verb>::value &&
+ ! std::is_convertible<typename
+ std::decay<Arg1>::type, header>::value
+ >::type>
+ explicit
+ header(Arg1&& arg1, ArgN&&... argn);
+
+private:
+ template<bool, class, class>
+ friend struct message;
+
+ template<class T>
+ friend
+ void
+ swap(header<true, T>& m1, header<true, T>& m2);
+
+ template<class... FieldsArgs>
+ header(
+ verb method,
+ string_view target_,
+ unsigned version_value,
+ FieldsArgs&&... fields_args)
+ : Fields(std::forward<FieldsArgs>(fields_args)...)
+ , method_(method)
+ {
+ version(version_value);
+ target(target_);
+ }
+
+ unsigned version_ = 11;
+ verb method_ = verb::unknown;
+};
+
+/** A container for an HTTP request or response header.
+
+ A `header` includes the start-line and header-fields.
+*/
+template<class Fields>
+struct header<false, Fields> : Fields
+{
+ static_assert(is_fields<Fields>::value,
+ "Fields requirements not met");
+
+ /// Indicates if the header is a request or response.
+ using is_request = std::false_type;
+
+ /// The type representing the fields.
+ using fields_type = Fields;
+
+ /// Constructor.
+ header() = default;
+
+ /// Constructor
+ header(header&&) = default;
+
+ /// Constructor
+ header(header const&) = default;
+
+ /// Assignment
+ header& operator=(header&&) = default;
+
+ /// Assignment
+ header& operator=(header const&) = default;
+
+ /** Constructor
+
+ @param args Arguments forwarded to the `Fields`
+ base class constructor.
+
+ @note This constructor participates in overload
+ resolution if and only if the first parameter is
+ not convertible to @ref header, @ref verb, or
+ @ref status.
+ */
+ template<class Arg1, class... ArgN,
+ class = typename std::enable_if<
+ ! std::is_convertible<typename
+ std::decay<Arg1>::type, status>::value &&
+ ! std::is_convertible<typename
+ std::decay<Arg1>::type, header>::value
+ >::type>
+ explicit
+ header(Arg1&& arg1, ArgN&&... argn);
+
+ /** Return the HTTP-version.
+
+ This holds both the major and minor version numbers,
+ using these formulas:
+ @code
+ unsigned major = version / 10;
+ unsigned minor = version % 10;
+ @endcode
+
+ Newly constructed headers will use HTTP/1.1 by default.
+ */
+ unsigned version() const noexcept
+ {
+ return version_;
+ }
+
+ /** Set the HTTP-version.
+
+ This holds both the major and minor version numbers,
+ using these formulas:
+ @code
+ unsigned major = version / 10;
+ unsigned minor = version % 10;
+ @endcode
+
+ Newly constructed headers will use HTTP/1.1 by default.
+
+ @param value The version number to use
+ */
+ void version(unsigned value) noexcept
+ {
+ BOOST_ASSERT(value > 0 && value < 100);
+ version_ = value;
+ }
+#endif
+
+ /** The response status-code result.
+
+ If the actual status code is not a known code, this
+ function returns @ref status::unknown. Use @ref result_int
+ to return the raw status code as a number.
+
+ @note This member is only available when `isRequest == false`.
+ */
+ status
+ result() const;
+
+ /** Set the response status-code.
+
+ @param v The code to set.
+
+ @note This member is only available when `isRequest == false`.
+ */
+ void
+ result(status v);
+
+ /** Set the response status-code as an integer.
+
+ This sets the status code to the exact number passed in.
+ If the number does not correspond to one of the known
+ status codes, the function @ref result will return
+ @ref status::unknown. Use @ref result_int to obtain the
+ original raw status-code.
+
+ @param v The status-code integer to set.
+
+ @throws std::invalid_argument if `v > 999`.
+ */
+ void
+ result(unsigned v);
+
+ /** The response status-code expressed as an integer.
+
+ This returns the raw status code as an integer, even
+ when that code is not in the list of known status codes.
+
+ @note This member is only available when `isRequest == false`.
+ */
+ unsigned
+ result_int() const;
+
+ /** Return the response reason-phrase.
+
+ The reason-phrase is obsolete as of rfc7230.
+
+ @note This function is only available when `isRequest == false`.
+ */
+ string_view
+ reason() const;
+
+ /** Set the response reason-phrase (deprecated)
+
+ This function sets a custom reason-phrase to a copy of
+ the string passed in. Normally it is not necessary to set
+ the reason phrase on an outgoing response object; the
+ implementation will automatically use the standard reason
+ text for the corresponding status code.
+
+ To clear a previously set custom phrase, pass an empty
+ string. This will restore the default standard reason text
+ based on the status code used when serializing.
+
+ The reason-phrase is obsolete as of rfc7230.
+
+ @param s The string to use for the reason-phrase.
+
+ @note This function is only available when `isRequest == false`.
+ */
+ void
+ reason(string_view s);
+
+private:
+#if ! BOOST_BEAST_DOXYGEN
+ template<bool, class, class>
+ friend struct message;
+
+ template<class T>
+ friend
+ void
+ swap(header<false, T>& m1, header<false, T>& m2);
+
+ template<class... FieldsArgs>
+ header(
+ status result,
+ unsigned version_value,
+ FieldsArgs&&... fields_args)
+ : Fields(std::forward<FieldsArgs>(fields_args)...)
+ , result_(result)
+ {
+ version(version_value);
+ }
+
+ unsigned version_ = 11;
+ status result_ = status::ok;
+#endif
+};
+
+/// A typical HTTP request header
+template<class Fields = fields>
+using request_header = header<true, Fields>;
+
+/// A typical HTTP response header
+template<class Fields = fields>
+using response_header = header<false, Fields>;
+
+#if defined(BOOST_MSVC)
+// Workaround for MSVC bug with private base classes
+namespace detail {
+template<class T>
+using value_type_t = typename T::value_type;
+} // detail
+#endif
+
+/** A container for a complete HTTP message.
+
+ This container is derived from the `Fields` template type.
+ To understand all of the members of this class it is necessary
+ to view the declaration for the `Fields` type. When using
+ the default fields container, those declarations are in
+ @ref fields.
+
+ A message can be a request or response, depending on the
+ `isRequest` template argument value. Requests and responses
+ have different types; functions may be overloaded based on
+ the type if desired.
+
+ The `Body` template argument type determines the model used
+ to read or write the content body of the message.
+
+ Newly constructed messages objects have version set to
+ HTTP/1.1. Newly constructed response objects also have
+ result code set to @ref status::ok.
+
+ @tparam isRequest `true` if this represents a request,
+ or `false` if this represents a response. Some class data
+ members are conditionally present depending on this value.
+
+ @tparam Body A type meeting the requirements of Body.
+
+ @tparam Fields The type of container used to hold the
+ field value pairs.
+*/
+template<bool isRequest, class Body, class Fields = fields>
+struct message
+ : header<isRequest, Fields>
+#if ! BOOST_BEAST_DOXYGEN
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>
+#endif
+{
+ /// The base class used to hold the header portion of the message.
+ using header_type = header<isRequest, Fields>;
+
+ /** The type providing the body traits.
+
+ The @ref message::body member will be of type `body_type::value_type`.
+ */
+ using body_type = Body;
+
+ /// Constructor
+ message() = default;
+
+ /// Constructor
+ message(message&&) = default;
+
+ /// Constructor
+ message(message const&) = default;
+
+ /// Assignment
+ message& operator=(message&&) = default;
+
+ /// Assignment
+ message& operator=(message const&) = default;
+
+ /** Constructor
+
+ @param h The header to move construct from.
+
+ @param body_args Optional arguments forwarded
+ to the `body` constructor.
+ */
+ template<class... BodyArgs>
+ explicit
+ message(header_type&& h, BodyArgs&&... body_args);
+
+ /** Constructor.
+
+ @param h The header to copy construct from.
+
+ @param body_args Optional arguments forwarded
+ to the `body` constructor.
+ */
+ template<class... BodyArgs>
+ explicit
+ message(header_type const& h, BodyArgs&&... body_args);
+
+ /** Constructor
+
+ @param method The request-method to use
+
+ @param target The request-target.
+
+ @param version The HTTP-version
+
+ @note This function is only available when `isRequest == true`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ message(verb method, string_view target, unsigned version);
+#else
+ template<class Version,
+ class = typename std::enable_if<isRequest &&
+ std::is_convertible<Version, unsigned>::value>::type>
+ message(verb method, string_view target, Version version);
+#endif
+
+ /** Constructor
+
+ @param method The request-method to use
+
+ @param target The request-target.
+
+ @param version The HTTP-version
+
+ @param body_arg An argument forwarded to the `body` constructor.
+
+ @note This function is only available when `isRequest == true`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class BodyArg>
+ message(verb method, string_view target,
+ unsigned version, BodyArg&& body_arg);
+#else
+ template<class Version, class BodyArg,
+ class = typename std::enable_if<isRequest &&
+ std::is_convertible<Version, unsigned>::value>::type>
+ message(verb method, string_view target,
+ Version version, BodyArg&& body_arg);
+#endif
+
+ /** Constructor
+
+ @param method The request-method to use
+
+ @param target The request-target.
+
+ @param version The HTTP-version
+
+ @param body_arg An argument forwarded to the `body` constructor.
+
+ @param fields_arg An argument forwarded to the `Fields` constructor.
+
+ @note This function is only available when `isRequest == true`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class BodyArg, class FieldsArg>
+ message(verb method, string_view target, unsigned version,
+ BodyArg&& body_arg, FieldsArg&& fields_arg);
+#else
+ template<class Version, class BodyArg, class FieldsArg,
+ class = typename std::enable_if<isRequest &&
+ std::is_convertible<Version, unsigned>::value>::type>
+ message(verb method, string_view target, Version version,
+ BodyArg&& body_arg, FieldsArg&& fields_arg);
+#endif
+
+ /** Constructor
+
+ @param result The status-code for the response
+
+ @param version The HTTP-version
+
+ @note This member is only available when `isRequest == false`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ message(status result, unsigned version);
+#else
+ template<class Version,
+ class = typename std::enable_if<! isRequest &&
+ std::is_convertible<Version, unsigned>::value>::type>
+ message(status result, Version version);
+#endif
+
+ /** Constructor
+
+ @param result The status-code for the response
+
+ @param version The HTTP-version
+
+ @param body_arg An argument forwarded to the `body` constructor.
+
+ @note This member is only available when `isRequest == false`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class BodyArg>
+ message(status result, unsigned version, BodyArg&& body_arg);
+#else
+ template<class Version, class BodyArg,
+ class = typename std::enable_if<! isRequest &&
+ std::is_convertible<Version, unsigned>::value>::type>
+ message(status result, Version version, BodyArg&& body_arg);
+#endif
+
+ /** Constructor
+
+ @param result The status-code for the response
+
+ @param version The HTTP-version
+
+ @param body_arg An argument forwarded to the `body` constructor.
+
+ @param fields_arg An argument forwarded to the `Fields` base class constructor.
+
+ @note This member is only available when `isRequest == false`.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class BodyArg, class FieldsArg>
+ message(status result, unsigned version,
+ BodyArg&& body_arg, FieldsArg&& fields_arg);
+#else
+ template<class Version, class BodyArg, class FieldsArg,
+ class = typename std::enable_if<! isRequest &&
+ std::is_convertible<Version, unsigned>::value>::type>
+ message(status result, Version version,
+ BodyArg&& body_arg, FieldsArg&& fields_arg);
+#endif
+
+ /** Constructor
+
+ The header and body are default-constructed.
+ */
+ explicit
+ message(std::piecewise_construct_t);
+
+ /** Construct a message.
+
+ @param body_args A tuple forwarded as a parameter
+ pack to the body constructor.
+ */
+ template<class... BodyArgs>
+ message(std::piecewise_construct_t,
+ std::tuple<BodyArgs...> body_args);
+
+ /** Construct a message.
+
+ @param body_args A tuple forwarded as a parameter
+ pack to the body constructor.
+
+ @param fields_args A tuple forwarded as a parameter
+ pack to the `Fields` constructor.
+ */
+ template<class... BodyArgs, class... FieldsArgs>
+ message(std::piecewise_construct_t,
+ std::tuple<BodyArgs...> body_args,
+ std::tuple<FieldsArgs...> fields_args);
+
+ /// Returns the header portion of the message
+ header_type const&
+ base() const
+ {
+ return *this;
+ }
+
+ /// Returns the header portion of the message
+ header_type&
+ base()
+ {
+ return *this;
+ }
+
+ /// Returns `true` if the chunked Transfer-Encoding is specified
+ bool
+ chunked() const
+ {
+ return this->get_chunked_impl();
+ }
+
+ /** Set or clear the chunked Transfer-Encoding
+
+ This function will set or removed the "chunked" transfer
+ encoding as the last item in the list of encodings in the
+ field.
+
+ If the result of removing the chunked token results in an
+ empty string, the field is erased.
+
+ The Content-Length field is erased unconditionally.
+ */
+ void
+ chunked(bool value);
+
+ /** Returns `true` if the Content-Length field is present.
+
+ This function inspects the fields and returns `true` if
+ the Content-Length field is present. The properties of the
+ body are not checked, this only looks for the field.
+ */
+ bool
+ has_content_length() const
+ {
+ return this->has_content_length_impl();
+ }
+
+ /** Set or clear the Content-Length field
+
+ This function adjusts the Content-Length field as follows:
+
+ @li If `value` specifies a value, the Content-Length field
+ is set to the value. Otherwise
+
+ @li The Content-Length field is erased.
+
+ If "chunked" token appears as the last item in the
+ Transfer-Encoding field it is unconditionally removed.
+
+ @param value The value to set for Content-Length.
+ */
+ void
+ content_length(boost::optional<std::uint64_t> const& value);
+
+ /** Returns `true` if the message semantics indicate keep-alive
+
+ The value depends on the version in the message, which must
+ be set to the final value before this function is called or
+ else the return value is unreliable.
+ */
+ bool
+ keep_alive() const
+ {
+ return this->get_keep_alive_impl(this->version());
+ }
+
+ /** Set the keep-alive message semantic option
+
+ This function adjusts the Connection field to indicate
+ whether or not the connection should be kept open after
+ the corresponding response. The result depends on the
+ version set on the message, which must be set to the
+ final value before making this call.
+
+ @param value `true` if the connection should persist.
+ */
+ void
+ keep_alive(bool value)
+ {
+ this->set_keep_alive_impl(this->version(), value);
+ }
+
+ /** Returns `true` if the message semantics require an end of file.
+
+ For HTTP requests, this function returns the logical
+ NOT of a call to @ref keep_alive.
+
+ For HTTP responses, this function returns the logical NOT
+ of a call to @ref keep_alive if any of the following are true:
+
+ @li @ref has_content_length would return `true`
+
+ @li @ref chunked would return `true`
+
+ @li @ref result returns @ref status::no_content
+
+ @li @ref result returns @ref status::not_modified
+
+ @li @ref result returns any informational status class (100 to 199)
+
+ Otherwise, the function returns `true`.
+
+ @see https://tools.ietf.org/html/rfc7230#section-3.3
+ */
+ bool
+ need_eof() const
+ {
+ return need_eof(typename header_type::is_request{});
+ }
+
+ /** Returns the payload size of the body in octets if possible.
+
+ This function invokes the @b Body algorithm to measure
+ the number of octets in the serialized body container. If
+ there is no body, this will return zero. Otherwise, if the
+ body exists but is not known ahead of time, `boost::none`
+ is returned (usually indicating that a chunked Transfer-Encoding
+ will be used).
+
+ @note The value of the Content-Length field in the message
+ is not inspected.
+ */
+ boost::optional<std::uint64_t>
+ payload_size() const;
+
+ /** Prepare the message payload fields for the body.
+
+ This function will adjust the Content-Length and
+ Transfer-Encoding field values based on the properties
+ of the body.
+
+ @par Example
+ @code
+ request<string_body> req{verb::post, "/"};
+ req.set(field::user_agent, "Beast");
+ req.body() = "Hello, world!";
+ req.prepare_payload();
+ @endcode
+ */
+ void
+ prepare_payload()
+ {
+ prepare_payload(typename header_type::is_request{});
+ }
+
+ /// Returns the body
+#if BOOST_BEAST_DOXYGEN || ! defined(BOOST_MSVC)
+ typename body_type::value_type&
+#else
+ detail::value_type_t<Body>&
+#endif
+ body()& noexcept
+ {
+ return this->member();
+ }
+
+ /// Returns the body
+#if BOOST_BEAST_DOXYGEN || ! defined(BOOST_MSVC)
+ typename body_type::value_type&&
+#else
+ detail::value_type_t<Body>&&
+#endif
+ body()&& noexcept
+ {
+ return std::move(this->member());
+ }
+
+ /// Returns the body
+#if BOOST_BEAST_DOXYGEN || ! defined(BOOST_MSVC)
+ typename body_type::value_type const&
+#else
+ detail::value_type_t<Body> const&
+#endif
+ body() const& noexcept
+ {
+ return this->member();
+ }
+
+private:
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+
+ template<
+ class... BodyArgs,
+ std::size_t... IBodyArgs>
+ message(
+ std::piecewise_construct_t,
+ std::tuple<BodyArgs...>& body_args,
+ beast::detail::index_sequence<IBodyArgs...>)
+ : beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArgs>(
+ std::get<IBodyArgs>(body_args))...)
+ {
+ boost::ignore_unused(body_args);
+ }
+
+ template<
+ class... BodyArgs,
+ class... FieldsArgs,
+ std::size_t... IBodyArgs,
+ std::size_t... IFieldsArgs>
+ message(
+ std::piecewise_construct_t,
+ std::tuple<BodyArgs...>& body_args,
+ std::tuple<FieldsArgs...>& fields_args,
+ beast::detail::index_sequence<IBodyArgs...>,
+ beast::detail::index_sequence<IFieldsArgs...>)
+ : header_type(std::forward<FieldsArgs>(
+ std::get<IFieldsArgs>(fields_args))...)
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArgs>(
+ std::get<IBodyArgs>(body_args))...)
+ {
+ boost::ignore_unused(body_args);
+ boost::ignore_unused(fields_args);
+ }
+
+ bool
+ need_eof(std::true_type) const
+ {
+ return ! keep_alive();
+ }
+
+ bool
+ need_eof(std::false_type) const;
+
+ boost::optional<std::uint64_t>
+ payload_size(std::true_type) const
+ {
+ return Body::size(this->body());
+ }
+
+ boost::optional<std::uint64_t>
+ payload_size(std::false_type) const
+ {
+ return boost::none;
+ }
+
+ void
+ prepare_payload(std::true_type);
+
+ void
+ prepare_payload(std::false_type);
+};
+
+/// A typical HTTP request
+template<class Body, class Fields = fields>
+using request = message<true, Body, Fields>;
+
+/// A typical HTTP response
+template<class Body, class Fields = fields>
+using response = message<false, Body, Fields>;
+
+//------------------------------------------------------------------------------
+
+#if BOOST_BEAST_DOXYGEN
+/** Swap two header objects.
+
+ @par Requirements
+ `Fields` is @b Swappable.
+*/
+template<bool isRequest, class Fields>
+void
+swap(
+ header<isRequest, Fields>& m1,
+ header<isRequest, Fields>& m2);
+#endif
+
+/** Swap two message objects.
+
+ @par Requirements:
+ `Body::value_type` and `Fields` are @b Swappable.
+*/
+template<bool isRequest, class Body, class Fields>
+void
+swap(
+ message<isRequest, Body, Fields>& m1,
+ message<isRequest, Body, Fields>& m2);
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/message.ipp>
+
+#endif
diff --git a/boost/beast/http/parser.hpp b/boost/beast/http/parser.hpp
new file mode 100644
index 0000000000..47e1003533
--- /dev/null
+++ b/boost/beast/http/parser.hpp
@@ -0,0 +1,436 @@
+//
+// 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_HTTP_PARSER_HPP
+#define BOOST_BEAST_HTTP_PARSER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/basic_parser.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** An HTTP/1 parser for producing a message.
+
+ This class uses the basic HTTP/1 wire format parser to convert
+ a series of octets into a @ref message using the @ref basic_fields
+ container to represent the fields.
+
+ @tparam isRequest Indicates whether a request or response
+ will be parsed.
+
+ @tparam Body The type used to represent the body. This must
+ meet the requirements of @b Body.
+
+ @tparam Allocator The type of allocator used with the
+ @ref basic_fields container.
+
+ @note A new instance of the parser is required for each message.
+*/
+template<
+ bool isRequest,
+ class Body,
+ class Allocator = std::allocator<char>>
+class parser
+ : public basic_parser<isRequest,
+ parser<isRequest, Body, Allocator>>
+{
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+
+ static_assert(is_body_reader<Body>::value,
+ "BodyReader requirements not met");
+
+ template<bool, class, class>
+ friend class parser;
+
+ using base_type = basic_parser<isRequest,
+ parser<isRequest, Body, Allocator>>;
+
+ message<isRequest, Body, basic_fields<Allocator>> m_;
+ typename Body::reader wr_;
+ bool rd_inited_ = false;
+
+ std::function<void(
+ std::uint64_t,
+ string_view,
+ error_code&)> cb_h_;
+
+ std::function<std::size_t(
+ std::uint64_t,
+ string_view,
+ error_code&)> cb_b_;
+
+public:
+ /// The type of message returned by the parser
+ using value_type =
+ message<isRequest, Body, basic_fields<Allocator>>;
+
+ /// Destructor
+ ~parser() = default;
+
+ /// Constructor
+ parser();
+
+ /// Constructor
+ parser(parser const&) = delete;
+
+ /// Assignment
+ parser& operator=(parser const&) = delete;
+
+ /** Constructor
+
+ After the move, the only valid operation
+ on the moved-from object is destruction.
+ */
+ parser(parser&& other) = default;
+
+ /** Constructor
+
+ @param args Optional arguments forwarded to the
+ @ref http::header constructor.
+
+ @note This function participates in overload
+ resolution only if the first argument is not a
+ @ref parser.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class... Args>
+ explicit
+ parser(Args&&... args);
+#else
+ template<class Arg1, class... ArgN,
+ class = typename std::enable_if<
+ ! detail::is_parser<typename
+ std::decay<Arg1>::type>::value>::type>
+ explicit
+ parser(Arg1&& arg1, ArgN&&... argn);
+#endif
+
+ /** Construct a parser from another parser, changing the Body type.
+
+ This constructs a new parser by move constructing the
+ header from another parser with a different body type. The
+ constructed-from parser must not have any parsed body octets or
+ initialized @b BodyReader, otherwise an exception is generated.
+
+ @par Example
+ @code
+ // Deferred body type commitment
+ request_parser<empty_body> req0;
+ ...
+ request_parser<string_body> req{std::move(req0)};
+ @endcode
+
+ If an exception is thrown, the state of the constructed-from
+ parser is undefined.
+
+ @param parser The other parser to construct from. After
+ this call returns, the constructed-from parser may only
+ be destroyed.
+
+ @param args Optional arguments forwarded to the message
+ constructor.
+
+ @throws std::invalid_argument Thrown when the constructed-from
+ parser has already initialized a body reader.
+
+ @note This function participates in overload resolution only
+ if the other parser uses a different body type.
+ */
+#if BOOST_BEAST_DOXYGEN
+ template<class OtherBody, class... Args>
+#else
+ template<class OtherBody, class... Args,
+ class = typename std::enable_if<
+ ! std::is_same<Body, OtherBody>::value>::type>
+#endif
+ explicit
+ parser(parser<isRequest, OtherBody,
+ Allocator>&& parser, Args&&... args);
+
+ /** Returns the parsed message.
+
+ Depending on the parser's progress,
+ parts of this object may be incomplete.
+ */
+ value_type const&
+ get() const
+ {
+ return m_;
+ }
+
+ /** Returns the parsed message.
+
+ Depending on the parser's progress,
+ parts of this object may be incomplete.
+ */
+ value_type&
+ get()
+ {
+ return m_;
+ }
+
+ /** Returns ownership of the parsed message.
+
+ Ownership is transferred to the caller.
+ Depending on the parser's progress,
+ parts of this object may be incomplete.
+
+ @par Requires
+
+ @ref value_type is @b MoveConstructible
+ */
+ value_type
+ release()
+ {
+ static_assert(std::is_move_constructible<decltype(m_)>::value,
+ "MoveConstructible requirements not met");
+ return std::move(m_);
+ }
+
+ /** Set a callback to be invoked on each chunk header.
+
+ The callback will be invoked once for every chunk in the message
+ payload, as well as once for the last chunk. The invocation
+ happens after the chunk header is available but before any body
+ octets have been parsed.
+
+ The extensions are provided in raw, validated form, use
+ @ref chunk_extensions::parse to parse the extensions into a
+ structured container for easier access.
+ The implementation type-erases the callback without requiring
+ a dynamic allocation. For this reason, the callback object is
+ passed by a non-constant reference.
+
+ @par Example
+ @code
+ auto callback =
+ [](std::uint64_t size, string_view extensions, error_code& ec)
+ {
+ //...
+ };
+ parser.on_chunk_header(callback);
+ @endcode
+
+ @param cb The function to set, which must be invocable with
+ this equivalent signature:
+ @code
+ void
+ on_chunk_header(
+ std::uint64_t size, // Size of the chunk, zero for the last chunk
+ string_view extensions, // The chunk-extensions in raw form
+ error_code& ec); // May be set by the callback to indicate an error
+ @endcode
+ */
+ template<class Callback>
+ void
+ on_chunk_header(Callback& cb)
+ {
+ // Callback may not be constant, caller is responsible for
+ // managing the lifetime of the callback. Copies are not made.
+ BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
+
+ // Can't set the callback after receiving any chunk data!
+ BOOST_ASSERT(! rd_inited_);
+
+ cb_h_ = std::ref(cb);
+ }
+
+ /** Set a callback to be invoked on chunk body data
+
+ The provided function object will be invoked one or more times
+ to provide buffers corresponding to the chunk body for the current
+ chunk. The callback receives the number of octets remaining in this
+ chunk body including the octets in the buffer provided.
+
+ The callback must return the number of octets actually consumed.
+ Any octets not consumed will be presented again in a subsequent
+ invocation of the callback.
+ The implementation type-erases the callback without requiring
+ a dynamic allocation. For this reason, the callback object is
+ passed by a non-constant reference.
+
+ @par Example
+ @code
+ auto callback =
+ [](std::uint64_t remain, string_view body, error_code& ec)
+ {
+ //...
+ };
+ parser.on_chunk_body(callback);
+ @endcode
+
+ @param cb The function to set, which must be invocable with
+ this equivalent signature:
+ @code
+ std::size_t
+ on_chunk_header(
+ std::uint64_t remain, // Octets remaining in this chunk, includes `body`
+ string_view body, // A buffer holding some or all of the remainder of the chunk body
+ error_code& ec); // May be set by the callback to indicate an error
+ @endcode
+ */
+ template<class Callback>
+ void
+ on_chunk_body(Callback& cb)
+ {
+ // Callback may not be constant, caller is responsible for
+ // managing the lifetime of the callback. Copies are not made.
+ BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
+
+ // Can't set the callback after receiving any chunk data!
+ BOOST_ASSERT(! rd_inited_);
+
+ cb_b_ = std::ref(cb);
+ }
+
+private:
+ friend class basic_parser<isRequest, parser>;
+
+ void
+ on_request_impl(
+ verb method,
+ string_view method_str,
+ string_view target,
+ int version,
+ error_code& ec)
+ {
+ try
+ {
+ m_.target(target);
+ if(method != verb::unknown)
+ m_.method(method);
+ else
+ m_.method_string(method_str);
+ ec.assign(0, ec.category());
+ }
+ catch(std::bad_alloc const&)
+ {
+ ec = error::bad_alloc;
+ }
+ m_.version(version);
+ }
+
+ void
+ on_response_impl(
+ int code,
+ string_view reason,
+ int version,
+ error_code& ec)
+ {
+ m_.result(code);
+ m_.version(version);
+ try
+ {
+ m_.reason(reason);
+ ec.assign(0, ec.category());
+ }
+ catch(std::bad_alloc const&)
+ {
+ ec = error::bad_alloc;
+ }
+ }
+
+ void
+ on_field_impl(
+ field name,
+ string_view name_string,
+ string_view value,
+ error_code& ec)
+ {
+ try
+ {
+ m_.insert(name, name_string, value);
+ ec.assign(0, ec.category());
+ }
+ catch(std::bad_alloc const&)
+ {
+ ec = error::bad_alloc;
+ }
+ }
+
+ void
+ on_header_impl(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ void
+ on_body_init_impl(
+ boost::optional<std::uint64_t> const& content_length,
+ error_code& ec)
+ {
+ wr_.init(content_length, ec);
+ rd_inited_ = true;
+ }
+
+ std::size_t
+ on_body_impl(
+ string_view body,
+ error_code& ec)
+ {
+ return wr_.put(boost::asio::buffer(
+ body.data(), body.size()), ec);
+ }
+
+ void
+ on_chunk_header_impl(
+ std::uint64_t size,
+ string_view extensions,
+ error_code& ec)
+ {
+ if(cb_h_)
+ return cb_h_(size, extensions, ec);
+ ec.assign(0, ec.category());
+ }
+
+ std::size_t
+ on_chunk_body_impl(
+ std::uint64_t remain,
+ string_view body,
+ error_code& ec)
+ {
+ if(cb_b_)
+ return cb_b_(remain, body, ec);
+ return wr_.put(boost::asio::buffer(
+ body.data(), body.size()), ec);
+ }
+
+ void
+ on_finish_impl(error_code& ec)
+ {
+ wr_.finish(ec);
+ }
+};
+
+/// An HTTP/1 parser for producing a request message.
+template<class Body, class Allocator = std::allocator<char>>
+using request_parser = parser<true, Body, Allocator>;
+
+/// An HTTP/1 parser for producing a response message.
+template<class Body, class Allocator = std::allocator<char>>
+using response_parser = parser<false, Body, Allocator>;
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/parser.ipp>
+
+#endif
diff --git a/boost/beast/http/read.hpp b/boost/beast/http/read.hpp
new file mode 100644
index 0000000000..4f159aec2b
--- /dev/null
+++ b/boost/beast/http/read.hpp
@@ -0,0 +1,765 @@
+//
+// 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_HTTP_READ_HPP
+#define BOOST_BEAST_HTTP_READ_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/http/basic_parser.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/asio/async_result.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** Read part of a message from a stream using a parser.
+
+ This function is used to read part of a message from a stream into a
+ subclass of @ref basic_parser.
+ The call will block until one of the following conditions is true:
+
+ @li A call to @ref basic_parser::put with a non-empty buffer sequence
+ is successful.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or
+ more calls to the stream's `read_some` function.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b SyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+
+ @return The number of bytes transferred to the parser.
+
+ @throws system_error Thrown on failure.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_some(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser);
+
+/** Read part of a message from a stream using a parser.
+
+ This function is used to read part of a message from a stream into a
+ subclass of @ref basic_parser.
+ The call will block until one of the following conditions is true:
+
+ @li A call to @ref basic_parser::put with a non-empty buffer sequence
+ is successful.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or
+ more calls to the stream's `read_some` function.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ The function returns the number of bytes processed from the dynamic
+ buffer. The caller should remove these bytes by calling `consume` on
+ the dynamic buffer, regardless of any error.
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b SyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of bytes transferred to the parser.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_some(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec);
+
+/** Read part of a message asynchronously from a stream using a parser.
+
+ This function is used to asynchronously read part of a message from
+ a stream into a subclass of @ref basic_parser.
+ The function call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
+
+ @li A call to @ref basic_parser::put with a non-empty buffer sequence
+ is successful.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of zero or more calls to
+ the next layer's `async_read_some` function, and is known as a
+ <em>composed operation</em>. The program must ensure that the
+ stream performs no other reads until this operation completes.
+ The implementation may read additional octets that lie past the
+ end of the object being parsed. This additional data is stored
+ in the stream buffer, which may be used in subsequent calls.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b AsyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+ The object must remain valid at least until the
+ handler is called; ownership is not transferred.
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // the number of bytes transferred to the parser
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+
+ The completion handler will receive as a parameter the number
+ of octets processed from the dynamic buffer. The octets should
+ be removed by calling `consume` on the dynamic buffer after
+ the read completes, regardless of any error.
+*/
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read_some(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler);
+
+//------------------------------------------------------------------------------
+
+/** Read a header from a stream using a parser.
+
+ This function is used to read a header from a stream into a subclass
+ of @ref basic_parser.
+ The call will block until one of the following conditions is true:
+
+ @li @ref basic_parser::is_header_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or
+ more calls to the stream's `read_some` function.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b SyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+
+ @return The number of bytes transferred to the parser.
+
+ @throws system_error Thrown on failure.
+
+ @note The implementation will call @ref basic_parser::eager
+ with the value `false` on the parser passed in.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_header(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser);
+
+/** Read a header from a stream using a parser.
+
+ This function is used to read a header from a stream into a subclass
+ of @ref basic_parser.
+ The call will block until one of the following conditions is true:
+
+ @li @ref basic_parser::is_header_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or
+ more calls to the stream's `read_some` function.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b SyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of bytes transferred to the parser.
+
+ @note The implementation will call @ref basic_parser::eager
+ with the value `false` on the parser passed in.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_header(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec);
+
+/** Read a header from a stream asynchronously using a parser.
+
+ This function is used to asynchronously read a header from a stream
+ into a subclass of @ref basic_parser.
+ The function call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
+
+ @li @ref basic_parser::is_header_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls to
+ the stream's `async_read_some` function, and is known as a
+ <em>composed operation</em>. The program must ensure that the
+ stream performs no other reads until this operation completes.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b AsyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+ The object must remain valid at least until the
+ handler is called; ownership is not transferred.
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation,
+ std::size_t bytes_transferred // the number of bytes transferred to the parser
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+
+ @note The implementation will call @ref basic_parser::eager
+ with the value `false` on the parser passed in.
+*/
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read_header(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler);
+
+//------------------------------------------------------------------------------
+
+/** Read a complete message from a stream using a parser.
+
+ This function is used to read a complete message from a stream into a
+ subclass of @ref basic_parser.
+ The call will block until one of the following conditions is true:
+
+ @li @ref basic_parser::is_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or
+ more calls to the stream's `read_some` function.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b SyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+
+ @return The number of bytes transferred to the parser.
+
+ @throws system_error Thrown on failure.
+
+ @note The implementation will call @ref basic_parser::eager
+ with the value `true` on the parser passed in.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser);
+
+/** Read a complete message from a stream using a parser.
+
+ This function is used to read a complete message from a stream into a
+ subclass of @ref basic_parser.
+ The call will block until one of the following conditions is true:
+
+ @li @ref basic_parser::is_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or
+ more calls to the stream's `read_some` function.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b SyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of bytes transferred to the parser.
+
+ @note The implementation will call @ref basic_parser::eager
+ with the value `true` on the parser passed in.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec);
+
+/** Read a complete message from a stream asynchronously using a parser.
+
+ This function is used to asynchronously read a complete message from a
+ stream into a subclass of @ref basic_parser.
+ The function call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
+
+ @li @ref basic_parser::is_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls to
+ the stream's `async_read_some` function, and is known as a
+ <em>composed operation</em>. The program must ensure that the
+ stream performs no other reads until this operation completes.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b AsyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param parser The parser to use.
+ The object must remain valid at least until the
+ handler is called; ownership is not transferred.
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation,
+ std::size_t bytes_transferred // the number of bytes transferred to the parser
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+
+ @note The implementation will call @ref basic_parser::eager
+ with the value `true` on the parser passed in.
+*/
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler);
+
+//------------------------------------------------------------------------------
+
+/** Read a complete message from a stream.
+
+ This function is used to read a complete message from a stream using HTTP/1.
+ The call will block until one of the following conditions is true:
+
+ @li The entire message is read.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or
+ more calls to the stream's `read_some` function.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b SyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param msg An object in which to store the message contents.
+ This object should not have previous contents, otherwise
+ the behavior is undefined.
+ The type must be @b MoveAssignable and @b MoveConstructible.
+
+ @return The number of bytes transferred to the parser.
+
+ @throws system_error Thrown on failure.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Body, class Allocator>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ message<isRequest, Body, basic_fields<Allocator>>& msg);
+
+/** Read a complete message from a stream.
+
+ This function is used to read a complete message from a stream using HTTP/1.
+ The call will block until one of the following conditions is true:
+
+ @li The entire message is read.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or
+ more calls to the stream's `read_some` function.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b SyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param msg An object in which to store the message contents.
+ This object should not have previous contents, otherwise
+ the behavior is undefined.
+ The type must be @b MoveAssignable and @b MoveConstructible.
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of bytes transferred to the parser.
+*/
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Body, class Allocator>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ message<isRequest, Body, basic_fields<Allocator>>& msg,
+ error_code& ec);
+
+/** Read a complete message from a stream asynchronously.
+
+ This function is used to asynchronously read a complete message from a
+ stream using HTTP/1.
+ The function call always returns immediately. The asynchronous operation
+ will continue until one of the following conditions is true:
+
+ @li The entire message is read.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls to
+ the stream's `async_read_some` function, and is known as a
+ <em>composed operation</em>. The program must ensure that the
+ stream performs no other reads until this operation completes.
+ The implementation may read additional octets that lie past the
+ end of the message being read. This additional data is stored
+ in the dynamic buffer, which must be retained for subsequent reads.
+
+ If the stream returns the error `boost::asio::error::eof` indicating the
+ end of file during a read, the error returned from this function will be:
+
+ @li @ref error::end_of_stream if no octets were parsed, or
+
+ @li @ref error::partial_message if any octets were parsed but the
+ message was incomplete, otherwise:
+
+ @li A successful result. A subsequent attempt to read will
+ return @ref error::end_of_stream
+
+ @param stream The stream from which the data is to be read.
+ The type must support the @b AsyncReadStream concept.
+
+ @param buffer A @b DynamicBuffer holding additional bytes
+ read by the implementation from the stream. This is both
+ an input and an output parameter; on entry, any data in the
+ dynamic buffer's input sequence will be given to the parser
+ first.
+
+ @param msg An object in which to store the message contents.
+ This object should not have previous contents, otherwise
+ the behavior is undefined.
+ The type must be @b MoveAssignable and @b MoveConstructible.
+
+ The object must remain valid at least until the
+ handler is called; ownership is not transferred.
+
+ @param handler The handler to be called when the operation
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation,
+ std::size_t bytes_transferred // the number of bytes transferred to the parser
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+*/
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Body, class Allocator,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ message<isRequest, Body, basic_fields<Allocator>>& msg,
+ ReadHandler&& handler);
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/read.ipp>
+
+#endif
diff --git a/boost/beast/http/rfc7230.hpp b/boost/beast/http/rfc7230.hpp
new file mode 100644
index 0000000000..952cb929d6
--- /dev/null
+++ b/boost/beast/http/rfc7230.hpp
@@ -0,0 +1,329 @@
+//
+// 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_HTTP_RFC7230_HPP
+#define BOOST_BEAST_HTTP_RFC7230_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/detail/rfc7230.hpp>
+#include <boost/beast/http/detail/basic_parsed_list.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A list of parameters in an HTTP extension field value.
+
+ This container allows iteration of the parameter list in an HTTP
+ extension. The parameter list is a series of name/value pairs
+ with each pair starting with a semicolon. The value is optional.
+
+ If a parsing error is encountered while iterating the string,
+ the behavior of the container will be as if a string containing
+ only characters up to but excluding the first invalid character
+ was used to construct the list.
+
+ @par BNF
+ @code
+ param-list = *( OWS ";" OWS param )
+ param = token OWS [ "=" OWS ( token / quoted-string ) ]
+ @endcode
+
+ To use this class, construct with the string to be parsed and
+ then use @ref begin and @ref end, or range-for to iterate each
+ item:
+
+ @par Example
+ @code
+ for(auto const& param : param_list{";level=9;no_context_takeover;bits=15"})
+ {
+ std::cout << ";" << param.first;
+ if(! param.second.empty())
+ std::cout << "=" << param.second;
+ std::cout << "\n";
+ }
+ @endcode
+*/
+class param_list
+{
+ string_view s_;
+
+public:
+ /** The type of each element in the list.
+
+ The first string in the pair is the name of the parameter,
+ and the second string in the pair is its value (which may
+ be empty).
+ */
+ using value_type =
+ std::pair<string_view, string_view>;
+
+ /// A constant iterator to the list
+#if BOOST_BEAST_DOXYGEN
+ using const_iterator = implementation_defined;
+#else
+ class const_iterator;
+#endif
+
+ /// Default constructor.
+ param_list() = default;
+
+ /** Construct a list.
+
+ @param s A string containing the list contents. The string
+ must remain valid for the lifetime of the container.
+ */
+ explicit
+ param_list(string_view s)
+ : s_(s)
+ {
+ }
+
+ /// Return a const iterator to the beginning of the list
+ const_iterator begin() const;
+
+ /// Return a const iterator to the end of the list
+ const_iterator end() const;
+
+ /// Return a const iterator to the beginning of the list
+ const_iterator cbegin() const;
+
+ /// Return a const iterator to the end of the list
+ const_iterator cend() const;
+};
+
+//------------------------------------------------------------------------------
+
+/** A list of extensions in a comma separated HTTP field value.
+
+ This container allows iteration of the extensions in an HTTP
+ field value. The extension list is a comma separated list of
+ token parameter list pairs.
+
+ If a parsing error is encountered while iterating the string,
+ the behavior of the container will be as if a string containing
+ only characters up to but excluding the first invalid character
+ was used to construct the list.
+
+ @par BNF
+ @code
+ ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
+ ext = token param-list
+ param-list = *( OWS ";" OWS param )
+ param = token OWS [ "=" OWS ( token / quoted-string ) ]
+ @endcode
+
+ To use this class, construct with the string to be parsed and
+ then use @ref begin and @ref end, or range-for to iterate each
+ item:
+
+ @par Example
+ @code
+ for(auto const& ext : ext_list{"none, 7z;level=9, zip;no_context_takeover;bits=15"})
+ {
+ std::cout << ext.first << "\n";
+ for(auto const& param : ext.second)
+ {
+ std::cout << ";" << param.first;
+ if(! param.second.empty())
+ std::cout << "=" << param.second;
+ std::cout << "\n";
+ }
+ }
+ @endcode
+*/
+class ext_list
+{
+ using iter_type = string_view::const_iterator;
+
+ string_view s_;
+
+public:
+ /** The type of each element in the list.
+
+ The first element of the pair is the extension token, and the
+ second element of the pair is an iterable container holding the
+ extension's name/value parameters.
+ */
+ using value_type = std::pair<string_view, param_list>;
+
+ /// A constant iterator to the list
+#if BOOST_BEAST_DOXYGEN
+ using const_iterator = implementation_defined;
+#else
+ class const_iterator;
+#endif
+
+ /** Construct a list.
+
+ @param s A string containing the list contents. The string
+ must remain valid for the lifetime of the container.
+ */
+ explicit
+ ext_list(string_view s)
+ : s_(s)
+ {
+ }
+
+ /// Return a const iterator to the beginning of the list
+ const_iterator begin() const;
+
+ /// Return a const iterator to the end of the list
+ const_iterator end() const;
+
+ /// Return a const iterator to the beginning of the list
+ const_iterator cbegin() const;
+
+ /// Return a const iterator to the end of the list
+ const_iterator cend() const;
+
+ /** Find a token in the list.
+
+ @param s The token to find. A case-insensitive comparison is used.
+
+ @return An iterator to the matching token, or `end()` if no
+ token exists.
+ */
+ template<class T>
+ const_iterator
+ find(T const& s);
+
+ /** Return `true` if a token is present in the list.
+
+ @param s The token to find. A case-insensitive comparison is used.
+ */
+ template<class T>
+ bool
+ exists(T const& s);
+};
+
+//------------------------------------------------------------------------------
+
+/** A list of tokens in a comma separated HTTP field value.
+
+ This container allows iteration of a list of items in a
+ header field value. The input is a comma separated list of
+ tokens.
+
+ If a parsing error is encountered while iterating the string,
+ the behavior of the container will be as if a string containing
+ only characters up to but excluding the first invalid character
+ was used to construct the list.
+
+ @par BNF
+ @code
+ token-list = *( "," OWS ) token *( OWS "," [ OWS token ] )
+ @endcode
+
+ To use this class, construct with the string to be parsed and
+ then use @ref begin and @ref end, or range-for to iterate each
+ item:
+
+ @par Example
+ @code
+ for(auto const& token : token_list{"apple, pear, banana"})
+ std::cout << token << "\n";
+ @endcode
+*/
+class token_list
+{
+ using iter_type = string_view::const_iterator;
+
+ string_view s_;
+
+public:
+ /// The type of each element in the token list.
+ using value_type = string_view;
+
+ /// A constant iterator to the list
+#if BOOST_BEAST_DOXYGEN
+ using const_iterator = implementation_defined;
+#else
+ class const_iterator;
+#endif
+
+ /** Construct a list.
+
+ @param s A string containing the list contents. The string
+ must remain valid for the lifetime of the container.
+ */
+ explicit
+ token_list(string_view s)
+ : s_(s)
+ {
+ }
+
+ /// Return a const iterator to the beginning of the list
+ const_iterator begin() const;
+
+ /// Return a const iterator to the end of the list
+ const_iterator end() const;
+
+ /// Return a const iterator to the beginning of the list
+ const_iterator cbegin() const;
+
+ /// Return a const iterator to the end of the list
+ const_iterator cend() const;
+
+ /** Return `true` if a token is present in the list.
+
+ @param s The token to find. A case-insensitive comparison is used.
+ */
+ template<class T>
+ bool
+ exists(T const& s);
+};
+
+/** A list of tokens in a comma separated HTTP field value.
+
+ This container allows iteration of a list of items in a
+ header field value. The input is a comma separated list of
+ tokens.
+
+ If a parsing error is encountered while iterating the string,
+ the behavior of the container will be as if a string containing
+ only characters up to but excluding the first invalid character
+ was used to construct the list.
+
+ @par BNF
+ @code
+ token-list = *( "," OWS ) token *( OWS "," [ OWS token ] )
+ @endcode
+
+ To use this class, construct with the string to be parsed and
+ then use `begin` and `end`, or range-for to iterate each item:
+
+ @par Example
+ @code
+ for(auto const& token : token_list{"apple, pear, banana"})
+ std::cout << token << "\n";
+ @endcode
+*/
+using opt_token_list =
+ detail::basic_parsed_list<
+ detail::opt_token_list_policy>;
+
+/** Returns `true` if a parsed list is parsed without errors.
+
+ This function iterates a single pass through a parsed list
+ and returns `true` if there were no parsing errors, else
+ returns `false`.
+*/
+template<class Policy>
+bool
+validate_list(detail::basic_parsed_list<
+ Policy> const& list);
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/rfc7230.ipp>
+
+#endif
diff --git a/boost/beast/http/serializer.hpp b/boost/beast/http/serializer.hpp
new file mode 100644
index 0000000000..50e58185a9
--- /dev/null
+++ b/boost/beast/http/serializer.hpp
@@ -0,0 +1,370 @@
+//
+// 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_HTTP_SERIALIZER_HPP
+#define BOOST_BEAST_HTTP_SERIALIZER_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/variant.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/chunk_encode.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/optional.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** Provides buffer oriented HTTP message serialization functionality.
+
+ An object of this type is used to serialize a complete
+ HTTP message into a sequence of octets. To use this class,
+ construct an instance with the message to be serialized.
+ The implementation will automatically perform chunk encoding
+ if the contents of the message indicate that chunk encoding
+ is required.
+
+ Chunked output produced by the serializer never contains chunk
+ extensions or trailers, and the location of chunk boundaries
+ is not specified. If callers require chunk extensions, trailers,
+ or control over the exact contents of each chunk they should
+ use the serializer to write just the message header, and then
+ assume control over serializing the chunked payload by using
+ the chunk buffer sequence types @ref chunk_body, @ref chunk_crlf,
+ @ref chunk_header, and @ref chunk_last.
+
+ @tparam isRequest `true` if the message is a request.
+
+ @tparam Body The body type of the message.
+
+ @tparam Fields The type of fields in the message.
+*/
+template<
+ bool isRequest,
+ class Body,
+ class Fields = fields>
+class serializer
+{
+public:
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+
+ /** The type of message this serializer uses
+
+ This may be const or non-const depending on the
+ implementation of the corresponding @b BodyWriter.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using value_type = implementation_defined;
+#else
+ using value_type =
+ typename std::conditional<
+ std::is_constructible<typename Body::writer,
+ message<isRequest, Body, Fields>&>::value &&
+ ! std::is_constructible<typename Body::writer,
+ message<isRequest, Body, Fields> const&>::value,
+ message<isRequest, Body, Fields>,
+ message<isRequest, Body, Fields> const>::type;
+#endif
+
+private:
+ enum
+ {
+ do_construct = 0,
+
+ do_init = 10,
+ do_header_only = 20,
+ do_header = 30,
+ do_body = 40,
+
+ do_init_c = 50,
+ do_header_only_c = 60,
+ do_header_c = 70,
+ do_body_c = 80,
+ do_final_c = 90,
+ #ifndef BOOST_BEAST_NO_BIG_VARIANTS
+ do_body_final_c = 100,
+ do_all_c = 110,
+ #endif
+
+ do_complete = 120
+ };
+
+ void frdinit(std::true_type);
+ void frdinit(std::false_type);
+
+ template<std::size_t, class Visit>
+ void
+ do_visit(error_code& ec, Visit& visit);
+
+ using writer = typename Body::writer;
+
+ using cb1_t = buffers_suffix<typename
+ Fields::writer::const_buffers_type>; // header
+ using pcb1_t = buffers_prefix_view<cb1_t const&>;
+
+ using cb2_t = buffers_suffix<buffers_cat_view<
+ typename Fields::writer::const_buffers_type,// header
+ typename writer::const_buffers_type>>; // body
+ using pcb2_t = buffers_prefix_view<cb2_t const&>;
+
+ using cb3_t = buffers_suffix<
+ typename writer::const_buffers_type>; // body
+ using pcb3_t = buffers_prefix_view<cb3_t const&>;
+
+ using cb4_t = buffers_suffix<buffers_cat_view<
+ typename Fields::writer::const_buffers_type,// header
+ detail::chunk_size, // chunk-size
+ boost::asio::const_buffer, // chunk-ext
+ chunk_crlf, // crlf
+ typename writer::const_buffers_type, // body
+ chunk_crlf>>; // crlf
+ using pcb4_t = buffers_prefix_view<cb4_t const&>;
+
+ using cb5_t = buffers_suffix<buffers_cat_view<
+ detail::chunk_size, // chunk-header
+ boost::asio::const_buffer, // chunk-ext
+ chunk_crlf, // crlf
+ typename writer::const_buffers_type, // body
+ chunk_crlf>>; // crlf
+ using pcb5_t = buffers_prefix_view<cb5_t const&>;
+
+ using cb6_t = buffers_suffix<buffers_cat_view<
+ detail::chunk_size, // chunk-header
+ boost::asio::const_buffer, // chunk-size
+ chunk_crlf, // crlf
+ typename writer::const_buffers_type, // body
+ chunk_crlf, // crlf
+ boost::asio::const_buffer, // chunk-final
+ boost::asio::const_buffer, // trailers
+ chunk_crlf>>; // crlf
+ using pcb6_t = buffers_prefix_view<cb6_t const&>;
+
+ using cb7_t = buffers_suffix<buffers_cat_view<
+ typename Fields::writer::const_buffers_type,// header
+ detail::chunk_size, // chunk-size
+ boost::asio::const_buffer, // chunk-ext
+ chunk_crlf, // crlf
+ typename writer::const_buffers_type, // body
+ chunk_crlf, // crlf
+ boost::asio::const_buffer, // chunk-final
+ boost::asio::const_buffer, // trailers
+ chunk_crlf>>; // crlf
+ using pcb7_t = buffers_prefix_view<cb7_t const&>;
+
+ using cb8_t = buffers_suffix<buffers_cat_view<
+ boost::asio::const_buffer, // chunk-final
+ boost::asio::const_buffer, // trailers
+ chunk_crlf>>; // crlf
+ using pcb8_t = buffers_prefix_view<cb8_t const&>;
+
+ value_type& m_;
+ writer rd_;
+ boost::optional<typename Fields::writer> frd_;
+ beast::detail::variant<
+ cb1_t, cb2_t, cb3_t, cb4_t,
+ cb5_t ,cb6_t, cb7_t, cb8_t> v_;
+ beast::detail::variant<
+ pcb1_t, pcb2_t, pcb3_t, pcb4_t,
+ pcb5_t ,pcb6_t, pcb7_t, pcb8_t> pv_;
+ std::size_t limit_ =
+ (std::numeric_limits<std::size_t>::max)();
+ int s_ = do_construct;
+ bool split_ = false;
+ bool header_done_ = false;
+ bool more_;
+
+public:
+ /// Constructor
+ serializer(serializer&&) = default;
+
+ /// Constructor
+ serializer(serializer const&) = default;
+
+ /// Assignment
+ serializer& operator=(serializer const&) = delete;
+
+ /** Constructor
+
+ The implementation guarantees that the message passed on
+ construction will not be accessed until the first call to
+ @ref next. This allows the message to be lazily created.
+ For example, if the header is filled in before serialization.
+
+ @param msg A reference to the message to serialize, which must
+ remain valid for the lifetime of the serializer. Depending on
+ the type of Body used, this may or may not be a `const` reference.
+
+ @note This function participates in overload resolution only if
+ Body::writer is constructible from a `const` message reference.
+ */
+ explicit
+ serializer(value_type& msg);
+
+ /// Returns the message being serialized
+ value_type&
+ get()
+ {
+ return m_;
+ }
+
+ /// Returns the serialized buffer size limit
+ std::size_t
+ limit()
+ {
+ return limit_;
+ }
+
+ /** Set the serialized buffer size limit
+
+ This function adjusts the limit on the maximum size of the
+ buffers passed to the visitor. The new size limit takes effect
+ in the following call to @ref next.
+
+ The default is no buffer size limit.
+
+ @param limit The new buffer size limit. If this number
+ is zero, the size limit is removed.
+ */
+ void
+ limit(std::size_t limit)
+ {
+ limit_ = limit > 0 ? limit :
+ (std::numeric_limits<std::size_t>::max)();
+ }
+
+ /** Returns `true` if we will pause after writing the complete header.
+ */
+ bool
+ split()
+ {
+ return split_;
+ }
+
+ /** Set whether the header and body are written separately.
+
+ When the split feature is enabled, the implementation will
+ write only the octets corresponding to the serialized header
+ first. If the header has already been written, this function
+ will have no effect on output.
+ */
+ void
+ split(bool v)
+ {
+ split_ = v;
+ }
+
+ /** Return `true` if serialization of the header is complete.
+
+ This function indicates whether or not all buffers containing
+ serialized header octets have been retrieved.
+ */
+ bool
+ is_header_done()
+ {
+ return header_done_;
+ }
+
+ /** Return `true` if serialization is complete.
+
+ The operation is complete when all octets corresponding
+ to the serialized representation of the message have been
+ successfully retrieved.
+ */
+ bool
+ is_done()
+ {
+ return s_ == do_complete;
+ }
+
+ /** Returns the next set of buffers in the serialization.
+
+ This function will attempt to call the `visit` function
+ object with a @b ConstBufferSequence of unspecified type
+ representing the next set of buffers in the serialization
+ of the message represented by this object.
+
+ If there are no more buffers in the serialization, the
+ visit function will not be called. In this case, no error
+ will be indicated, and the function @ref is_done will
+ return `true`.
+
+ @param ec Set to the error, if any occurred.
+
+ @param visit The function to call. The equivalent function
+ signature of this object must be:
+ @code
+ template<class ConstBufferSequence>
+ void visit(error_code&, ConstBufferSequence const&);
+ @endcode
+ The function is not copied, if no error occurs it will be
+ invoked before the call to @ref next returns.
+
+ */
+ template<class Visit>
+ void
+ next(error_code& ec, Visit&& visit);
+
+ /** Consume buffer octets in the serialization.
+
+ This function should be called after one or more octets
+ contained in the buffers provided in the prior call
+ to @ref next have been used.
+
+ After a call to @ref consume, callers should check the
+ return value of @ref is_done to determine if the entire
+ message has been serialized.
+
+ @param n The number of octets to consume. This number must
+ be greater than zero and no greater than the number of
+ octets in the buffers provided in the prior call to @ref next.
+ */
+ void
+ consume(std::size_t n);
+
+ /** Provides low-level access to the associated @b BodyWriter
+
+ This function provides access to the instance of the writer
+ associated with the body and created by the serializer
+ upon construction. The behavior of accessing this object
+ is defined by the specification of the particular writer
+ and its associated body.
+
+ @return A reference to the writer.
+ */
+ writer&
+ reader_impl()
+ {
+ return rd_;
+ }
+};
+
+/// A serializer for HTTP/1 requests
+template<class Body, class Fields = fields>
+using request_serializer = serializer<true, Body, Fields>;
+
+/// A serializer for HTTP/1 responses
+template<class Body, class Fields = fields>
+using response_serializer = serializer<false, Body, Fields>;
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/serializer.ipp>
+
+#endif
diff --git a/boost/beast/http/span_body.hpp b/boost/beast/http/span_body.hpp
new file mode 100644
index 0000000000..6282ecc6d9
--- /dev/null
+++ b/boost/beast/http/span_body.hpp
@@ -0,0 +1,171 @@
+//
+// 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_HTTP_SPAN_BODY_HPP
+#define BOOST_BEAST_HTTP_SPAN_BODY_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/span.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/optional.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A @b Body using @ref span
+
+ This body uses @ref span as a memory-based container for
+ holding message payloads. The container represents a
+ non-owning reference to a continguous area of memory.
+ Messages using this body type may be serialized and
+ parsed.
+
+ Unlike @ref buffer_body, only one buffer may be provided
+ during a parse or serialize operation.
+*/
+template<class T>
+struct span_body
+{
+private:
+ static_assert(std::is_pod<T>::value,
+ "POD requirements not met");
+
+public:
+ /** The type of container used for the body
+
+ This determines the type of @ref message::body
+ when this body type is used with a message container.
+ */
+ using value_type = span<T>;
+
+ /** Returns the payload size of the body
+
+ When this body is used with @ref message::prepare_payload,
+ the Content-Length will be set to the payload size, and
+ any chunked Transfer-Encoding will be removed.
+ */
+ static
+ std::uint64_t
+ size(value_type const& body)
+ {
+ return body.size();
+ }
+
+ /** The algorithm for parsing the body
+
+ Meets the requirements of @b BodyReader.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using reader = implementation_defined;
+#else
+ class reader
+ {
+ value_type& body_;
+
+ public:
+ template<bool isRequest, class Fields>
+ explicit
+ reader(message<isRequest,
+ span_body, Fields>& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(boost::optional<
+ std::uint64_t> const& length, error_code& ec)
+ {
+ if(length && *length > body_.size())
+ {
+ ec = error::buffer_overflow;
+ return;
+ }
+ ec.assign(0, ec.category());
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers,
+ error_code& ec)
+ {
+ using boost::asio::buffer_size;
+ using boost::asio::buffer_copy;
+ auto const n = buffer_size(buffers);
+ auto const len = body_.size();
+ if(n > len)
+ {
+ ec = error::buffer_overflow;
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ buffer_copy(boost::asio::buffer(
+ body_.data(), n), buffers);
+ body_ = value_type{
+ body_.data() + n, body_.size() - n};
+ return n;
+ }
+
+ void
+ finish(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+ };
+#endif
+
+ /** The algorithm for serializing the body
+
+ Meets the requirements of @b BodyWriter.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using writer = implementation_defined;
+#else
+ class writer
+ {
+ value_type const& body_;
+
+ public:
+ using const_buffers_type =
+ boost::asio::const_buffer;
+
+ template<bool isRequest, class Fields>
+ explicit
+ writer(message<isRequest,
+ span_body, Fields> const& msg)
+ : body_(msg.body())
+ {
+ }
+
+ void
+ init(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ boost::optional<std::pair<const_buffers_type, bool>>
+ get(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ return {{
+ { body_.data(),
+ body_.size() * sizeof(typename
+ value_type::value_type)},
+ false}};
+ }
+ };
+#endif
+};
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/status.hpp b/boost/beast/http/status.hpp
new file mode 100644
index 0000000000..8d7938ca0c
--- /dev/null
+++ b/boost/beast/http/status.hpp
@@ -0,0 +1,167 @@
+//
+// 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_HTTP_STATUS_HPP
+#define BOOST_BEAST_HTTP_STATUS_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/string.hpp>
+#include <iosfwd>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+enum class status : unsigned
+{
+ /** An unknown status-code.
+
+ This value indicates that the value for the status code
+ is not in the list of commonly recognized status codes.
+ Callers interested in the exactly value should use the
+ interface which provides the raw integer.
+ */
+ unknown = 0,
+
+ continue_ = 100,
+ switching_protocols = 101,
+ processing = 102,
+
+ ok = 200,
+ created = 201,
+ accepted = 202,
+ non_authoritative_information = 203,
+ no_content = 204,
+ reset_content = 205,
+ partial_content = 206,
+ multi_status = 207,
+ already_reported = 208,
+ im_used = 226,
+
+ multiple_choices = 300,
+ moved_permanently = 301,
+ found = 302,
+ see_other = 303,
+ not_modified = 304,
+ use_proxy = 305,
+ temporary_redirect = 307,
+ permanent_redirect = 308,
+
+ bad_request = 400,
+ unauthorized = 401,
+ payment_required = 402,
+ forbidden = 403,
+ not_found = 404,
+ method_not_allowed = 405,
+ not_acceptable = 406,
+ proxy_authentication_required = 407,
+ request_timeout = 408,
+ conflict = 409,
+ gone = 410,
+ length_required = 411,
+ precondition_failed = 412,
+ payload_too_large = 413,
+ uri_too_long = 414,
+ unsupported_media_type = 415,
+ range_not_satisfiable = 416,
+ expectation_failed = 417,
+ misdirected_request = 421,
+ unprocessable_entity = 422,
+ locked = 423,
+ failed_dependency = 424,
+ upgrade_required = 426,
+ precondition_required = 428,
+ too_many_requests = 429,
+ request_header_fields_too_large = 431,
+ connection_closed_without_response = 444,
+ unavailable_for_legal_reasons = 451,
+ client_closed_request = 499,
+
+ internal_server_error = 500,
+ not_implemented = 501,
+ bad_gateway = 502,
+ service_unavailable = 503,
+ gateway_timeout = 504,
+ http_version_not_supported = 505,
+ variant_also_negotiates = 506,
+ insufficient_storage = 507,
+ loop_detected = 508,
+ not_extended = 510,
+ network_authentication_required = 511,
+ network_connect_timeout_error = 599
+};
+
+/** Represents the class of a status-code.
+*/
+enum class status_class : unsigned
+{
+ /// Unknown status-class
+ unknown = 0,
+
+ /// The request was received, continuing processing.
+ informational = 1,
+
+ /// The request was successfully received, understood, and accepted.
+ successful = 2,
+
+ /// Further action needs to be taken in order to complete the request.
+ redirection = 3,
+
+ /// The request contains bad syntax or cannot be fulfilled.
+ client_error = 4,
+
+ /// The server failed to fulfill an apparently valid request.
+ server_error = 5,
+};
+
+/** Converts the integer to a known status-code.
+
+ If the integer does not match a known status code,
+ @ref status::unknown is returned.
+*/
+status
+int_to_status(unsigned v);
+
+/** Convert an integer to a status_class.
+
+ @param v The integer representing a status code.
+
+ @return The status class. If the integer does not match
+ a known status class, @ref status_class::unknown is returned.
+*/
+status_class
+to_status_class(unsigned v);
+
+/** Convert a status_code to a status_class.
+
+ @param v The status code to convert.
+
+ @return The status class.
+*/
+status_class
+to_status_class(status v);
+
+/** Returns the obsolete reason-phrase text for a status code.
+
+ @param v The status code to use.
+*/
+string_view
+obsolete_reason(status v);
+
+/// Outputs the standard reason phrase of a status code to a stream.
+std::ostream&
+operator<<(std::ostream&, status);
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/status.ipp>
+
+#endif
diff --git a/boost/beast/http/string_body.hpp b/boost/beast/http/string_body.hpp
new file mode 100644
index 0000000000..18eda414e8
--- /dev/null
+++ b/boost/beast/http/string_body.hpp
@@ -0,0 +1,199 @@
+//
+// 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_HTTP_STRING_BODY_HPP
+#define BOOST_BEAST_HTTP_STRING_BODY_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/optional.hpp>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A @b Body using `std::basic_string`
+
+ This body uses `std::basic_string` as a memory-based container
+ for holding message payloads. Messages using this body type
+ may be serialized and parsed.
+*/
+template<
+ class CharT,
+ class Traits = std::char_traits<CharT>,
+ class Allocator = std::allocator<CharT>>
+struct basic_string_body
+{
+private:
+ static_assert(
+ std::is_integral<CharT>::value &&
+ sizeof(CharT) == 1,
+ "CharT requirements not met");
+
+public:
+ /** The type of container used for the body
+
+ This determines the type of @ref message::body
+ when this body type is used with a message container.
+ */
+ using value_type =
+ std::basic_string<CharT, Traits, Allocator>;
+
+ /** Returns the payload size of the body
+
+ When this body is used with @ref message::prepare_payload,
+ the Content-Length will be set to the payload size, and
+ any chunked Transfer-Encoding will be removed.
+ */
+ static
+ std::uint64_t
+ size(value_type const& body)
+ {
+ return body.size();
+ }
+
+ /** The algorithm for parsing the body
+
+ Meets the requirements of @b BodyReader.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using reader = implementation_defined;
+#else
+ class reader
+ {
+ value_type& body_;
+
+ public:
+ template<bool isRequest, class Fields>
+ explicit
+ reader(message<isRequest,
+ basic_string_body, Fields>& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(boost::optional<
+ std::uint64_t> const& length, error_code& ec)
+ {
+ if(length)
+ {
+ if(static_cast<std::size_t>(*length) != *length)
+ {
+ ec = error::buffer_overflow;
+ return;
+ }
+ try
+ {
+ body_.reserve(
+ static_cast<std::size_t>(*length));
+ }
+ catch(std::exception const&)
+ {
+ ec = error::buffer_overflow;
+ return;
+ }
+ }
+ ec.assign(0, ec.category());
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers,
+ error_code& ec)
+ {
+ using boost::asio::buffer_size;
+ using boost::asio::buffer_copy;
+ auto const extra = buffer_size(buffers);
+ auto const size = body_.size();
+ try
+ {
+ body_.resize(size + extra);
+ }
+ catch(std::exception const&)
+ {
+ ec = error::buffer_overflow;
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ CharT* dest = &body_[size];
+ for(auto b : beast::detail::buffers_range(buffers))
+ {
+ Traits::copy(dest, reinterpret_cast<
+ CharT const*>(b.data()), b.size());
+ dest += b.size();
+ }
+ return extra;
+ }
+
+ void
+ finish(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+ };
+#endif
+
+ /** The algorithm for serializing the body
+
+ Meets the requirements of @b BodyWriter.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using writer = implementation_defined;
+#else
+ class writer
+ {
+ value_type const& body_;
+
+ public:
+ using const_buffers_type =
+ boost::asio::const_buffer;
+
+ template<bool isRequest, class Fields>
+ explicit
+ writer(message<isRequest,
+ basic_string_body, Fields> const& msg)
+ : body_(msg.body())
+ {
+ }
+
+ void
+ init(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ boost::optional<std::pair<const_buffers_type, bool>>
+ get(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ return {{const_buffers_type{
+ body_.data(), body_.size()}, false}};
+ }
+ };
+#endif
+};
+
+/// A @b Body using `std::string`
+using string_body = basic_string_body<char>;
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/type_traits.hpp b/boost/beast/http/type_traits.hpp
new file mode 100644
index 0000000000..f90f16ffef
--- /dev/null
+++ b/boost/beast/http/type_traits.hpp
@@ -0,0 +1,185 @@
+//
+// 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_HTTP_TYPE_TRAITS_HPP
+#define BOOST_BEAST_HTTP_TYPE_TRAITS_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/http/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/optional.hpp>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<bool, class, class>
+struct message;
+
+/** Determine if `T` meets the requirements of @b Body.
+
+ This metafunction is equivalent to `std::true_type`
+ if `T` has a nested type named `value_type`.
+
+ @tparam T The body type to test.
+
+ @par Example
+ @code
+ template<bool isRequest, class Body, class Fields>
+ void check_body(message<isRequest, Body, Fields> const&)
+ {
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ }
+ @endcode
+*/
+template<class T>
+#if BOOST_BEAST_DOXYGEN
+struct is_body : std::integral_constant<bool, ...>{};
+#else
+using is_body = detail::has_value_type<T>;
+#endif
+
+/** Determine if a @b Body type has a reader.
+
+ This metafunction is equivalent to `std::true_type` if:
+
+ @li `T` has a nested type named `reader`
+
+ @li The nested type meets the requirements of @b BodyWriter.
+
+ @tparam T The body type to test.
+
+ @par Example
+ @code
+ template<bool isRequest, class Body, class Fields>
+ void check_can_serialize(message<isRequest, Body, Fields> const&)
+ {
+ static_assert(is_body_writer<Body>::value,
+ "Cannot serialize Body, no reader");
+ }
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_body_writer : std::integral_constant<bool, ...> {};
+#else
+template<class T, class = void>
+struct is_body_writer : std::false_type {};
+
+template<class T>
+struct is_body_writer<T, beast::detail::void_t<
+ typename T::writer,
+ typename T::writer::const_buffers_type,
+ decltype(
+ std::declval<typename T::writer&>().init(std::declval<error_code&>()),
+ std::declval<boost::optional<std::pair<
+ typename T::writer::const_buffers_type, bool>>&>() =
+ std::declval<typename T::writer>().get(std::declval<error_code&>()),
+ (void)0)>> : std::integral_constant<bool,
+ boost::asio::is_const_buffer_sequence<
+ typename T::writer::const_buffers_type>::value &&
+ std::is_constructible<typename T::writer,
+ message<true, T, detail::fields_model>&>::value &&
+ std::is_constructible<typename T::writer,
+ message<false, T, detail::fields_model>&>::value
+ > {};
+#endif
+
+/** Determine if a @b Body type has a reader.
+
+ This metafunction is equivalent to `std::true_type` if:
+
+ @li `T` has a nested type named `reader`
+
+ @li The nested type meets the requirements of @b BodyReader.
+
+ @tparam T The body type to test.
+
+ @par Example
+ @code
+ template<bool isRequest, class Body, class Fields>
+ void check_can_parse(message<isRequest, Body, Fields>&)
+ {
+ static_assert(is_body_reader<Body>::value,
+ "Cannot parse Body, no reader");
+ }
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_body_reader : std::integral_constant<bool, ...> {};
+#else
+template<class T, class = void>
+struct is_body_reader : std::false_type {};
+
+template<class T>
+struct is_body_reader<T, beast::detail::void_t<decltype(
+ std::declval<typename T::reader&>().init(
+ boost::optional<std::uint64_t>(),
+ std::declval<error_code&>()),
+ std::declval<std::size_t&>() =
+ std::declval<typename T::reader&>().put(
+ std::declval<boost::asio::const_buffer>(),
+ std::declval<error_code&>()),
+ std::declval<typename T::reader&>().finish(
+ std::declval<error_code&>()),
+ (void)0)>> : std::integral_constant<bool,
+ std::is_constructible<typename T::reader,
+ message<true, T, detail::fields_model>&>::value &&
+ std::is_constructible<typename T::reader,
+ message<false, T, detail::fields_model>&>::value
+ >
+{
+};
+#endif
+
+/** Determine if `T` meets the requirements of @b Fields
+
+ @tparam T The body type to test.
+
+ @par Example
+
+ Use with `static_assert`:
+
+ @code
+ template<bool isRequest, class Body, class Fields>
+ void f(message<isRequest, Body, Fields> const&)
+ {
+ static_assert(is_fields<Fields>::value,
+ "Fields requirements not met");
+ ...
+ @endcode
+
+ Use with `std::enable_if` (SFINAE):
+
+ @code
+ template<bool isRequest, class Body, class Fields>
+ typename std::enable_if<is_fields<Fields>::value>::type
+ f(message<isRequest, Body, Fields> const&);
+ @endcode
+*/
+#if BOOST_BEAST_DOXYGEN
+template<class T>
+struct is_fields : std::integral_constant<bool, ...> {};
+#else
+template<class T>
+using is_fields = typename detail::is_fields_helper<T>::type;
+#endif
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/vector_body.hpp b/boost/beast/http/vector_body.hpp
new file mode 100644
index 0000000000..eb37145d61
--- /dev/null
+++ b/boost/beast/http/vector_body.hpp
@@ -0,0 +1,185 @@
+//
+// 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_HTTP_VECTOR_BODY_HPP
+#define BOOST_BEAST_HTTP_VECTOR_BODY_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/optional.hpp>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** A @b Body using `std::vector`
+
+ This body uses `std::vector` as a memory-based container
+ for holding message payloads. Messages using this body type
+ may be serialized and parsed.
+*/
+template<class T, class Allocator = std::allocator<T>>
+struct vector_body
+{
+private:
+ static_assert(sizeof(T) == 1 &&
+ std::is_integral<T>::value,
+ "T requirements not met");
+
+public:
+ /** The type of container used for the body
+
+ This determines the type of @ref message::body
+ when this body type is used with a message container.
+ */
+ using value_type = std::vector<T, Allocator>;
+
+ /** Returns the payload size of the body
+
+ When this body is used with @ref message::prepare_payload,
+ the Content-Length will be set to the payload size, and
+ any chunked Transfer-Encoding will be removed.
+ */
+ static
+ std::uint64_t
+ size(value_type const& body)
+ {
+ return body.size();
+ }
+
+ /** The algorithm for parsing the body
+
+ Meets the requirements of @b BodyReader.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using reader = implementation_defined;
+#else
+ class reader
+ {
+ value_type& body_;
+
+ public:
+ template<bool isRequest, class Fields>
+ explicit
+ reader(message<isRequest,
+ vector_body, Fields>& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(boost::optional<
+ std::uint64_t> const& length, error_code& ec)
+ {
+ if(length)
+ {
+ if(static_cast<std::size_t>(*length) != *length)
+ {
+ ec = error::buffer_overflow;
+ return;
+ }
+ try
+ {
+ body_.reserve(
+ static_cast<std::size_t>(*length));
+ }
+ catch(std::exception const&)
+ {
+ ec = error::buffer_overflow;
+ return;
+ }
+ }
+ ec.assign(0, ec.category());
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers,
+ error_code& ec)
+ {
+ using boost::asio::buffer_size;
+ using boost::asio::buffer_copy;
+ auto const n = buffer_size(buffers);
+ auto const len = body_.size();
+ try
+ {
+ body_.resize(len + n);
+ }
+ catch(std::exception const&)
+ {
+ ec = error::buffer_overflow;
+ return 0;
+ }
+ ec.assign(0, ec.category());
+ return buffer_copy(boost::asio::buffer(
+ &body_[0] + len, n), buffers);
+ }
+
+ void
+ finish(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+ };
+#endif
+
+ /** The algorithm for serializing the body
+
+ Meets the requirements of @b BodyWriter.
+ */
+#if BOOST_BEAST_DOXYGEN
+ using writer = implementation_defined;
+#else
+ class writer
+ {
+ value_type const& body_;
+
+ public:
+ using const_buffers_type =
+ boost::asio::const_buffer;
+
+ template<bool isRequest, class Fields>
+ explicit
+ writer(message<isRequest,
+ vector_body, Fields> const& msg)
+ : body_(msg.body())
+ {
+ }
+
+ void
+ init(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+
+ boost::optional<std::pair<const_buffers_type, bool>>
+ get(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ return {{const_buffers_type{
+ body_.data(), body_.size()}, false}};
+ }
+ };
+#endif
+};
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/verb.hpp b/boost/beast/http/verb.hpp
new file mode 100644
index 0000000000..1540a243e8
--- /dev/null
+++ b/boost/beast/http/verb.hpp
@@ -0,0 +1,157 @@
+//
+// 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_HTTP_VERB_HPP
+#define BOOST_BEAST_HTTP_VERB_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/string.hpp>
+#include <iosfwd>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** HTTP request method verbs
+
+ Each verb corresponds to a particular method string
+ used in HTTP request messages.
+*/
+enum class verb
+{
+ /** An unknown method.
+
+ This value indicates that the request method string is not
+ one of the recognized verbs. Callers interested in the method
+ should use an interface which returns the original string.
+ */
+ unknown = 0,
+
+ /// The DELETE method deletes the specified resource
+ delete_,
+
+ /** The GET method requests a representation of the specified resource.
+
+ Requests using GET should only retrieve data and should have no other effect.
+ */
+ get,
+
+ /** The HEAD method asks for a response identical to that of a GET request, but without the response body.
+
+ This is useful for retrieving meta-information written in response
+ headers, without having to transport the entire content.
+ */
+ head,
+
+ /** The POST method requests that the server accept the entity enclosed in the request as a new subordinate of the web resource identified by the URI.
+
+ The data POSTed might be, for example, an annotation for existing
+ resources; a message for a bulletin board, newsgroup, mailing list,
+ or comment thread; a block of data that is the result of submitting
+ a web form to a data-handling process; or an item to add to a database
+ */
+ post,
+
+ /** The PUT method requests that the enclosed entity be stored under the supplied URI.
+
+ If the URI refers to an already existing resource, it is modified;
+ if the URI does not point to an existing resource, then the server
+ can create the resource with that URI.
+ */
+ put,
+
+ /** The CONNECT method converts the request connection to a transparent TCP/IP tunnel.
+
+ This is usually to facilitate SSL-encrypted communication (HTTPS)
+ through an unencrypted HTTP proxy.
+ */
+ connect,
+
+ /** The OPTIONS method returns the HTTP methods that the server supports for the specified URL.
+
+ This can be used to check the functionality of a web server by requesting
+ '*' instead of a specific resource.
+ */
+ options,
+
+ /** The TRACE method echoes the received request so that a client can see what (if any) changes or additions have been made by intermediate servers.
+ */
+ trace,
+
+ // WebDAV
+
+ copy,
+ lock,
+ mkcol,
+ move,
+ propfind,
+ proppatch,
+ search,
+ unlock,
+ bind,
+ rebind,
+ unbind,
+ acl,
+
+ // subversion
+
+ report,
+ mkactivity,
+ checkout,
+ merge,
+
+ // upnp
+
+ msearch,
+ notify,
+ subscribe,
+ unsubscribe,
+
+ // RFC-5789
+
+ patch,
+ purge,
+
+ // CalDAV
+
+ mkcalendar,
+
+ // RFC-2068, section 19.6.1.2
+
+ link,
+ unlink
+};
+
+/** Converts a string to the request method verb.
+
+ If the string does not match a known request method,
+ @ref verb::unknown is returned.
+*/
+verb
+string_to_verb(string_view s);
+
+/// Returns the text representation of a request method verb.
+string_view
+to_string(verb v);
+
+/// Write the text for a request method verb to an output stream.
+inline
+std::ostream&
+operator<<(std::ostream& os, verb v)
+{
+ return os << to_string(v);
+}
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/verb.ipp>
+
+#endif
diff --git a/boost/beast/http/write.hpp b/boost/beast/http/write.hpp
new file mode 100644
index 0000000000..755ed0b94b
--- /dev/null
+++ b/boost/beast/http/write.hpp
@@ -0,0 +1,569 @@
+//
+// 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_HTTP_WRITE_HPP
+#define BOOST_BEAST_HTTP_WRITE_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/multi_buffer.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/serializer.hpp>
+#include <boost/beast/http/detail/chunk_encode.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/asio/async_result.hpp>
+#include <iosfwd>
+#include <limits>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+/** Write part of a message to a stream using a serializer.
+
+ This function is used to write part of a message to a stream using
+ a caller-provided HTTP/1 serializer. The call will block until one
+ of the following conditions is true:
+
+ @li One or more bytes have been transferred.
+
+ @li The function @ref serializer::is_done returns `true`
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls
+ to the stream's `write_some` function.
+
+ The amount of data actually transferred is controlled by the behavior
+ of the underlying stream, subject to the buffer size limit of the
+ serializer obtained or set through a call to @ref serializer::limit.
+ Setting a limit and performing bounded work helps applications set
+ reasonable timeouts. It also allows application-level flow control
+ to function correctly. For example when using a TCP/IP based
+ stream.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b SyncWriteStream concept.
+
+ @param sr The serializer to use.
+
+ @return The number of bytes written to the stream.
+
+ @throws system_error Thrown on failure.
+
+ @see serializer
+*/
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_some(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr);
+
+/** Write part of a message to a stream using a serializer.
+
+ This function is used to write part of a message to a stream using
+ a caller-provided HTTP/1 serializer. The call will block until one
+ of the following conditions is true:
+
+ @li One or more bytes have been transferred.
+
+ @li The function @ref serializer::is_done returns `true`
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls
+ to the stream's `write_some` function.
+
+ The amount of data actually transferred is controlled by the behavior
+ of the underlying stream, subject to the buffer size limit of the
+ serializer obtained or set through a call to @ref serializer::limit.
+ Setting a limit and performing bounded work helps applications set
+ reasonable timeouts. It also allows application-level flow control
+ to function correctly. For example when using a TCP/IP based
+ stream.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b SyncWriteStream concept.
+
+ @param sr The serializer to use.
+
+ @param ec Set to indicate what error occurred, if any.
+
+ @return The number of bytes written to the stream.
+
+ @see @ref async_write_some, @ref serializer
+*/
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_some(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ error_code& ec);
+
+/** Write part of a message to a stream asynchronously using a serializer.
+
+ This function is used to write part of a message to a stream
+ asynchronously using a caller-provided HTTP/1 serializer. The function
+ call always returns immediately. The asynchronous operation will continue
+ until one of the following conditions is true:
+
+ @li One or more bytes have been transferred.
+
+ @li The function @ref serializer::is_done returns `true`
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of zero or more calls to the stream's
+ `async_write_some` function, and is known as a <em>composed operation</em>.
+ The program must ensure that the stream performs no other writes
+ until this operation completes.
+
+ The amount of data actually transferred is controlled by the behavior
+ of the underlying stream, subject to the buffer size limit of the
+ serializer obtained or set through a call to @ref serializer::limit.
+ Setting a limit and performing bounded work helps applications set
+ reasonable timeouts. It also allows application-level flow control
+ to function correctly. For example when using a TCP/IP based
+ stream.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b AsyncWriteStream concept.
+
+ @param sr The serializer to use.
+ The object must remain valid at least until the
+ handler is called; ownership is not transferred.
+
+ @param handler The handler to be called when the operation
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // the number of bytes written to the stream
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+
+ @see @ref serializer
+*/
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_some(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler);
+
+//------------------------------------------------------------------------------
+
+/** Write a header to a stream using a serializer.
+
+ This function is used to write a header to a stream using a
+ caller-provided HTTP/1 serializer. The call will block until one
+ of the following conditions is true:
+
+ @li The function @ref serializer::is_header_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls
+ to the stream's `write_some` function.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b SyncWriteStream concept.
+
+ @param sr The serializer to use.
+
+ @return The number of bytes written to the stream.
+
+ @throws system_error Thrown on failure.
+
+ @note The implementation will call @ref serializer::split with
+ the value `true` on the serializer passed in.
+
+ @see @ref serializer
+*/
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_header(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr);
+
+/** Write a header to a stream using a serializer.
+
+ This function is used to write a header to a stream using a
+ caller-provided HTTP/1 serializer. The call will block until one
+ of the following conditions is true:
+
+ @li The function @ref serializer::is_header_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls
+ to the stream's `write_some` function.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b SyncWriteStream concept.
+
+ @param sr The serializer to use.
+
+ @param ec Set to indicate what error occurred, if any.
+
+ @return The number of bytes written to the stream.
+
+ @note The implementation will call @ref serializer::split with
+ the value `true` on the serializer passed in.
+
+ @see @ref serializer
+*/
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_header(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ error_code& ec);
+
+/** Write a header to a stream asynchronously using a serializer.
+
+ This function is used to write a header to a stream asynchronously
+ using a caller-provided HTTP/1 serializer. The function call always
+ returns immediately. The asynchronous operation will continue until
+ one of the following conditions is true:
+
+ @li The function @ref serializer::is_header_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of zero or more calls to the stream's
+ `async_write_some` function, and is known as a <em>composed operation</em>.
+ The program must ensure that the stream performs no other writes
+ until this operation completes.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b AsyncWriteStream concept.
+
+ @param sr The serializer to use.
+ The object must remain valid at least until the
+ handler is called; ownership is not transferred.
+
+ @param handler The handler to be called when the operation
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // the number of bytes written to the stream
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+
+ @note The implementation will call @ref serializer::split with
+ the value `true` on the serializer passed in.
+
+ @see @ref serializer
+*/
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_header(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler);
+
+//------------------------------------------------------------------------------
+
+/** Write a complete message to a stream using a serializer.
+
+ This function is used to write a complete message to a stream using
+ a caller-provided HTTP/1 serializer. The call will block until one
+ of the following conditions is true:
+
+ @li The function @ref serializer::is_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls
+ to the stream's `write_some` function.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b SyncWriteStream concept.
+
+ @param sr The serializer to use.
+
+ @return The number of bytes written to the stream.
+
+ @throws system_error Thrown on failure.
+
+ @see @ref serializer
+*/
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr);
+
+/** Write a complete message to a stream using a serializer.
+
+ This function is used to write a complete message to a stream using
+ a caller-provided HTTP/1 serializer. The call will block until one
+ of the following conditions is true:
+
+ @li The function @ref serializer::is_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls
+ to the stream's `write_some` function.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b SyncWriteStream concept.
+
+ @param sr The serializer to use.
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of bytes written to the stream.
+
+ @see @ref serializer
+*/
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ error_code& ec);
+
+/** Write a complete message to a stream asynchronously using a serializer.
+
+ This function is used to write a complete message to a stream
+ asynchronously using a caller-provided HTTP/1 serializer. The
+ function call always returns immediately. The asynchronous
+ operation will continue until one of the following conditions is true:
+
+ @li The function @ref serializer::is_done returns `true`
+
+ @li An error occurs.
+
+ This operation is implemented in terms of zero or more calls to the stream's
+ `async_write_some` function, and is known as a <em>composed operation</em>.
+ The program must ensure that the stream performs no other writes
+ until this operation completes.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b AsyncWriteStream concept.
+
+ @param sr The serializer to use.
+ The object must remain valid at least until the
+ handler is called; ownership is not transferred.
+
+ @param handler The handler to be called when the operation
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // the number of bytes written to the stream
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+
+ @see @ref serializer
+*/
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler);
+
+//------------------------------------------------------------------------------
+
+/** Write a complete message to a stream.
+
+ This function is used to write a complete message to a stream using
+ HTTP/1. The call will block until one of the following conditions is true:
+
+ @li The entire message is written.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls to the stream's
+ `write_some` function. The algorithm will use a temporary @ref serializer
+ with an empty chunk decorator to produce buffers.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b SyncWriteStream concept.
+
+ @param msg The message to write.
+
+ @return The number of bytes written to the stream.
+
+ @throws system_error Thrown on failure.
+
+ @see @ref message
+*/
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ message<isRequest, Body, Fields> const& msg);
+
+/** Write a complete message to a stream.
+
+ This function is used to write a complete message to a stream using
+ HTTP/1. The call will block until one of the following conditions is true:
+
+ @li The entire message is written.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls to the stream's
+ `write_some` function. The algorithm will use a temporary @ref serializer
+ with an empty chunk decorator to produce buffers.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b SyncWriteStream concept.
+
+ @param msg The message to write.
+
+ @param ec Set to the error, if any occurred.
+
+ @return The number of bytes written to the stream.
+
+ @see @ref message
+*/
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ message<isRequest, Body, Fields> const& msg,
+ error_code& ec);
+
+/** Write a complete message to a stream asynchronously.
+
+ This function is used to write a complete message to a stream asynchronously
+ using HTTP/1. The function call always returns immediately. The asynchronous
+ operation will continue until one of the following conditions is true:
+
+ @li The entire message is written.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of zero or more calls to the stream's
+ `async_write_some` function, and is known as a <em>composed operation</em>.
+ The program must ensure that the stream performs no other writes
+ until this operation completes. The algorithm will use a temporary
+ @ref serializer with an empty chunk decorator to produce buffers.
+
+ @param stream The stream to which the data is to be written.
+ The type must support the @b AsyncWriteStream concept.
+
+ @param msg The message to write.
+ The object must remain valid at least until the
+ handler is called; ownership is not transferred.
+
+ @param handler The handler to be called when the operation
+ completes. Copies will be made of the handler as required.
+ The equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& error, // result of operation
+ std::size_t bytes_transferred // the number of bytes written to the stream
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+
+ @see @ref message
+*/
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write(
+ AsyncWriteStream& stream,
+ message<isRequest, Body, Fields>& msg,
+ WriteHandler&& handler);
+
+//------------------------------------------------------------------------------
+
+/** Serialize an HTTP/1 header to a `std::ostream`.
+
+ The function converts the header to its HTTP/1 serialized
+ representation and stores the result in the output stream.
+
+ @param os The output stream to write to.
+
+ @param msg The message fields to write.
+*/
+template<bool isRequest, class Fields>
+std::ostream&
+operator<<(std::ostream& os,
+ header<isRequest, Fields> const& msg);
+
+/** Serialize an HTTP/1 message to a `std::ostream`.
+
+ The function converts the message to its HTTP/1 serialized
+ representation and stores the result in the output stream.
+
+ The implementation will automatically perform chunk encoding if
+ the contents of the message indicate that chunk encoding is required.
+
+ @param os The output stream to write to.
+
+ @param msg The message to write.
+*/
+template<bool isRequest, class Body, class Fields>
+std::ostream&
+operator<<(std::ostream& os,
+ message<isRequest, Body, Fields> const& msg);
+
+} // http
+} // beast
+} // boost
+
+#include <boost/beast/http/impl/write.ipp>
+
+#endif
diff --git a/boost/beast/version.hpp b/boost/beast/version.hpp
new file mode 100644
index 0000000000..e8898e66aa
--- /dev/null
+++ b/boost/beast/version.hpp
@@ -0,0 +1,28 @@
+//
+// 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_VERSION_HPP
+#define BOOST_BEAST_VERSION_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/config.hpp>
+
+/** @def BOOST_BEAST_API_VERSION
+
+ Identifies the API version of Beast.
+
+ This is a simple integer that is incremented by one every
+ time a set of code changes is merged to the develop branch.
+*/
+#define BOOST_BEAST_VERSION 144
+
+#define BOOST_BEAST_VERSION_STRING "Boost.Beast/" BOOST_STRINGIZE(BOOST_BEAST_VERSION)
+
+#endif
+
diff --git a/boost/beast/websocket.hpp b/boost/beast/websocket.hpp
new file mode 100644
index 0000000000..24b9d25e73
--- /dev/null
+++ b/boost/beast/websocket.hpp
@@ -0,0 +1,21 @@
+//
+// 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_HPP
+#define BOOST_BEAST_WEBSOCKET_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+
+#include <boost/beast/websocket/error.hpp>
+#include <boost/beast/websocket/option.hpp>
+#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/websocket/stream.hpp>
+#include <boost/beast/websocket/teardown.hpp>
+
+#endif
diff --git a/boost/beast/websocket/detail/frame.hpp b/boost/beast/websocket/detail/frame.hpp
new file mode 100644
index 0000000000..95080aeefc
--- /dev/null
+++ b/boost/beast/websocket/detail/frame.hpp
@@ -0,0 +1,304 @@
+//
+// 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_DETAIL_FRAME_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP
+
+#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/websocket/detail/utf8_checker.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/static_string.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/assert.hpp>
+#include <boost/endian/buffers.hpp>
+#include <cstdint>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+inline
+std::uint16_t
+big_uint16_to_native(void const* buf)
+{
+ auto const p = reinterpret_cast<
+ std::uint8_t const*>(buf);
+ return (p[0]<<8) + p[1];
+}
+
+inline
+std::uint64_t
+big_uint64_to_native(void const* buf)
+{
+ auto const p = reinterpret_cast<
+ std::uint8_t const*>(buf);
+ return
+ (static_cast<std::uint64_t>(p[0])<<56) +
+ (static_cast<std::uint64_t>(p[1])<<48) +
+ (static_cast<std::uint64_t>(p[2])<<40) +
+ (static_cast<std::uint64_t>(p[3])<<32) +
+ (static_cast<std::uint64_t>(p[4])<<24) +
+ (static_cast<std::uint64_t>(p[5])<<16) +
+ (static_cast<std::uint64_t>(p[6])<< 8) +
+ p[7];
+}
+
+inline
+std::uint32_t
+little_uint32_to_native(void const* buf)
+{
+ auto const p = reinterpret_cast<
+ std::uint8_t const*>(buf);
+ return
+ p[0] +
+ (static_cast<std::uint32_t>(p[1])<< 8) +
+ (static_cast<std::uint32_t>(p[2])<<16) +
+ (static_cast<std::uint32_t>(p[3])<<24);
+}
+
+inline
+void
+native_to_little_uint32(std::uint32_t v, void* buf)
+{
+ auto p = reinterpret_cast<std::uint8_t*>(buf);
+ p[0] = v & 0xff;
+ p[1] = (v >> 8) & 0xff;
+ p[2] = (v >> 16) & 0xff;
+ p[3] = (v >> 24) & 0xff;
+}
+
+/** WebSocket frame header opcodes. */
+enum class opcode : std::uint8_t
+{
+ cont = 0,
+ text = 1,
+ binary = 2,
+ rsv3 = 3,
+ rsv4 = 4,
+ rsv5 = 5,
+ rsv6 = 6,
+ rsv7 = 7,
+ close = 8,
+ ping = 9,
+ pong = 10,
+ crsvb = 11,
+ crsvc = 12,
+ crsvd = 13,
+ crsve = 14,
+ crsvf = 15
+};
+
+// Contents of a WebSocket frame header
+struct frame_header
+{
+ std::uint64_t len;
+ std::uint32_t key;
+ opcode op;
+ bool fin : 1;
+ bool mask : 1;
+ bool rsv1 : 1;
+ bool rsv2 : 1;
+ bool rsv3 : 1;
+};
+
+// holds the largest possible frame header
+using fh_buffer =
+ flat_static_buffer<14>;
+
+// holds the largest possible control frame
+using frame_buffer =
+ flat_static_buffer< 2 + 8 + 4 + 125 >;
+
+inline
+bool constexpr
+is_reserved(opcode op)
+{
+ return
+ (op >= opcode::rsv3 && op <= opcode::rsv7) ||
+ (op >= opcode::crsvb && op <= opcode::crsvf);
+}
+
+inline
+bool constexpr
+is_valid(opcode op)
+{
+ return op <= opcode::crsvf;
+}
+
+inline
+bool constexpr
+is_control(opcode op)
+{
+ return op >= opcode::close;
+}
+
+inline
+bool
+is_valid_close_code(std::uint16_t v)
+{
+ switch(v)
+ {
+ case close_code::normal: // 1000
+ case close_code::going_away: // 1001
+ case close_code::protocol_error: // 1002
+ case close_code::unknown_data: // 1003
+ case close_code::bad_payload: // 1007
+ case close_code::policy_error: // 1008
+ case close_code::too_big: // 1009
+ case close_code::needs_extension: // 1010
+ case close_code::internal_error: // 1011
+ case close_code::service_restart: // 1012
+ case close_code::try_again_later: // 1013
+ return true;
+
+ // explicitly reserved
+ case close_code::reserved1: // 1004
+ case close_code::no_status: // 1005
+ case close_code::abnormal: // 1006
+ case close_code::reserved2: // 1014
+ case close_code::reserved3: // 1015
+ return false;
+ }
+ // reserved
+ if(v >= 1016 && v <= 2999)
+ return false;
+ // not used
+ if(v <= 999)
+ return false;
+ return true;
+}
+
+//------------------------------------------------------------------------------
+
+// Write frame header to dynamic buffer
+//
+template<class DynamicBuffer>
+void
+write(DynamicBuffer& db, frame_header const& fh)
+{
+ using boost::asio::buffer;
+ using boost::asio::buffer_copy;
+ using namespace boost::endian;
+ std::size_t n;
+ std::uint8_t b[14];
+ b[0] = (fh.fin ? 0x80 : 0x00) | static_cast<std::uint8_t>(fh.op);
+ if(fh.rsv1)
+ b[0] |= 0x40;
+ if(fh.rsv2)
+ b[0] |= 0x20;
+ if(fh.rsv3)
+ b[0] |= 0x10;
+ b[1] = fh.mask ? 0x80 : 0x00;
+ if(fh.len <= 125)
+ {
+ b[1] |= fh.len;
+ n = 2;
+ }
+ else if(fh.len <= 65535)
+ {
+ b[1] |= 126;
+ ::new(&b[2]) big_uint16_buf_t{
+ (std::uint16_t)fh.len};
+ n = 4;
+ }
+ else
+ {
+ b[1] |= 127;
+ ::new(&b[2]) big_uint64_buf_t{fh.len};
+ n = 10;
+ }
+ if(fh.mask)
+ {
+ native_to_little_uint32(fh.key, &b[n]);
+ n += 4;
+ }
+ db.commit(buffer_copy(
+ db.prepare(n), buffer(b)));
+}
+
+// Read data from buffers
+// This is for ping and pong payloads
+//
+template<class Buffers>
+void
+read_ping(ping_data& data, Buffers const& bs)
+{
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ using boost::asio::mutable_buffer;
+ BOOST_ASSERT(buffer_size(bs) <= data.max_size());
+ data.resize(buffer_size(bs));
+ buffer_copy(mutable_buffer{
+ data.data(), data.size()}, bs);
+}
+
+// Read close_reason, return true on success
+// This is for the close payload
+//
+template<class Buffers>
+void
+read_close(close_reason& cr,
+ Buffers const& bs, close_code& code)
+{
+ using boost::asio::buffer;
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ using namespace boost::endian;
+ auto n = buffer_size(bs);
+ BOOST_ASSERT(n <= 125);
+ if(n == 0)
+ {
+ cr = close_reason{};
+ code = close_code::none;
+ return;
+ }
+ if(n == 1)
+ {
+ code = close_code::protocol_error;
+ return;
+ }
+ buffers_suffix<Buffers> cb(bs);
+ {
+ std::uint8_t b[2];
+ buffer_copy(buffer(b), cb);
+ cr.code = big_uint16_to_native(&b[0]);
+ cb.consume(2);
+ n -= 2;
+ if(! is_valid_close_code(cr.code))
+ {
+ code = close_code::protocol_error;
+ return;
+ }
+ }
+ if(n > 0)
+ {
+ cr.reason.resize(n);
+ buffer_copy(buffer(&cr.reason[0], n), cb);
+ if(! check_utf8(
+ cr.reason.data(), cr.reason.size()))
+ {
+ code = close_code::protocol_error;
+ return;
+ }
+ }
+ else
+ {
+ cr.reason = "";
+ }
+ code = close_code::none;
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/hybi13.hpp b/boost/beast/websocket/detail/hybi13.hpp
new file mode 100644
index 0000000000..b9c67b8200
--- /dev/null
+++ b/boost/beast/websocket/detail/hybi13.hpp
@@ -0,0 +1,75 @@
+//
+// 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_DETAIL_HYBI13_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
+
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/detail/base64.hpp>
+#include <boost/beast/core/detail/sha1.hpp>
+#include <boost/assert.hpp>
+#include <array>
+#include <cstdint>
+#include <string>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+using sec_ws_key_type = static_string<
+ beast::detail::base64::encoded_size(16)>;
+
+using sec_ws_accept_type = static_string<
+ beast::detail::base64::encoded_size(20)>;
+
+template<class Gen>
+void
+make_sec_ws_key(sec_ws_key_type& key, Gen& g)
+{
+ char a[16];
+ for(int i = 0; i < 16; i += 4)
+ {
+ auto const v = g();
+ a[i ] = v & 0xff;
+ a[i+1] = (v >> 8) & 0xff;
+ a[i+2] = (v >> 16) & 0xff;
+ a[i+3] = (v >> 24) & 0xff;
+ }
+ key.resize(key.max_size());
+ key.resize(beast::detail::base64::encode(
+ key.data(), &a[0], 16));
+}
+
+template<class = void>
+void
+make_sec_ws_accept(sec_ws_accept_type& accept,
+ string_view key)
+{
+ BOOST_ASSERT(key.size() <= sec_ws_key_type::max_size_n);
+ static_string<sec_ws_key_type::max_size_n + 36> m(key);
+ m.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ beast::detail::sha1_context ctx;
+ beast::detail::init(ctx);
+ beast::detail::update(ctx, m.data(), m.size());
+ char digest[beast::detail::sha1_context::digest_size];
+ beast::detail::finish(ctx, &digest[0]);
+ accept.resize(accept.max_size());
+ accept.resize(beast::detail::base64::encode(
+ accept.data(), &digest[0], sizeof(digest)));
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/mask.hpp b/boost/beast/websocket/detail/mask.hpp
new file mode 100644
index 0000000000..92ea0a4238
--- /dev/null
+++ b/boost/beast/websocket/detail/mask.hpp
@@ -0,0 +1,267 @@
+//
+// 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_DETAIL_MASK_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_MASK_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/buffer.hpp>
+#include <array>
+#include <climits>
+#include <cstdint>
+#include <random>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+// Pseudo-random source of mask keys
+//
+template<class Generator>
+class maskgen_t
+{
+ Generator g_;
+
+public:
+ using result_type =
+ typename Generator::result_type;
+
+ maskgen_t();
+
+ result_type
+ operator()() noexcept;
+
+ void
+ rekey();
+};
+
+template<class Generator>
+maskgen_t<Generator>::maskgen_t()
+{
+ rekey();
+}
+
+template<class Generator>
+auto
+maskgen_t<Generator>::operator()() noexcept ->
+ result_type
+{
+ for(;;)
+ if(auto key = g_())
+ return key;
+}
+
+template<class _>
+void
+maskgen_t<_>::rekey()
+{
+ std::random_device rng;
+#if 0
+ std::array<std::uint32_t, 32> e;
+ for(auto& i : e)
+ i = rng();
+ // VFALCO This constructor causes
+ // address sanitizer to fail, no idea why.
+ std::seed_seq ss(e.begin(), e.end());
+ g_.seed(ss);
+#else
+ g_.seed(rng());
+#endif
+}
+
+// VFALCO NOTE This generator has 5KB of state!
+//using maskgen = maskgen_t<std::mt19937>;
+using maskgen = maskgen_t<std::minstd_rand>;
+
+//------------------------------------------------------------------------------
+
+using prepared_key =
+ std::conditional<sizeof(void*) == 8,
+ std::uint64_t, std::uint32_t>::type;
+
+inline
+void
+prepare_key(std::uint32_t& prepared, std::uint32_t key)
+{
+ prepared = key;
+}
+
+inline
+void
+prepare_key(std::uint64_t& prepared, std::uint32_t key)
+{
+ prepared =
+ (static_cast<std::uint64_t>(key) << 32) | key;
+}
+
+template<class T>
+inline
+typename std::enable_if<std::is_integral<T>::value, T>::type
+ror(T t, unsigned n = 1)
+{
+ auto constexpr bits =
+ static_cast<unsigned>(
+ sizeof(T) * CHAR_BIT);
+ n &= bits-1;
+ return static_cast<T>((t << (bits - n)) | (
+ static_cast<typename std::make_unsigned<T>::type>(t) >> n));
+}
+
+// 32-bit optimized
+//
+template<class = void>
+void
+mask_inplace_fast(
+ boost::asio::mutable_buffer const& b,
+ std::uint32_t& key)
+{
+ auto n = b.size();
+ auto p = reinterpret_cast<std::uint8_t*>(b.data());
+ if(n >= sizeof(key))
+ {
+ // Bring p to 4-byte alignment
+ auto const i = reinterpret_cast<
+ std::uintptr_t>(p) & (sizeof(key)-1);
+ switch(i)
+ {
+ case 1: p[2] ^= static_cast<std::uint8_t>(key >> 16); BOOST_BEAST_FALLTHROUGH;
+ case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8); BOOST_BEAST_FALLTHROUGH;
+ case 3: p[0] ^= static_cast<std::uint8_t>(key);
+ {
+ auto const d = static_cast<unsigned>(sizeof(key) - i);
+ key = ror(key, 8*d);
+ n -= d;
+ p += d;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Mask 4 bytes at a time
+ for(auto i = n / sizeof(key); i; --i)
+ {
+ *reinterpret_cast<
+ std::uint32_t*>(p) ^= key;
+ p += sizeof(key);
+ }
+
+ // Leftovers
+ n &= sizeof(key)-1;
+ switch(n)
+ {
+ case 3: p[2] ^= static_cast<std::uint8_t>(key >> 16); BOOST_BEAST_FALLTHROUGH;
+ case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8); BOOST_BEAST_FALLTHROUGH;
+ case 1: p[0] ^= static_cast<std::uint8_t>(key);
+ key = ror(key, static_cast<unsigned>(8*n));
+ BOOST_BEAST_FALLTHROUGH;
+ default:
+ break;
+ }
+}
+
+// 64-bit optimized
+//
+template<class = void>
+void
+mask_inplace_fast(
+ boost::asio::mutable_buffer const& b,
+ std::uint64_t& key)
+{
+ auto n = b.size();
+ auto p = reinterpret_cast<std::uint8_t*>(b.data());
+ if(n >= sizeof(key))
+ {
+ // Bring p to 8-byte alignment
+ auto const i = reinterpret_cast<
+ std::uintptr_t>(p) & (sizeof(key)-1);
+ switch(i)
+ {
+ case 1: p[6] ^= static_cast<std::uint8_t>(key >> 48);
+ case 2: p[5] ^= static_cast<std::uint8_t>(key >> 40);
+ case 3: p[4] ^= static_cast<std::uint8_t>(key >> 32);
+ case 4: p[3] ^= static_cast<std::uint8_t>(key >> 24);
+ case 5: p[2] ^= static_cast<std::uint8_t>(key >> 16);
+ case 6: p[1] ^= static_cast<std::uint8_t>(key >> 8);
+ case 7: p[0] ^= static_cast<std::uint8_t>(key);
+ {
+ auto const d = static_cast<
+ unsigned>(sizeof(key) - i);
+ key = ror(key, 8*d);
+ n -= d;
+ p += d;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Mask 8 bytes at a time
+ for(auto i = n / sizeof(key); i; --i)
+ {
+ *reinterpret_cast<
+ std::uint64_t*>(p) ^= key;
+ p += sizeof(key);
+ }
+
+ // Leftovers
+ n &= sizeof(key)-1;
+ switch(n)
+ {
+ case 7: p[6] ^= static_cast<std::uint8_t>(key >> 48);
+ case 6: p[5] ^= static_cast<std::uint8_t>(key >> 40);
+ case 5: p[4] ^= static_cast<std::uint8_t>(key >> 32);
+ case 4: p[3] ^= static_cast<std::uint8_t>(key >> 24);
+ case 3: p[2] ^= static_cast<std::uint8_t>(key >> 16);
+ case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8);
+ case 1: p[0] ^= static_cast<std::uint8_t>(key);
+ key = ror(key, static_cast<unsigned>(8*n));
+ default:
+ break;
+ }
+}
+
+inline
+void
+mask_inplace(
+ boost::asio::mutable_buffer const& b,
+ std::uint32_t& key)
+{
+ mask_inplace_fast(b, key);
+}
+
+inline
+void
+mask_inplace(
+ boost::asio::mutable_buffer const& b,
+ std::uint64_t& key)
+{
+ mask_inplace_fast(b, key);
+}
+
+// Apply mask in place
+//
+template<class MutableBuffers, class KeyType>
+void
+mask_inplace(
+ MutableBuffers const& bs, KeyType& key)
+{
+ for(boost::asio::mutable_buffer b : bs)
+ mask_inplace(b, key);
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/pausation.hpp b/boost/beast/websocket/detail/pausation.hpp
new file mode 100644
index 0000000000..f51ee10327
--- /dev/null
+++ b/boost/beast/websocket/detail/pausation.hpp
@@ -0,0 +1,229 @@
+//
+// 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_DETAIL_PAUSATION_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_PAUSATION_HPP
+
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/assert.hpp>
+#include <array>
+#include <memory>
+#include <new>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+// A container that holds a suspended, asynchronous composed
+// operation. The contained object may be invoked later to
+// resume the operation, or the container may be destroyed.
+//
+class pausation
+{
+ struct base
+ {
+ base() = default;
+ base(base &&) = delete;
+ base(base const&) = delete;
+ virtual ~base() = default;
+ virtual void operator()() = 0;
+ };
+
+ template<class F>
+ struct holder : base
+ {
+ F f;
+
+ holder(holder&&) = default;
+
+ template<class U>
+ explicit
+ holder(U&& u)
+ : f(std::forward<U>(u))
+ {
+ }
+
+ void
+ operator()() override
+ {
+ F f_(std::move(f));
+ this->~holder();
+ // invocation of f_() can
+ // assign a new object to *this.
+ f_();
+ }
+ };
+
+ struct exemplar : boost::asio::coroutine
+ {
+ struct H
+ {
+ void operator()();
+ };
+
+ struct T
+ {
+ using handler_type = H;
+ };
+
+ handler_ptr<T, H> hp;
+
+ void operator()();
+ };
+
+ template<class Op>
+ class saved_op
+ {
+ Op* op_ = nullptr;
+
+ public:
+ ~saved_op()
+ {
+ if(op_)
+ {
+ Op op(std::move(*op_));
+ op_->~Op();
+ typename std::allocator_traits<
+ boost::asio::associated_allocator_t<Op>>::
+ template rebind_alloc<Op> alloc{
+ boost::asio::get_associated_allocator(op)};
+ std::allocator_traits<
+ decltype(alloc)>::deallocate(alloc, op_, 1);
+ }
+ }
+
+ saved_op(saved_op&& other)
+ : op_(other.op_)
+ {
+ other.op_ = nullptr;
+ }
+
+ saved_op& operator=(saved_op&& other)
+ {
+ BOOST_ASSERT(! op_);
+ op_ = other.op_;
+ other.op_ = 0;
+ return *this;
+ }
+
+ explicit
+ saved_op(Op&& op)
+ {
+ typename std::allocator_traits<
+ boost::asio::associated_allocator_t<Op>>::
+ template rebind_alloc<Op> alloc{
+ boost::asio::get_associated_allocator(op)};
+ auto const p = std::allocator_traits<
+ decltype(alloc)>::allocate(alloc, 1);
+ op_ = new(p) Op{std::move(op)};
+ }
+
+ void
+ operator()()
+ {
+ BOOST_ASSERT(op_);
+ Op op{std::move(*op_)};
+ typename std::allocator_traits<
+ boost::asio::associated_allocator_t<Op>>::
+ template rebind_alloc<Op> alloc{
+ boost::asio::get_associated_allocator(op)};
+ std::allocator_traits<
+ decltype(alloc)>::deallocate(alloc, op_, 1);
+ op_ = nullptr;
+ op();
+ }
+ };
+
+ using buf_type = char[sizeof(holder<exemplar>)];
+
+ base* base_ = nullptr;
+ alignas(holder<exemplar>) buf_type buf_;
+
+public:
+ pausation() = default;
+ pausation(pausation const&) = delete;
+ pausation& operator=(pausation const&) = delete;
+
+ ~pausation()
+ {
+ if(base_)
+ base_->~base();
+ }
+
+ pausation(pausation&& other)
+ {
+ boost::ignore_unused(other);
+ BOOST_ASSERT(! other.base_);
+ }
+
+ pausation&
+ operator=(pausation&& other)
+ {
+ boost::ignore_unused(other);
+ BOOST_ASSERT(! base_);
+ BOOST_ASSERT(! other.base_);
+ return *this;
+ }
+
+ template<class F>
+ void
+ emplace(F&& f);
+
+ template<class F>
+ void
+ save(F&& f);
+
+ explicit
+ operator bool() const
+ {
+ return base_ != nullptr;
+ }
+
+ bool
+ maybe_invoke()
+ {
+ if(base_)
+ {
+ auto const basep = base_;
+ base_ = nullptr;
+ (*basep)();
+ return true;
+ }
+ return false;
+ }
+};
+
+template<class F>
+void
+pausation::emplace(F&& f)
+{
+ using type = holder<typename std::decay<F>::type>;
+ static_assert(sizeof(buf_type) >= sizeof(type),
+ "buffer too small");
+ BOOST_ASSERT(! base_);
+ base_ = ::new(buf_) type{std::forward<F>(f)};
+}
+
+template<class F>
+void
+pausation::save(F&& f)
+{
+ emplace(saved_op<F>{std::move(f)});
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/pmd_extension.hpp b/boost/beast/websocket/detail/pmd_extension.hpp
new file mode 100644
index 0000000000..a28b844cd7
--- /dev/null
+++ b/boost/beast/websocket/detail/pmd_extension.hpp
@@ -0,0 +1,442 @@
+//
+// 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_DETAIL_PMD_EXTENSION_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
+
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/read_size.hpp>
+#include <boost/beast/zlib/deflate_stream.hpp>
+#include <boost/beast/zlib/inflate_stream.hpp>
+#include <boost/beast/websocket/option.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#include <boost/asio/buffer.hpp>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+// permessage-deflate offer parameters
+//
+// "context takeover" means:
+// preserve sliding window across messages
+//
+struct pmd_offer
+{
+ bool accept;
+
+ // 0 = absent, or 8..15
+ int server_max_window_bits;
+
+ // -1 = present, 0 = absent, or 8..15
+ int client_max_window_bits;
+
+ // `true` if server_no_context_takeover offered
+ bool server_no_context_takeover;
+
+ // `true` if client_no_context_takeover offered
+ bool client_no_context_takeover;
+};
+
+template<class = void>
+int
+parse_bits(string_view s)
+{
+ if(s.size() == 0)
+ return -1;
+ if(s.size() > 2)
+ return -1;
+ if(s[0] < '1' || s[0] > '9')
+ return -1;
+ unsigned i = 0;
+ for(auto c : s)
+ {
+ if(c < '0' || c > '9')
+ return -1;
+ auto const i0 = i;
+ i = 10 * i + (c - '0');
+ if(i < i0)
+ return -1;
+ }
+ return static_cast<int>(i);
+}
+
+// Parse permessage-deflate request fields
+//
+template<class Allocator>
+void
+pmd_read(pmd_offer& offer,
+ http::basic_fields<Allocator> const& fields)
+{
+ offer.accept = false;
+ offer.server_max_window_bits= 0;
+ offer.client_max_window_bits = 0;
+ offer.server_no_context_takeover = false;
+ offer.client_no_context_takeover = false;
+
+ http::ext_list list{
+ fields["Sec-WebSocket-Extensions"]};
+ for(auto const& ext : list)
+ {
+ if(iequals(ext.first, "permessage-deflate"))
+ {
+ for(auto const& param : ext.second)
+ {
+ if(iequals(param.first,
+ "server_max_window_bits"))
+ {
+ if(offer.server_max_window_bits != 0)
+ {
+ // The negotiation offer contains multiple
+ // extension parameters with the same name.
+ //
+ return; // MUST decline
+ }
+ if(param.second.empty())
+ {
+ // The negotiation offer extension
+ // parameter is missing the value.
+ //
+ return; // MUST decline
+ }
+ offer.server_max_window_bits =
+ parse_bits(param.second);
+ if( offer.server_max_window_bits < 8 ||
+ offer.server_max_window_bits > 15)
+ {
+ // The negotiation offer contains an
+ // extension parameter with an invalid value.
+ //
+ return; // MUST decline
+ }
+ }
+ else if(iequals(param.first,
+ "client_max_window_bits"))
+ {
+ if(offer.client_max_window_bits != 0)
+ {
+ // The negotiation offer contains multiple
+ // extension parameters with the same name.
+ //
+ return; // MUST decline
+ }
+ if(! param.second.empty())
+ {
+ offer.client_max_window_bits =
+ parse_bits(param.second);
+ if( offer.client_max_window_bits < 8 ||
+ offer.client_max_window_bits > 15)
+ {
+ // The negotiation offer contains an
+ // extension parameter with an invalid value.
+ //
+ return; // MUST decline
+ }
+ }
+ else
+ {
+ offer.client_max_window_bits = -1;
+ }
+ }
+ else if(iequals(param.first,
+ "server_no_context_takeover"))
+ {
+ if(offer.server_no_context_takeover)
+ {
+ // The negotiation offer contains multiple
+ // extension parameters with the same name.
+ //
+ return; // MUST decline
+ }
+ if(! param.second.empty())
+ {
+ // The negotiation offer contains an
+ // extension parameter with an invalid value.
+ //
+ return; // MUST decline
+ }
+ offer.server_no_context_takeover = true;
+ }
+ else if(iequals(param.first,
+ "client_no_context_takeover"))
+ {
+ if(offer.client_no_context_takeover)
+ {
+ // The negotiation offer contains multiple
+ // extension parameters with the same name.
+ //
+ return; // MUST decline
+ }
+ if(! param.second.empty())
+ {
+ // The negotiation offer contains an
+ // extension parameter with an invalid value.
+ //
+ return; // MUST decline
+ }
+ offer.client_no_context_takeover = true;
+ }
+ else
+ {
+ // The negotiation offer contains an extension
+ // parameter not defined for use in an offer.
+ //
+ return; // MUST decline
+ }
+ }
+ offer.accept = true;
+ return;
+ }
+ }
+}
+
+// Set permessage-deflate fields for a client offer
+//
+template<class Allocator>
+void
+pmd_write(http::basic_fields<Allocator>& fields,
+ pmd_offer const& offer)
+{
+ static_string<512> s;
+ s = "permessage-deflate";
+ if(offer.server_max_window_bits != 0)
+ {
+ if(offer.server_max_window_bits != -1)
+ {
+ s += "; server_max_window_bits=";
+ s += to_static_string(
+ offer.server_max_window_bits);
+ }
+ else
+ {
+ s += "; server_max_window_bits";
+ }
+ }
+ if(offer.client_max_window_bits != 0)
+ {
+ if(offer.client_max_window_bits != -1)
+ {
+ s += "; client_max_window_bits=";
+ s += to_static_string(
+ offer.client_max_window_bits);
+ }
+ else
+ {
+ s += "; client_max_window_bits";
+ }
+ }
+ if(offer.server_no_context_takeover)
+ {
+ s += "; server_no_context_takeover";
+ }
+ if(offer.client_no_context_takeover)
+ {
+ s += "; client_no_context_takeover";
+ }
+ fields.set(http::field::sec_websocket_extensions, s);
+}
+
+// Negotiate a permessage-deflate client offer
+//
+template<class Allocator>
+void
+pmd_negotiate(
+ http::basic_fields<Allocator>& fields,
+ pmd_offer& config,
+ pmd_offer const& offer,
+ permessage_deflate const& o)
+{
+ if(! (offer.accept && o.server_enable))
+ {
+ config.accept = false;
+ return;
+ }
+ config.accept = true;
+
+ static_string<512> s = "permessage-deflate";
+
+ config.server_no_context_takeover =
+ offer.server_no_context_takeover ||
+ o.server_no_context_takeover;
+ if(config.server_no_context_takeover)
+ s += "; server_no_context_takeover";
+
+ config.client_no_context_takeover =
+ o.client_no_context_takeover ||
+ offer.client_no_context_takeover;
+ if(config.client_no_context_takeover)
+ s += "; client_no_context_takeover";
+
+ if(offer.server_max_window_bits != 0)
+ config.server_max_window_bits = (std::min)(
+ offer.server_max_window_bits,
+ o.server_max_window_bits);
+ else
+ config.server_max_window_bits =
+ o.server_max_window_bits;
+ if(config.server_max_window_bits < 15)
+ {
+ // ZLib's deflateInit silently treats 8 as
+ // 9 due to a bug, so prevent 8 from being used.
+ //
+ if(config.server_max_window_bits < 9)
+ config.server_max_window_bits = 9;
+
+ s += "; server_max_window_bits=";
+ s += to_static_string(
+ config.server_max_window_bits);
+ }
+
+ switch(offer.client_max_window_bits)
+ {
+ case -1:
+ // extension parameter is present with no value
+ config.client_max_window_bits =
+ o.client_max_window_bits;
+ if(config.client_max_window_bits < 15)
+ {
+ s += "; client_max_window_bits=";
+ s += to_static_string(
+ config.client_max_window_bits);
+ }
+ break;
+
+ case 0:
+ /* extension parameter is absent.
+
+ If a received extension negotiation offer doesn't have the
+ "client_max_window_bits" extension parameter, the corresponding
+ extension negotiation response to the offer MUST NOT include the
+ "client_max_window_bits" extension parameter.
+ */
+ if(o.client_max_window_bits == 15)
+ config.client_max_window_bits = 15;
+ else
+ config.accept = false;
+ break;
+
+ default:
+ // extension parameter has value in [8..15]
+ config.client_max_window_bits = (std::min)(
+ o.client_max_window_bits,
+ offer.client_max_window_bits);
+ s += "; client_max_window_bits=";
+ s += to_static_string(
+ config.client_max_window_bits);
+ break;
+ }
+ if(config.accept)
+ fields.set(http::field::sec_websocket_extensions, s);
+}
+
+// Normalize the server's response
+//
+inline
+void
+pmd_normalize(pmd_offer& offer)
+{
+ if(offer.accept)
+ {
+ if( offer.server_max_window_bits == 0)
+ offer.server_max_window_bits = 15;
+
+ if( offer.client_max_window_bits == 0 ||
+ offer.client_max_window_bits == -1)
+ offer.client_max_window_bits = 15;
+ }
+}
+
+//--------------------------------------------------------------------
+
+// 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
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/type_traits.hpp b/boost/beast/websocket/detail/type_traits.hpp
new file mode 100644
index 0000000000..6c2806146c
--- /dev/null
+++ b/boost/beast/websocket/detail/type_traits.hpp
@@ -0,0 +1,36 @@
+//
+// 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_DETAIL_TYPE_TRAITS_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_TYPE_TRAITS_HPP
+
+#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+template<class F>
+using is_RequestDecorator =
+ typename beast::detail::is_invocable<F,
+ void(request_type&)>::type;
+
+template<class F>
+using is_ResponseDecorator =
+ typename beast::detail::is_invocable<F,
+ void(response_type&)>::type;
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/detail/utf8_checker.hpp b/boost/beast/websocket/detail/utf8_checker.hpp
new file mode 100644
index 0000000000..e86310f051
--- /dev/null
+++ b/boost/beast/websocket/detail/utf8_checker.hpp
@@ -0,0 +1,344 @@
+//
+// 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_DETAIL_UTF8_CHECKER_HPP
+#define BOOST_BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
+
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/assert.hpp>
+#include <algorithm>
+#include <cstdint>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+namespace detail {
+
+/** A UTF8 validator.
+
+ This validator can be used to check if a buffer containing UTF8 text is
+ valid. The write function may be called incrementally with segmented UTF8
+ sequences. The finish function determines if all processed text is valid.
+*/
+template<class = void>
+class utf8_checker_t
+{
+ std::size_t need_ = 0; // chars we need to finish the code point
+ std::uint8_t* p_ = cp_; // current position in temp buffer
+ std::uint8_t cp_[4]; // a temp buffer for the code point
+
+public:
+ /** Prepare to process text as valid utf8
+ */
+ void
+ reset();
+
+ /** Check that all processed text is valid utf8
+ */
+ bool
+ finish();
+
+ /** Check if text is valid UTF8
+
+ @return `true` if the text is valid utf8 or false otherwise.
+ */
+ bool
+ write(std::uint8_t const* in, std::size_t size);
+
+ /** Check if text is valid UTF8
+
+ @return `true` if the text is valid utf8 or false otherwise.
+ */
+ template<class ConstBufferSequence>
+ bool
+ write(ConstBufferSequence const& bs);
+};
+
+template<class _>
+void
+utf8_checker_t<_>::
+reset()
+{
+ need_ = 0;
+ p_ = cp_;
+}
+
+template<class _>
+bool
+utf8_checker_t<_>::
+finish()
+{
+ auto const success = need_ == 0;
+ reset();
+ return success;
+}
+
+template<class _>
+template<class ConstBufferSequence>
+bool
+utf8_checker_t<_>::
+write(ConstBufferSequence const& bs)
+{
+ static_assert(boost::asio::is_const_buffer_sequence<ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ for(auto b : beast::detail::buffers_range(bs))
+ if(! write(reinterpret_cast<
+ std::uint8_t const*>(b.data()),
+ b.size()))
+ return false;
+ return true;
+}
+
+template<class _>
+bool
+utf8_checker_t<_>::
+write(std::uint8_t const* in, std::size_t size)
+{
+ auto const valid =
+ [](std::uint8_t const*& p)
+ {
+ if(p[0] < 128)
+ {
+ ++p;
+ return true;
+ }
+ if((p[0] & 0xe0) == 0xc0)
+ {
+ if( (p[1] & 0xc0) != 0x80 ||
+ (p[0] & 0xfe) == 0xc0) // overlong
+ return false;
+ p += 2;
+ return true;
+ }
+ if((p[0] & 0xf0) == 0xe0)
+ {
+ if( (p[1] & 0xc0) != 0x80
+ || (p[2] & 0xc0) != 0x80
+ || (p[0] == 0xe0 && (p[1] & 0xe0) == 0x80) // overlong
+ || (p[0] == 0xed && (p[1] & 0xe0) == 0xa0) // surrogate
+ //|| (p[0] == 0xef && p[1] == 0xbf && (p[2] & 0xfe) == 0xbe) // U+FFFE or U+FFFF
+ )
+ return false;
+ p += 3;
+ return true;
+ }
+ if((p[0] & 0xf8) == 0xf0)
+ {
+ if( (p[1] & 0xc0) != 0x80
+ || (p[2] & 0xc0) != 0x80
+ || (p[3] & 0xc0) != 0x80
+ || (p[0] == 0xf0 && (p[1] & 0xf0) == 0x80) // overlong
+ || (p[0] == 0xf4 && p[1] > 0x8f) || p[0] > 0xf4 // > U+10FFFF
+ )
+ return false;
+ p += 4;
+ return true;
+ }
+ return false;
+ };
+ auto const fail_fast =
+ [&]()
+ {
+ auto const n = p_ - cp_;
+ switch(n)
+ {
+ default:
+ BOOST_ASSERT(false);
+ BOOST_BEAST_FALLTHROUGH;
+ case 1:
+ cp_[1] = 0x81;
+ BOOST_BEAST_FALLTHROUGH;
+ case 2:
+ cp_[2] = 0x81;
+ BOOST_BEAST_FALLTHROUGH;
+ case 3:
+ cp_[3] = 0x81;
+ break;
+ }
+ std::uint8_t const* p = cp_;
+ return ! valid(p);
+ };
+ auto const needed =
+ [](std::uint8_t const v)
+ {
+ if(v < 128)
+ return 1;
+ if(v < 192)
+ return 0;
+ if(v < 224)
+ return 2;
+ if(v < 240)
+ return 3;
+ if(v < 248)
+ return 4;
+ return 0;
+ };
+
+ auto const end = in + size;
+
+ // Finish up any incomplete code point
+ if(need_ > 0)
+ {
+ // Calculate what we have
+ auto n = (std::min)(size, need_);
+ size -= n;
+ need_ -= n;
+
+ // Add characters to the code point
+ while(n--)
+ *p_++ = *in++;
+ BOOST_ASSERT(p_ <= cp_ + 5);
+
+ // Still incomplete?
+ if(need_ > 0)
+ {
+ // Incomplete code point
+ BOOST_ASSERT(in == end);
+
+ // Do partial validation on the incomplete
+ // code point, this is called "Fail fast"
+ // in Autobahn|Testsuite parlance.
+ return ! fail_fast();
+ }
+
+ // Complete code point, validate it
+ std::uint8_t const* p = &cp_[0];
+ if(! valid(p))
+ return false;
+ p_ = cp_;
+ }
+
+ if(size <= sizeof(std::size_t))
+ goto slow;
+
+ // Align `in` to sizeof(std::size_t) boundary
+ {
+ auto const in0 = in;
+ auto last = reinterpret_cast<std::uint8_t const*>(
+ ((reinterpret_cast<std::uintptr_t>(in) + sizeof(std::size_t) - 1) /
+ sizeof(std::size_t)) * sizeof(std::size_t));
+
+ // Check one character at a time for low-ASCII
+ while(in < last)
+ {
+ if(*in & 0x80)
+ {
+ // Not low-ASCII so switch to slow loop
+ size = size - (in - in0);
+ goto slow;
+ }
+ ++in;
+ }
+ size = size - (in - in0);
+ }
+
+ // Fast loop: Process 4 or 8 low-ASCII characters at a time
+ {
+ auto const in0 = in;
+ auto last = in + size - 7;
+ auto constexpr mask = static_cast<
+ std::size_t>(0x8080808080808080 & ~std::size_t{0});
+ while(in < last)
+ {
+#if 0
+ std::size_t temp;
+ std::memcpy(&temp, in, sizeof(temp));
+ if((temp & mask) != 0)
+#else
+ // Technically UB but works on all known platforms
+ if((*reinterpret_cast<std::size_t const*>(in) & mask) != 0)
+#endif
+ {
+ size = size - (in - in0);
+ goto slow;
+ }
+ in += sizeof(std::size_t);
+ }
+ // There's at least one more full code point left
+ last += 4;
+ while(in < last)
+ if(! valid(in))
+ return false;
+ goto tail;
+ }
+
+slow:
+ // Slow loop: Full validation on one code point at a time
+ {
+ auto last = in + size - 3;
+ while(in < last)
+ if(! valid(in))
+ return false;
+ }
+
+tail:
+ // Handle the remaining bytes. The last
+ // characters could split a code point so
+ // we save the partial code point for later.
+ //
+ // On entry to the loop, `in` points to the
+ // beginning of a code point.
+ //
+ for(;;)
+ {
+ // Number of chars left
+ auto n = end - in;
+ if(! n)
+ break;
+
+ // Chars we need to finish this code point
+ auto const need = needed(*in);
+ if(need == 0)
+ return false;
+ if(need <= n)
+ {
+ // Check a whole code point
+ if(! valid(in))
+ return false;
+ }
+ else
+ {
+ // Calculate how many chars we need
+ // to finish this partial code point
+ need_ = need - n;
+
+ // Save the partial code point
+ while(n--)
+ *p_++ = *in++;
+ BOOST_ASSERT(in == end);
+ BOOST_ASSERT(p_ <= cp_ + 5);
+
+ // Do partial validation on the incomplete
+ // code point, this is called "Fail fast"
+ // in Autobahn|Testsuite parlance.
+ return ! fail_fast();
+ }
+ }
+ return true;
+}
+
+using utf8_checker = utf8_checker_t<>;
+
+template<class = void>
+bool
+check_utf8(char const* p, std::size_t n)
+{
+ utf8_checker c;
+ if(! c.write(reinterpret_cast<const uint8_t*>(p), n))
+ return false;
+ return c.finish();
+}
+
+} // detail
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/error.hpp b/boost/beast/websocket/error.hpp
new file mode 100644
index 0000000000..139a20879d
--- /dev/null
+++ b/boost/beast/websocket/error.hpp
@@ -0,0 +1,45 @@
+//
+// 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_ERROR_HPP
+#define BOOST_BEAST_WEBSOCKET_ERROR_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/// Error codes returned from @ref beast::websocket::stream operations.
+enum class error
+{
+ /// Both sides performed a WebSocket close
+ closed = 1,
+
+ /// WebSocket connection failed, protocol violation
+ failed,
+
+ /// Upgrade handshake failed
+ handshake_failed,
+
+ /// buffer overflow
+ buffer_overflow,
+
+ /// partial deflate block
+ partial_deflate_block
+};
+
+} // websocket
+} // beast
+} // boost
+
+#include <boost/beast/websocket/impl/error.ipp>
+
+#endif
diff --git a/boost/beast/websocket/impl/accept.ipp b/boost/beast/websocket/impl/accept.ipp
new file mode 100644
index 0000000000..e52a74efa5
--- /dev/null
+++ b/boost/beast/websocket/impl/accept.ipp
@@ -0,0 +1,757 @@
+//
+// 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_IMPL_ACCEPT_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
+
+#include <boost/beast/websocket/detail/type_traits.hpp>
+#include <boost/beast/http/empty_body.hpp>
+#include <boost/beast/http/parser.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/http/string_body.hpp>
+#include <boost/beast/http/write.hpp>
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+// Respond to an upgrade HTTP request
+template<class NextLayer>
+template<class Handler>
+class stream<NextLayer>::response_op
+ : public boost::asio::coroutine
+{
+ struct data
+ {
+ stream<NextLayer>& ws;
+ response_type res;
+
+ template<class Body, class Allocator, class Decorator>
+ data(Handler&, stream<NextLayer>& ws_, http::request<
+ Body, http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator)
+ : ws(ws_)
+ , res(ws_.build_response(req, decorator))
+ {
+ }
+ };
+
+ handler_ptr<data, Handler> d_;
+
+public:
+ response_op(response_op&&) = default;
+ response_op(response_op const&) = default;
+
+ template<class DeducedHandler, class... Args>
+ response_op(DeducedHandler&& h,
+ stream<NextLayer>& ws, Args&&... args)
+ : d_(std::forward<DeducedHandler>(h),
+ ws, std::forward<Args>(args)...)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->ws.get_executor());
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(response_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class NextLayer>
+template<class Handler>
+void
+stream<NextLayer>::
+response_op<Handler>::
+operator()(
+ error_code ec,
+ std::size_t)
+{
+ auto& d = *d_;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Send response
+ BOOST_ASIO_CORO_YIELD
+ http::async_write(d.ws.next_layer(),
+ d.res, std::move(*this));
+ if(! ec && d.res.result() !=
+ http::status::switching_protocols)
+ ec = error::handshake_failed;
+ if(! ec)
+ {
+ pmd_read(d.ws.pmd_config_, d.res);
+ d.ws.open(role_type::server);
+ }
+ d_.invoke(ec);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+// read and respond to an upgrade request
+//
+template<class NextLayer>
+template<class Decorator, class Handler>
+class stream<NextLayer>::accept_op
+ : public boost::asio::coroutine
+{
+ struct data
+ {
+ stream<NextLayer>& ws;
+ Decorator decorator;
+ http::request_parser<http::empty_body> p;
+ data(Handler&, stream<NextLayer>& ws_,
+ Decorator const& decorator_)
+ : ws(ws_)
+ , decorator(decorator_)
+ {
+ }
+ };
+
+ handler_ptr<data, Handler> d_;
+
+public:
+ accept_op(accept_op&&) = default;
+ accept_op(accept_op const&) = default;
+
+ template<class DeducedHandler, class... Args>
+ accept_op(DeducedHandler&& h,
+ stream<NextLayer>& ws, Args&&... args)
+ : d_(std::forward<DeducedHandler>(h),
+ ws, std::forward<Args>(args)...)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->ws.get_executor());
+ }
+
+ template<class Buffers>
+ void run(Buffers const& buffers);
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_used = 0);
+
+ friend
+ bool asio_handler_is_continuation(accept_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class NextLayer>
+template<class Decorator, class Handler>
+template<class Buffers>
+void
+stream<NextLayer>::
+accept_op<Decorator, Handler>::
+run(Buffers const& buffers)
+{
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ auto& d = *d_;
+ error_code ec;
+ boost::optional<typename
+ static_buffer_base::mutable_buffers_type> mb;
+ auto const len = buffer_size(buffers);
+ try
+ {
+ mb.emplace(d.ws.rd_buf_.prepare(len));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ return (*this)(ec);
+ }
+ d.ws.rd_buf_.commit(
+ buffer_copy(*mb, buffers));
+ (*this)(ec);
+}
+
+template<class NextLayer>
+template<class Decorator, class Handler>
+void
+stream<NextLayer>::
+accept_op<Decorator, Handler>::
+operator()(error_code ec, std::size_t)
+{
+ auto& d = *d_;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ if(ec)
+ {
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ d.ws.get_executor(),
+ bind_handler(std::move(*this), ec));
+ }
+ else
+ {
+ BOOST_ASIO_CORO_YIELD
+ http::async_read(
+ d.ws.next_layer(), d.ws.rd_buf_,
+ d.p, std::move(*this));
+ if(ec == http::error::end_of_stream)
+ ec = error::closed;
+ if(! ec)
+ {
+ // Arguments from our state must be
+ // moved to the stack before releasing
+ // the handler.
+ auto& ws = d.ws;
+ auto const req = d.p.release();
+ auto const decorator = d.decorator;
+ #if 1
+ return response_op<Handler>{
+ d_.release_handler(),
+ ws, req, decorator}(ec);
+ #else
+ // VFALCO This *should* work but breaks
+ // coroutine invariants in the unit test.
+ // Also it calls reset() when it shouldn't.
+ return ws.async_accept_ex(
+ req, decorator, d_.release_handler());
+ #endif
+ }
+ }
+ d_.invoke(ec);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+accept()
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ error_code ec;
+ accept(ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+template<class ResponseDecorator>
+void
+stream<NextLayer>::
+accept_ex(ResponseDecorator const& decorator)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(detail::is_ResponseDecorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ error_code ec;
+ accept_ex(decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+accept(error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ reset();
+ do_accept(&default_decorate_res, ec);
+}
+
+template<class NextLayer>
+template<class ResponseDecorator>
+void
+stream<NextLayer>::
+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<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ reset();
+ do_accept(decorator, ec);
+}
+
+template<class NextLayer>
+template<class ConstBufferSequence>
+typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+stream<NextLayer>::
+accept(ConstBufferSequence const& buffers)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ error_code ec;
+ accept(buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+template<
+ class ConstBufferSequence,
+ class ResponseDecorator>
+typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+stream<NextLayer>::
+accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const &decorator)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ static_assert(detail::is_ResponseDecorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ error_code ec;
+ accept_ex(buffers, decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+template<class ConstBufferSequence>
+typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+stream<NextLayer>::
+accept(
+ ConstBufferSequence const& buffers, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ reset();
+ boost::optional<typename
+ static_buffer_base::mutable_buffers_type> mb;
+ try
+ {
+ mb.emplace(rd_buf_.prepare(
+ buffer_size(buffers)));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ return;
+ }
+ rd_buf_.commit(
+ buffer_copy(*mb, buffers));
+ do_accept(&default_decorate_res, ec);
+}
+
+template<class NextLayer>
+template<
+ class ConstBufferSequence,
+ class ResponseDecorator>
+typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+stream<NextLayer>::
+accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ reset();
+ boost::optional<typename
+ static_buffer_base::mutable_buffers_type> mb;
+ try
+ {
+ mb.emplace(rd_buf_.prepare(
+ buffer_size(buffers)));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ return;
+ }
+ rd_buf_.commit(buffer_copy(*mb, buffers));
+ do_accept(decorator, ec);
+}
+
+template<class NextLayer>
+template<class Body, class Allocator>
+void
+stream<NextLayer>::
+accept(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ error_code ec;
+ accept(req, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+template<
+ class Body, class Allocator,
+ class ResponseDecorator>
+void
+stream<NextLayer>::
+accept_ex(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(detail::is_ResponseDecorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ error_code ec;
+ accept_ex(req, decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+template<class Body, class Allocator>
+void
+stream<NextLayer>::
+accept(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ reset();
+ do_accept(req, &default_decorate_res, ec);
+}
+
+template<class NextLayer>
+template<
+ class Body, class Allocator,
+ class ResponseDecorator>
+void
+stream<NextLayer>::
+accept_ex(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(detail::is_ResponseDecorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ reset();
+ do_accept(req, decorator, ec);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<
+ class AcceptHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))
+stream<NextLayer>::
+async_accept(
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ boost::asio::async_completion<AcceptHandler,
+ void(error_code)> init{handler};
+ reset();
+ accept_op<
+ decltype(&default_decorate_res),
+ BOOST_ASIO_HANDLER_TYPE(
+ AcceptHandler, void(error_code))>{
+ init.completion_handler,
+ *this,
+ &default_decorate_res}({});
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<
+ class ResponseDecorator,
+ class AcceptHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))
+stream<NextLayer>::
+async_accept_ex(
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ static_assert(detail::is_ResponseDecorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ boost::asio::async_completion<AcceptHandler,
+ void(error_code)> init{handler};
+ reset();
+ accept_op<
+ ResponseDecorator,
+ BOOST_ASIO_HANDLER_TYPE(
+ AcceptHandler, void(error_code))>{
+ init.completion_handler,
+ *this,
+ decorator}({});
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<
+ class ConstBufferSequence,
+ class AcceptHandler>
+typename std::enable_if<
+ ! http::detail::is_header<ConstBufferSequence>::value,
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))>::type
+stream<NextLayer>::
+async_accept(
+ ConstBufferSequence const& buffers,
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ boost::asio::async_completion<AcceptHandler,
+ void(error_code)> init{handler};
+ reset();
+ accept_op<
+ decltype(&default_decorate_res),
+ BOOST_ASIO_HANDLER_TYPE(
+ AcceptHandler, void(error_code))>{
+ init.completion_handler,
+ *this,
+ &default_decorate_res}.run(buffers);
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<
+ class ConstBufferSequence,
+ class ResponseDecorator,
+ class AcceptHandler>
+typename std::enable_if<
+ ! http::detail::is_header<ConstBufferSequence>::value,
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))>::type
+stream<NextLayer>::
+async_accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ static_assert(detail::is_ResponseDecorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ boost::asio::async_completion<AcceptHandler,
+ void(error_code)> init{handler};
+ reset();
+ accept_op<
+ ResponseDecorator,
+ BOOST_ASIO_HANDLER_TYPE(
+ AcceptHandler, void(error_code))>{
+ init.completion_handler,
+ *this,
+ decorator}.run(buffers);
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<
+ class Body, class Allocator,
+ class AcceptHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))
+stream<NextLayer>::
+async_accept(
+ http::request<Body, http::basic_fields<Allocator>> const& req,
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ boost::asio::async_completion<AcceptHandler,
+ void(error_code)> init{handler};
+ reset();
+ using boost::asio::asio_handler_is_continuation;
+ response_op<
+ BOOST_ASIO_HANDLER_TYPE(
+ AcceptHandler, void(error_code))>{
+ init.completion_handler,
+ *this,
+ req,
+ &default_decorate_res}();
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<
+ class Body, class Allocator,
+ class ResponseDecorator,
+ class AcceptHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))
+stream<NextLayer>::
+async_accept_ex(
+ http::request<Body, http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ static_assert(detail::is_ResponseDecorator<
+ ResponseDecorator>::value,
+ "ResponseDecorator requirements not met");
+ boost::asio::async_completion<AcceptHandler,
+ void(error_code)> init{handler};
+ reset();
+ using boost::asio::asio_handler_is_continuation;
+ response_op<
+ BOOST_ASIO_HANDLER_TYPE(
+ AcceptHandler, void(error_code))>{
+ init.completion_handler,
+ *this,
+ req,
+ decorator}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<class Decorator>
+void
+stream<NextLayer>::
+do_accept(
+ Decorator const& decorator,
+ error_code& ec)
+{
+ http::request_parser<http::empty_body> p;
+ http::read(next_layer(), rd_buf_, p, ec);
+ if(ec == http::error::end_of_stream)
+ ec = error::closed;
+ if(ec)
+ return;
+ do_accept(p.get(), decorator, ec);
+}
+
+template<class NextLayer>
+template<class Body, class Allocator,
+ class Decorator>
+void
+stream<NextLayer>::
+do_accept(
+ http::request<Body,http::basic_fields<Allocator>> const& req,
+ Decorator const& decorator,
+ error_code& ec)
+{
+ auto const res = build_response(req, decorator);
+ http::write(stream_, res, ec);
+ if(ec)
+ return;
+ if(res.result() != http::status::switching_protocols)
+ {
+ ec = error::handshake_failed;
+ // VFALCO TODO Respect keep alive setting, perform
+ // teardown if Connection: close.
+ return;
+ }
+ pmd_read(pmd_config_, res);
+ open(role_type::server);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/close.ipp b/boost/beast/websocket/impl/close.ipp
new file mode 100644
index 0000000000..0349dd1409
--- /dev/null
+++ b/boost/beast/websocket/impl/close.ipp
@@ -0,0 +1,459 @@
+//
+// 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_IMPL_CLOSE_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_CLOSE_IPP
+
+#include <boost/beast/websocket/teardown.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/* Close the WebSocket Connection
+
+ This composed operation sends the close frame if it hasn't already
+ been sent, then reads and discards frames until receiving a close
+ frame. Finally it invokes the teardown operation to shut down the
+ underlying connection.
+*/
+template<class NextLayer>
+template<class Handler>
+class stream<NextLayer>::close_op
+ : public boost::asio::coroutine
+{
+ struct state
+ {
+ stream<NextLayer>& ws;
+ detail::frame_buffer fb;
+ error_code ev;
+ token tok;
+ bool cont = false;
+
+ state(
+ Handler&,
+ stream<NextLayer>& ws_,
+ close_reason const& cr)
+ : ws(ws_)
+ , tok(ws.tok_.unique())
+ {
+ // Serialize the close frame
+ ws.template write_close<
+ flat_static_buffer_base>(fb, cr);
+ }
+ };
+
+ handler_ptr<state, Handler> d_;
+
+public:
+ close_op(close_op&&) = default;
+ close_op(close_op const&) = default;
+
+ template<class DeducedHandler>
+ close_op(
+ DeducedHandler&& h,
+ stream<NextLayer>& ws,
+ close_reason const& cr)
+ : d_(std::forward<DeducedHandler>(h), ws, cr)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->ws.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true);
+
+ friend
+ bool asio_handler_is_continuation(close_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->d_->cont || asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class NextLayer>
+template<class Handler>
+void
+stream<NextLayer>::
+close_op<Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred,
+ bool cont)
+{
+ using beast::detail::clamp;
+ auto& d = *d_;
+ close_code code{};
+ d.cont = cont;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Maybe suspend
+ if(! d.ws.wr_block_)
+ {
+ // Acquire the write block
+ d.ws.wr_block_ = d.tok;
+
+ // Make sure the stream is open
+ if(! d.ws.check_open(ec))
+ goto upcall;
+ }
+ else
+ {
+ // Suspend
+ BOOST_ASSERT(d.ws.wr_block_ != d.tok);
+ BOOST_ASIO_CORO_YIELD
+ d.ws.paused_close_.emplace(std::move(*this));
+
+ // Acquire the write block
+ BOOST_ASSERT(! d.ws.wr_block_);
+ d.ws.wr_block_ = d.tok;
+
+ // Resume
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ d.ws.get_executor(), std::move(*this));
+ BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+
+ // Make sure the stream is open
+ if(! d.ws.check_open(ec))
+ goto upcall;
+ }
+
+ // Can't call close twice
+ BOOST_ASSERT(! d.ws.wr_close_);
+
+ // Change status to closing
+ BOOST_ASSERT(d.ws.status_ == status::open);
+ d.ws.status_ = status::closing;
+
+ // Send close frame
+ d.ws.wr_close_ = true;
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(d.ws.stream_,
+ d.fb.data(), std::move(*this));
+ if(! d.ws.check_ok(ec))
+ goto upcall;
+
+ if(d.ws.rd_close_)
+ {
+ // This happens when the read_op gets a close frame
+ // at the same time close_op is sending the close frame.
+ // The read_op will be suspended on the write block.
+ goto teardown;
+ }
+
+ // Maybe suspend
+ if(! d.ws.rd_block_)
+ {
+ // Acquire the read block
+ d.ws.rd_block_ = d.tok;
+ }
+ else
+ {
+ // Suspend
+ BOOST_ASSERT(d.ws.rd_block_ != d.tok);
+ BOOST_ASIO_CORO_YIELD
+ d.ws.paused_r_close_.emplace(std::move(*this));
+
+ // Acquire the read block
+ BOOST_ASSERT(! d.ws.rd_block_);
+ d.ws.rd_block_ = d.tok;
+
+ // Resume
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ d.ws.get_executor(), std::move(*this));
+ BOOST_ASSERT(d.ws.rd_block_ == d.tok);
+
+ // Make sure the stream is open
+ BOOST_ASSERT(d.ws.status_ != status::open);
+ BOOST_ASSERT(d.ws.status_ != status::closed);
+ if( d.ws.status_ == status::failed)
+ goto upcall;
+
+ BOOST_ASSERT(! d.ws.rd_close_);
+ }
+
+ // Drain
+ if(d.ws.rd_remain_ > 0)
+ goto read_payload;
+ for(;;)
+ {
+ // Read frame header
+ while(! d.ws.parse_fh(
+ d.ws.rd_fh_, d.ws.rd_buf_, code))
+ {
+ if(code != close_code::none)
+ {
+ d.ev = error::failed;
+ goto teardown;
+ }
+ BOOST_ASIO_CORO_YIELD
+ d.ws.stream_.async_read_some(
+ d.ws.rd_buf_.prepare(read_size(d.ws.rd_buf_,
+ d.ws.rd_buf_.max_size())),
+ std::move(*this));
+ if(! d.ws.check_ok(ec))
+ goto upcall;
+ d.ws.rd_buf_.commit(bytes_transferred);
+ }
+ if(detail::is_control(d.ws.rd_fh_.op))
+ {
+ // Process control frame
+ if(d.ws.rd_fh_.op == detail::opcode::close)
+ {
+ BOOST_ASSERT(! d.ws.rd_close_);
+ d.ws.rd_close_ = true;
+ auto const mb = buffers_prefix(
+ clamp(d.ws.rd_fh_.len),
+ d.ws.rd_buf_.data());
+ if(d.ws.rd_fh_.len > 0 && d.ws.rd_fh_.mask)
+ detail::mask_inplace(mb, d.ws.rd_key_);
+ detail::read_close(d.ws.cr_, mb, code);
+ if(code != close_code::none)
+ {
+ // Protocol error
+ d.ev = error::failed;
+ goto teardown;
+ }
+ d.ws.rd_buf_.consume(clamp(d.ws.rd_fh_.len));
+ goto teardown;
+ }
+ d.ws.rd_buf_.consume(clamp(d.ws.rd_fh_.len));
+ }
+ else
+ {
+ read_payload:
+ while(d.ws.rd_buf_.size() < d.ws.rd_remain_)
+ {
+ d.ws.rd_remain_ -= d.ws.rd_buf_.size();
+ d.ws.rd_buf_.consume(d.ws.rd_buf_.size());
+ BOOST_ASIO_CORO_YIELD
+ d.ws.stream_.async_read_some(
+ d.ws.rd_buf_.prepare(read_size(d.ws.rd_buf_,
+ d.ws.rd_buf_.max_size())),
+ std::move(*this));
+ if(! d.ws.check_ok(ec))
+ goto upcall;
+ d.ws.rd_buf_.commit(bytes_transferred);
+ }
+ BOOST_ASSERT(d.ws.rd_buf_.size() >= d.ws.rd_remain_);
+ d.ws.rd_buf_.consume(clamp(d.ws.rd_remain_));
+ d.ws.rd_remain_ = 0;
+ }
+ }
+
+ teardown:
+ // Teardown
+ BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+ using beast::websocket::async_teardown;
+ BOOST_ASIO_CORO_YIELD
+ async_teardown(d.ws.role_,
+ d.ws.stream_, std::move(*this));
+ BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+ if(ec == boost::asio::error::eof)
+ {
+ // Rationale:
+ // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
+ ec.assign(0, ec.category());
+ }
+ if(! ec)
+ ec = d.ev;
+ if(ec)
+ d.ws.status_ = status::failed;
+ else
+ d.ws.status_ = status::closed;
+ d.ws.close();
+
+ upcall:
+ BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+ d.ws.wr_block_.reset();
+ if(d.ws.rd_block_ == d.tok)
+ {
+ d.ws.rd_block_.reset();
+ d.ws.paused_r_rd_.maybe_invoke();
+ }
+ d.ws.paused_rd_.maybe_invoke() ||
+ d.ws.paused_ping_.maybe_invoke() ||
+ d.ws.paused_wr_.maybe_invoke();
+ if(! d.cont)
+ {
+ auto& ws = d.ws;
+ return boost::asio::post(
+ ws.stream_.get_executor(),
+ bind_handler(d_.release_handler(), ec));
+ }
+ d_.invoke(ec);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+close(close_reason const& cr)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ error_code ec;
+ close(cr, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+close(close_reason const& cr, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ using beast::detail::clamp;
+ ec.assign(0, ec.category());
+ // Make sure the stream is open
+ if(! check_open(ec))
+ return;
+ // If rd_close_ is set then we already sent a close
+ BOOST_ASSERT(! rd_close_);
+ BOOST_ASSERT(! wr_close_);
+ wr_close_ = true;
+ {
+ detail::frame_buffer fb;
+ write_close<flat_static_buffer_base>(fb, cr);
+ boost::asio::write(stream_, fb.data(), ec);
+ }
+ if(! check_ok(ec))
+ return;
+ status_ = status::closing;
+ // Drain the connection
+ close_code code{};
+ if(rd_remain_ > 0)
+ goto read_payload;
+ for(;;)
+ {
+ // Read frame header
+ while(! parse_fh(rd_fh_, rd_buf_, code))
+ {
+ if(code != close_code::none)
+ return do_fail(close_code::none,
+ error::failed, ec);
+ auto const bytes_transferred =
+ stream_.read_some(
+ rd_buf_.prepare(read_size(rd_buf_,
+ rd_buf_.max_size())), ec);
+ if(! check_ok(ec))
+ return;
+ rd_buf_.commit(bytes_transferred);
+ }
+ if(detail::is_control(rd_fh_.op))
+ {
+ // Process control frame
+ if(rd_fh_.op == detail::opcode::close)
+ {
+ BOOST_ASSERT(! rd_close_);
+ rd_close_ = true;
+ auto const mb = buffers_prefix(
+ clamp(rd_fh_.len),
+ rd_buf_.data());
+ if(rd_fh_.len > 0 && rd_fh_.mask)
+ detail::mask_inplace(mb, rd_key_);
+ detail::read_close(cr_, mb, code);
+ if(code != close_code::none)
+ {
+ // Protocol error
+ return do_fail(close_code::none,
+ error::failed, ec);
+ }
+ rd_buf_.consume(clamp(rd_fh_.len));
+ break;
+ }
+ rd_buf_.consume(clamp(rd_fh_.len));
+ }
+ else
+ {
+ read_payload:
+ while(rd_buf_.size() < rd_remain_)
+ {
+ rd_remain_ -= rd_buf_.size();
+ rd_buf_.consume(rd_buf_.size());
+ auto const bytes_transferred =
+ stream_.read_some(
+ rd_buf_.prepare(read_size(rd_buf_,
+ rd_buf_.max_size())), ec);
+ if(! check_ok(ec))
+ return;
+ rd_buf_.commit(bytes_transferred);
+ }
+ BOOST_ASSERT(rd_buf_.size() >= rd_remain_);
+ rd_buf_.consume(clamp(rd_remain_));
+ rd_remain_ = 0;
+ }
+ }
+ // _Close the WebSocket Connection_
+ do_fail(close_code::none, error::closed, ec);
+ if(ec == error::closed)
+ ec.assign(0, ec.category());
+}
+
+template<class NextLayer>
+template<class CloseHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ CloseHandler, void(error_code))
+stream<NextLayer>::
+async_close(close_reason const& cr, CloseHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements not met");
+ boost::asio::async_completion<CloseHandler,
+ void(error_code)> init{handler};
+ close_op<BOOST_ASIO_HANDLER_TYPE(
+ CloseHandler, void(error_code))>{
+ init.completion_handler, *this, cr}(
+ {}, 0, false);
+ return init.result.get();
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/error.ipp b/boost/beast/websocket/impl/error.ipp
new file mode 100644
index 0000000000..ed18829cfe
--- /dev/null
+++ b/boost/beast/websocket/impl/error.ipp
@@ -0,0 +1,96 @@
+//
+// 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_IMPL_ERROR_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_ERROR_IPP
+
+namespace boost {
+
+namespace system {
+template<>
+struct is_error_code_enum<beast::websocket::error>
+{
+ static bool const value = true;
+};
+} // system
+
+namespace beast {
+namespace websocket {
+namespace detail {
+
+class websocket_error_category : public error_category
+{
+public:
+ const char*
+ name() const noexcept override
+ {
+ return "boost.beast.websocket";
+ }
+
+ std::string
+ message(int ev) const override
+ {
+ switch(static_cast<error>(ev))
+ {
+ default:
+ case error::failed: return "WebSocket connection failed due to a protocol violation";
+ case error::closed: return "WebSocket connection closed normally";
+ case error::handshake_failed: return "WebSocket upgrade handshake failed";
+ case error::buffer_overflow: return "WebSocket dynamic buffer overflow";
+ case error::partial_deflate_block: return "WebSocket partial deflate block";
+ }
+ }
+
+ error_condition
+ default_error_condition(int ev) const noexcept override
+ {
+ return error_condition(ev, *this);
+ }
+
+ bool
+ equivalent(int ev,
+ error_condition const& condition
+ ) const noexcept override
+ {
+ return condition.value() == ev &&
+ &condition.category() == this;
+ }
+
+ bool
+ equivalent(error_code const& error, int ev) const noexcept override
+ {
+ return error.value() == ev &&
+ &error.category() == this;
+ }
+};
+
+inline
+error_category const&
+get_error_category()
+{
+ static detail::websocket_error_category const cat{};
+ return cat;
+}
+
+} // detail
+
+inline
+error_code
+make_error_code(error e)
+{
+ return error_code(
+ static_cast<std::underlying_type<error>::type>(e),
+ detail::get_error_category());
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/handshake.ipp b/boost/beast/websocket/impl/handshake.ipp
new file mode 100644
index 0000000000..cdd8d47342
--- /dev/null
+++ b/boost/beast/websocket/impl/handshake.ipp
@@ -0,0 +1,407 @@
+//
+// 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_IMPL_HANDSHAKE_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
+
+#include <boost/beast/websocket/detail/type_traits.hpp>
+#include <boost/beast/http/empty_body.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/http/write.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+//------------------------------------------------------------------------------
+
+// send the upgrade request and process the response
+//
+template<class NextLayer>
+template<class Handler>
+class stream<NextLayer>::handshake_op
+ : public boost::asio::coroutine
+{
+ struct data
+ {
+ stream<NextLayer>& 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&, stream<NextLayer>& ws_,
+ response_type* res_p_,
+ string_view host,
+ string_view target,
+ Decorator const& decorator)
+ : ws(ws_)
+ , res_p(res_p_)
+ , req(ws.build_request(key,
+ host, target, decorator))
+ {
+ ws.reset();
+ }
+ };
+
+ handler_ptr<data, Handler> d_;
+
+public:
+ handshake_op(handshake_op&&) = default;
+ handshake_op(handshake_op const&) = default;
+
+ template<class DeducedHandler, class... Args>
+ handshake_op(DeducedHandler&& h,
+ stream<NextLayer>& ws, Args&&... args)
+ : d_(std::forward<DeducedHandler>(h),
+ ws, std::forward<Args>(args)...)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->ws.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_used = 0);
+
+ friend
+ bool asio_handler_is_continuation(handshake_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class NextLayer>
+template<class Handler>
+void
+stream<NextLayer>::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);
+ BOOST_ASIO_CORO_YIELD
+ http::async_write(d.ws.stream_,
+ d.req, std::move(*this));
+ if(ec)
+ goto upcall;
+
+ // VFALCO We could pre-serialize the request to
+ // a single buffer, send that instead,
+ // and delete the buffer here. The buffer
+ // could be a variable block at the end
+ // of handler_ptr's allocation.
+
+ // Read HTTP response
+ BOOST_ASIO_CORO_YIELD
+ http::async_read(d.ws.next_layer(),
+ d.ws.rd_buf_, d.res,
+ std::move(*this));
+ if(ec)
+ goto upcall;
+ d.ws.on_response(d.res, d.key, ec);
+ if(d.res_p)
+ swap(d.res, *d.res_p);
+ upcall:
+ d_.invoke(ec);
+ }
+}
+
+template<class NextLayer>
+template<class HandshakeHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ HandshakeHandler, void(error_code))
+stream<NextLayer>::
+async_handshake(string_view host,
+ string_view target,
+ HandshakeHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements not met");
+ boost::asio::async_completion<HandshakeHandler,
+ void(error_code)> init{handler};
+ handshake_op<BOOST_ASIO_HANDLER_TYPE(
+ HandshakeHandler, void(error_code))>{
+ init.completion_handler, *this, nullptr, host,
+ target, &default_decorate_req}();
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<class HandshakeHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ HandshakeHandler, void(error_code))
+stream<NextLayer>::
+async_handshake(response_type& res,
+ string_view host,
+ string_view target,
+ HandshakeHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements not met");
+ boost::asio::async_completion<HandshakeHandler,
+ void(error_code)> init{handler};
+ handshake_op<BOOST_ASIO_HANDLER_TYPE(
+ HandshakeHandler, void(error_code))>{
+ init.completion_handler, *this, &res, host,
+ target, &default_decorate_req}();
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<class RequestDecorator, class HandshakeHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ HandshakeHandler, void(error_code))
+stream<NextLayer>::
+async_handshake_ex(string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ HandshakeHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements not met");
+ static_assert(detail::is_RequestDecorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ boost::asio::async_completion<HandshakeHandler,
+ void(error_code)> init{handler};
+ handshake_op<BOOST_ASIO_HANDLER_TYPE(
+ HandshakeHandler, void(error_code))>{
+ init.completion_handler, *this, nullptr, host,
+ target, decorator}();
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<class RequestDecorator, class HandshakeHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ HandshakeHandler, void(error_code))
+stream<NextLayer>::
+async_handshake_ex(response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ HandshakeHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements not met");
+ static_assert(detail::is_RequestDecorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ boost::asio::async_completion<HandshakeHandler,
+ void(error_code)> init{handler};
+ handshake_op<BOOST_ASIO_HANDLER_TYPE(
+ HandshakeHandler, void(error_code))>{
+ init.completion_handler, *this, &res, host,
+ target, decorator}();
+ return init.result.get();
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+handshake(string_view host,
+ string_view target)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ error_code ec;
+ handshake(
+ host, target, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+handshake(response_type& res,
+ string_view host,
+ string_view target)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ error_code ec;
+ handshake(res, host, target, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+template<class RequestDecorator>
+void
+stream<NextLayer>::
+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<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ error_code ec;
+ handshake_ex(host, target, decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+template<class RequestDecorator>
+void
+stream<NextLayer>::
+handshake_ex(response_type& res,
+ 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<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ error_code ec;
+ handshake_ex(res, host, target, decorator, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+handshake(string_view host,
+ string_view target, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ do_handshake(nullptr,
+ host, target, &default_decorate_req, ec);
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+handshake(response_type& res,
+ string_view host,
+ string_view target,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ do_handshake(&res,
+ host, target, &default_decorate_req, ec);
+}
+
+template<class NextLayer>
+template<class RequestDecorator>
+void
+stream<NextLayer>::
+handshake_ex(string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(detail::is_RequestDecorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ do_handshake(nullptr,
+ host, target, decorator, ec);
+}
+
+template<class NextLayer>
+template<class RequestDecorator>
+void
+stream<NextLayer>::
+handshake_ex(response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(detail::is_RequestDecorator<
+ RequestDecorator>::value,
+ "RequestDecorator requirements not met");
+ do_handshake(&res,
+ host, target, decorator, ec);
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<class RequestDecorator>
+void
+stream<NextLayer>::
+do_handshake(
+ response_type* res_p,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec)
+{
+ response_type res;
+ reset();
+ detail::sec_ws_key_type key;
+ {
+ auto const req = build_request(
+ key, host, target, decorator);
+ pmd_read(pmd_config_, req);
+ http::write(stream_, req, ec);
+ }
+ if(ec)
+ return;
+ http::read(next_layer(), rd_buf_, res, ec);
+ if(ec)
+ return;
+ on_response(res, key, ec);
+ if(res_p)
+ *res_p = std::move(res);
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/ping.ipp b/boost/beast/websocket/impl/ping.ipp
new file mode 100644
index 0000000000..79003261de
--- /dev/null
+++ b/boost/beast/websocket/impl/ping.ipp
@@ -0,0 +1,271 @@
+//
+// 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_IMPL_PING_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_PING_IPP
+
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/websocket/detail/frame.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/throw_exception.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/*
+ This composed operation handles sending ping and pong frames.
+ It only sends the frames it does not make attempts to read
+ any frame data.
+*/
+template<class NextLayer>
+template<class Handler>
+class stream<NextLayer>::ping_op
+ : public boost::asio::coroutine
+{
+ struct state
+ {
+ stream<NextLayer>& ws;
+ detail::frame_buffer fb;
+ token tok;
+
+ state(
+ Handler&,
+ stream<NextLayer>& ws_,
+ detail::opcode op,
+ ping_data const& payload)
+ : ws(ws_)
+ , tok(ws.tok_.unique())
+ {
+ // Serialize the control frame
+ ws.template write_ping<
+ flat_static_buffer_base>(
+ fb, op, payload);
+ }
+ };
+
+ handler_ptr<state, Handler> d_;
+
+public:
+ ping_op(ping_op&&) = default;
+ ping_op(ping_op const&) = default;
+
+ template<class DeducedHandler>
+ ping_op(
+ DeducedHandler&& h,
+ stream<NextLayer>& ws,
+ detail::opcode op,
+ ping_data const& payload)
+ : d_(std::forward<DeducedHandler>(h),
+ ws, op, payload)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->ws.get_executor());
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(ping_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class NextLayer>
+template<class Handler>
+void
+stream<NextLayer>::
+ping_op<Handler>::
+operator()(error_code ec, std::size_t)
+{
+ auto& d = *d_;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Maybe suspend
+ if(! d.ws.wr_block_)
+ {
+ // Acquire the write block
+ d.ws.wr_block_ = d.tok;
+
+ // Make sure the stream is open
+ if(! d.ws.check_open(ec))
+ {
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ d.ws.get_executor(),
+ bind_handler(std::move(*this), ec));
+ goto upcall;
+ }
+ }
+ else
+ {
+ // Suspend
+ BOOST_ASSERT(d.ws.wr_block_ != d.tok);
+ BOOST_ASIO_CORO_YIELD
+ d.ws.paused_ping_.emplace(std::move(*this));
+
+ // Acquire the write block
+ BOOST_ASSERT(! d.ws.wr_block_);
+ d.ws.wr_block_ = d.tok;
+
+ // Resume
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ d.ws.get_executor(), std::move(*this));
+ BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+
+ // Make sure the stream is open
+ if(! d.ws.check_open(ec))
+ goto upcall;
+ }
+
+ // Send ping frame
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(d.ws.stream_,
+ d.fb.data(), std::move(*this));
+ if(! d.ws.check_ok(ec))
+ goto upcall;
+
+ upcall:
+ BOOST_ASSERT(d.ws.wr_block_ == d.tok);
+ d.ws.wr_block_.reset();
+ d.ws.paused_close_.maybe_invoke() ||
+ d.ws.paused_rd_.maybe_invoke() ||
+ d.ws.paused_wr_.maybe_invoke();
+ d_.invoke(ec);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+ping(ping_data const& payload)
+{
+ error_code ec;
+ ping(payload, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+ping(ping_data const& payload, error_code& ec)
+{
+ // Make sure the stream is open
+ if(! check_open(ec))
+ return;
+ detail::frame_buffer fb;
+ write_ping<flat_static_buffer_base>(
+ fb, detail::opcode::ping, payload);
+ boost::asio::write(stream_, fb.data(), ec);
+ if(! check_ok(ec))
+ return;
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+pong(ping_data const& payload)
+{
+ error_code ec;
+ pong(payload, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+pong(ping_data const& payload, error_code& ec)
+{
+ // Make sure the stream is open
+ if(! check_open(ec))
+ return;
+ detail::frame_buffer fb;
+ write_ping<flat_static_buffer_base>(
+ fb, detail::opcode::pong, payload);
+ boost::asio::write(stream_, fb.data(), ec);
+ if(! check_ok(ec))
+ return;
+}
+
+template<class NextLayer>
+template<class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code))
+stream<NextLayer>::
+async_ping(ping_data const& payload, WriteHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ boost::asio::async_completion<WriteHandler,
+ void(error_code)> init{handler};
+ ping_op<BOOST_ASIO_HANDLER_TYPE(
+ WriteHandler, void(error_code))>{
+ init.completion_handler, *this,
+ detail::opcode::ping, payload}();
+ return init.result.get();
+}
+
+template<class NextLayer>
+template<class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code))
+stream<NextLayer>::
+async_pong(ping_data const& payload, WriteHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ boost::asio::async_completion<WriteHandler,
+ void(error_code)> init{handler};
+ ping_op<BOOST_ASIO_HANDLER_TYPE(
+ WriteHandler, void(error_code))>{
+ init.completion_handler, *this,
+ detail::opcode::pong, payload}();
+ return init.result.get();
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/read.ipp b/boost/beast/websocket/impl/read.ipp
new file mode 100644
index 0000000000..422cc3766b
--- /dev/null
+++ b/boost/beast/websocket/impl/read.ipp
@@ -0,0 +1,1323 @@
+//
+// 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_IMPL_READ_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_READ_IPP
+
+#include <boost/beast/websocket/teardown.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/assert.hpp>
+#include <boost/config.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <limits>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/* Read some message frame data.
+
+ Also reads and handles control frames.
+*/
+template<class NextLayer>
+template<
+ class MutableBufferSequence,
+ class Handler>
+class stream<NextLayer>::read_some_op
+ : public boost::asio::coroutine
+{
+ Handler h_;
+ stream<NextLayer>& ws_;
+ MutableBufferSequence bs_;
+ buffers_suffix<MutableBufferSequence> cb_;
+ std::size_t bytes_written_ = 0;
+ error_code ev_;
+ token tok_;
+ close_code code_;
+ bool did_read_ = false;
+ bool cont_ = false;
+
+public:
+ read_some_op(read_some_op&&) = default;
+ read_some_op(read_some_op const&) = default;
+
+ template<class DeducedHandler>
+ read_some_op(
+ DeducedHandler&& h,
+ stream<NextLayer>& ws,
+ MutableBufferSequence const& bs)
+ : h_(std::forward<DeducedHandler>(h))
+ , ws_(ws)
+ , bs_(bs)
+ , cb_(bs)
+ , tok_(ws_.tok_.unique())
+ , code_(close_code::none)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, ws_.get_executor());
+ }
+
+ Handler&
+ handler()
+ {
+ return h_;
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true);
+
+ friend
+ bool asio_handler_is_continuation(read_some_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->cont_ || asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<class NextLayer>
+template<class MutableBufferSequence, class Handler>
+void
+stream<NextLayer>::
+read_some_op<MutableBufferSequence, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred,
+ bool cont)
+{
+ using beast::detail::clamp;
+ using boost::asio::buffer;
+ using boost::asio::buffer_size;
+ close_code code{};
+ cont_ = cont;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Maybe suspend
+ do_maybe_suspend:
+ if(! ws_.rd_block_)
+ {
+ // Acquire the read block
+ ws_.rd_block_ = tok_;
+
+ // Make sure the stream is not closed
+ if( ws_.status_ == status::closed ||
+ ws_.status_ == status::failed)
+ {
+ ec = boost::asio::error::operation_aborted;
+ goto upcall;
+ }
+ }
+ else
+ {
+ do_suspend:
+ // Suspend
+ BOOST_ASSERT(ws_.rd_block_ != tok_);
+ BOOST_ASIO_CORO_YIELD
+ ws_.paused_r_rd_.save(std::move(*this));
+
+ // Acquire the read block
+ BOOST_ASSERT(! ws_.rd_block_);
+ ws_.rd_block_ = tok_;
+
+ // Resume
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ ws_.get_executor(), std::move(*this));
+ BOOST_ASSERT(ws_.rd_block_ == tok_);
+
+ // The only way to get read blocked is if
+ // a `close_op` wrote a close frame
+ BOOST_ASSERT(ws_.wr_close_);
+ BOOST_ASSERT(ws_.status_ != status::open);
+ ec = boost::asio::error::operation_aborted;
+ goto upcall;
+ }
+
+ // if status_ == status::closing, we want to suspend
+ // the read operation until the close completes,
+ // then finish the read with operation_aborted.
+
+ loop:
+ BOOST_ASSERT(ws_.rd_block_ == tok_);
+ // See if we need to read a frame header. This
+ // condition is structured to give the decompressor
+ // a chance to emit the final empty deflate block
+ //
+ if(ws_.rd_remain_ == 0 &&
+ (! ws_.rd_fh_.fin || ws_.rd_done_))
+ {
+ // Read frame header
+ while(! ws_.parse_fh(
+ ws_.rd_fh_, ws_.rd_buf_, code))
+ {
+ if(code != close_code::none)
+ {
+ // _Fail the WebSocket Connection_
+ code_ = code;
+ ev_ = error::failed;
+ goto close;
+ }
+ BOOST_ASSERT(ws_.rd_block_ == tok_);
+ BOOST_ASIO_CORO_YIELD
+ ws_.stream_.async_read_some(
+ ws_.rd_buf_.prepare(read_size(
+ ws_.rd_buf_, ws_.rd_buf_.max_size())),
+ std::move(*this));
+ BOOST_ASSERT(ws_.rd_block_ == tok_);
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ ws_.rd_buf_.commit(bytes_transferred);
+
+ // Allow a close operation
+ // to acquire the read block
+ BOOST_ASSERT(ws_.rd_block_ == tok_);
+ ws_.rd_block_.reset();
+ if( ws_.paused_r_close_.maybe_invoke())
+ {
+ // Suspend
+ BOOST_ASSERT(ws_.rd_block_);
+ goto do_suspend;
+ }
+ // Acquire read block
+ ws_.rd_block_ = tok_;
+ }
+ // Immediately apply the mask to the portion
+ // of the buffer holding payload data.
+ if(ws_.rd_fh_.len > 0 && ws_.rd_fh_.mask)
+ detail::mask_inplace(buffers_prefix(
+ clamp(ws_.rd_fh_.len),
+ ws_.rd_buf_.data()),
+ ws_.rd_key_);
+ if(detail::is_control(ws_.rd_fh_.op))
+ {
+ // Clear this otherwise the next
+ // frame will be considered final.
+ ws_.rd_fh_.fin = false;
+
+ // Handle ping frame
+ if(ws_.rd_fh_.op == detail::opcode::ping)
+ {
+ {
+ auto const b = buffers_prefix(
+ clamp(ws_.rd_fh_.len),
+ ws_.rd_buf_.data());
+ auto const len = buffer_size(b);
+ BOOST_ASSERT(len == ws_.rd_fh_.len);
+ ping_data payload;
+ detail::read_ping(payload, b);
+ ws_.rd_buf_.consume(len);
+ // Ignore ping when closing
+ if(ws_.status_ == status::closing)
+ goto loop;
+ if(ws_.ctrl_cb_)
+ ws_.ctrl_cb_(frame_type::ping, payload);
+ ws_.rd_fb_.reset();
+ ws_.template write_ping<
+ flat_static_buffer_base>(ws_.rd_fb_,
+ detail::opcode::pong, payload);
+ }
+
+ //BOOST_ASSERT(! ws_.paused_r_close_);
+
+ // Allow a close operation
+ // to acquire the read block
+ BOOST_ASSERT(ws_.rd_block_ == tok_);
+ ws_.rd_block_.reset();
+ ws_.paused_r_close_.maybe_invoke();
+
+ // Maybe suspend
+ if(! ws_.wr_block_)
+ {
+ // Acquire the write block
+ ws_.wr_block_ = tok_;
+ }
+ else
+ {
+ // Suspend
+ BOOST_ASSERT(ws_.wr_block_ != tok_);
+ BOOST_ASIO_CORO_YIELD
+ ws_.paused_rd_.save(std::move(*this));
+
+ // Acquire the write block
+ BOOST_ASSERT(! ws_.wr_block_);
+ ws_.wr_block_ = tok_;
+
+ // Resume
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ ws_.get_executor(), std::move(*this));
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+
+ // Make sure the stream is open
+ if(! ws_.check_open(ec))
+ goto upcall;
+ }
+
+ // Send pong
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(ws_.stream_,
+ ws_.rd_fb_.data(), std::move(*this));
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ ws_.wr_block_.reset();
+ ws_.paused_close_.maybe_invoke() ||
+ ws_.paused_ping_.maybe_invoke() ||
+ ws_.paused_wr_.maybe_invoke();
+ goto do_maybe_suspend;
+ }
+ // Handle pong frame
+ if(ws_.rd_fh_.op == detail::opcode::pong)
+ {
+ auto const cb = buffers_prefix(clamp(
+ ws_.rd_fh_.len), ws_.rd_buf_.data());
+ auto const len = buffer_size(cb);
+ BOOST_ASSERT(len == ws_.rd_fh_.len);
+ code = close_code::none;
+ ping_data payload;
+ detail::read_ping(payload, cb);
+ ws_.rd_buf_.consume(len);
+ // Ignore pong when closing
+ if(! ws_.wr_close_ && ws_.ctrl_cb_)
+ ws_.ctrl_cb_(frame_type::pong, payload);
+ goto loop;
+ }
+ // Handle close frame
+ BOOST_ASSERT(ws_.rd_fh_.op == detail::opcode::close);
+ {
+ auto const cb = buffers_prefix(clamp(
+ ws_.rd_fh_.len), ws_.rd_buf_.data());
+ auto const len = buffer_size(cb);
+ BOOST_ASSERT(len == ws_.rd_fh_.len);
+ BOOST_ASSERT(! ws_.rd_close_);
+ ws_.rd_close_ = true;
+ close_reason cr;
+ detail::read_close(cr, cb, code);
+ if(code != close_code::none)
+ {
+ // _Fail the WebSocket Connection_
+ code_ = code;
+ ev_ = error::failed;
+ goto close;
+ }
+ ws_.cr_ = cr;
+ ws_.rd_buf_.consume(len);
+ if(ws_.ctrl_cb_)
+ ws_.ctrl_cb_(frame_type::close,
+ ws_.cr_.reason);
+ // See if we are already closing
+ if(ws_.status_ == status::closing)
+ {
+ // _Close the WebSocket Connection_
+ BOOST_ASSERT(ws_.wr_close_);
+ code_ = close_code::none;
+ ev_ = error::closed;
+ goto close;
+ }
+ // _Start the WebSocket Closing Handshake_
+ code_ = cr.code == close_code::none ?
+ close_code::normal :
+ static_cast<close_code>(cr.code);
+ ev_ = error::closed;
+ goto close;
+ }
+ }
+ if(ws_.rd_fh_.len == 0 && ! ws_.rd_fh_.fin)
+ {
+ // Empty non-final frame
+ goto loop;
+ }
+ ws_.rd_done_ = false;
+ }
+ if(! ws_.pmd_ || ! ws_.pmd_->rd_set)
+ {
+ if(ws_.rd_remain_ > 0)
+ {
+ if(ws_.rd_buf_.size() == 0 && ws_.rd_buf_.max_size() >
+ (std::min)(clamp(ws_.rd_remain_),
+ buffer_size(cb_)))
+ {
+ // Fill the read buffer first, otherwise we
+ // get fewer bytes at the cost of one I/O.
+ BOOST_ASIO_CORO_YIELD
+ ws_.stream_.async_read_some(
+ ws_.rd_buf_.prepare(read_size(
+ ws_.rd_buf_, ws_.rd_buf_.max_size())),
+ std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ ws_.rd_buf_.commit(bytes_transferred);
+ if(ws_.rd_fh_.mask)
+ detail::mask_inplace(buffers_prefix(clamp(
+ ws_.rd_remain_), ws_.rd_buf_.data()),
+ ws_.rd_key_);
+ }
+ if(ws_.rd_buf_.size() > 0)
+ {
+ // Copy from the read buffer.
+ // The mask was already applied.
+ bytes_transferred = buffer_copy(cb_,
+ ws_.rd_buf_.data(), clamp(ws_.rd_remain_));
+ auto const mb = buffers_prefix(
+ bytes_transferred, cb_);
+ ws_.rd_remain_ -= bytes_transferred;
+ if(ws_.rd_op_ == detail::opcode::text)
+ {
+ if(! ws_.rd_utf8_.write(mb) ||
+ (ws_.rd_remain_ == 0 && ws_.rd_fh_.fin &&
+ ! ws_.rd_utf8_.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::bad_payload;
+ ev_ = error::failed;
+ goto close;
+ }
+ }
+ bytes_written_ += bytes_transferred;
+ ws_.rd_size_ += bytes_transferred;
+ ws_.rd_buf_.consume(bytes_transferred);
+ }
+ else
+ {
+ // Read into caller's buffer
+ BOOST_ASSERT(ws_.rd_remain_ > 0);
+ BOOST_ASSERT(buffer_size(cb_) > 0);
+ BOOST_ASSERT(buffer_size(buffers_prefix(
+ clamp(ws_.rd_remain_), cb_)) > 0);
+ BOOST_ASIO_CORO_YIELD
+ ws_.stream_.async_read_some(buffers_prefix(
+ clamp(ws_.rd_remain_), cb_), std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ BOOST_ASSERT(bytes_transferred > 0);
+ auto const mb = buffers_prefix(
+ bytes_transferred, cb_);
+ ws_.rd_remain_ -= bytes_transferred;
+ if(ws_.rd_fh_.mask)
+ detail::mask_inplace(mb, ws_.rd_key_);
+ if(ws_.rd_op_ == detail::opcode::text)
+ {
+ if(! ws_.rd_utf8_.write(mb) ||
+ (ws_.rd_remain_ == 0 && ws_.rd_fh_.fin &&
+ ! ws_.rd_utf8_.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::bad_payload;
+ ev_ = error::failed;
+ goto close;
+ }
+ }
+ bytes_written_ += bytes_transferred;
+ ws_.rd_size_ += bytes_transferred;
+ }
+ }
+ ws_.rd_done_ = ws_.rd_remain_ == 0 && ws_.rd_fh_.fin;
+ }
+ else
+ {
+ // Read compressed message frame payload:
+ // inflate even if rd_fh_.len == 0, otherwise we
+ // never emit the end-of-stream deflate block.
+ while(buffer_size(cb_) > 0)
+ {
+ if( ws_.rd_remain_ > 0 &&
+ ws_.rd_buf_.size() == 0 &&
+ ! did_read_)
+ {
+ // read new
+ BOOST_ASIO_CORO_YIELD
+ ws_.stream_.async_read_some(
+ ws_.rd_buf_.prepare(read_size(
+ ws_.rd_buf_, ws_.rd_buf_.max_size())),
+ std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ BOOST_ASSERT(bytes_transferred > 0);
+ ws_.rd_buf_.commit(bytes_transferred);
+ if(ws_.rd_fh_.mask)
+ detail::mask_inplace(
+ buffers_prefix(clamp(ws_.rd_remain_),
+ ws_.rd_buf_.data()), ws_.rd_key_);
+ did_read_ = true;
+ }
+ zlib::z_params zs;
+ {
+ auto const out = buffers_front(cb_);
+ zs.next_out = out.data();
+ zs.avail_out = out.size();
+ BOOST_ASSERT(zs.avail_out > 0);
+ }
+ if(ws_.rd_remain_ > 0)
+ {
+ if(ws_.rd_buf_.size() > 0)
+ {
+ // use what's there
+ auto const in = buffers_prefix(
+ clamp(ws_.rd_remain_), buffers_front(
+ ws_.rd_buf_.data()));
+ zs.avail_in = in.size();
+ zs.next_in = in.data();
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if(ws_.rd_fh_.fin)
+ {
+ // append the empty block codes
+ static std::uint8_t constexpr
+ empty_block[4] = {
+ 0x00, 0x00, 0xff, 0xff };
+ zs.next_in = empty_block;
+ zs.avail_in = sizeof(empty_block);
+ ws_.pmd_->zi.write(zs, zlib::Flush::sync, ec);
+ if(! ec)
+ {
+ // https://github.com/madler/zlib/issues/280
+ if(zs.total_out > 0)
+ ec = error::partial_deflate_block;
+ }
+ 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_.rd_done_ = true;
+ break;
+ }
+ else
+ {
+ break;
+ }
+ ws_.pmd_->zi.write(zs, zlib::Flush::sync, ec);
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ if(ws_.rd_msg_max_ && beast::detail::sum_exceeds(
+ ws_.rd_size_, zs.total_out, ws_.rd_msg_max_))
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::too_big;
+ ev_ = error::failed;
+ goto close;
+ }
+ cb_.consume(zs.total_out);
+ ws_.rd_size_ += zs.total_out;
+ ws_.rd_remain_ -= zs.total_in;
+ ws_.rd_buf_.consume(zs.total_in);
+ bytes_written_ += zs.total_out;
+ }
+ if(ws_.rd_op_ == detail::opcode::text)
+ {
+ // check utf8
+ if(! ws_.rd_utf8_.write(
+ buffers_prefix(bytes_written_, bs_)) || (
+ ws_.rd_done_ && ! ws_.rd_utf8_.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ code_ = close_code::bad_payload;
+ ev_ = error::failed;
+ goto close;
+ }
+ }
+ }
+ goto upcall;
+
+ close:
+ if(! ws_.wr_block_)
+ {
+ // Acquire the write block
+ ws_.wr_block_ = tok_;
+
+ // Make sure the stream is open
+ BOOST_ASSERT(ws_.status_ == status::open);
+ }
+ else
+ {
+ // Suspend
+ BOOST_ASSERT(ws_.wr_block_ != tok_);
+ BOOST_ASIO_CORO_YIELD
+ ws_.paused_rd_.save(std::move(*this));
+
+ // Acquire the write block
+ BOOST_ASSERT(! ws_.wr_block_);
+ ws_.wr_block_ = tok_;
+
+ // Resume
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ ws_.get_executor(), std::move(*this));
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+
+ // Make sure the stream is open
+ if(! ws_.check_open(ec))
+ goto upcall;
+ }
+
+ // Set the status
+ ws_.status_ = status::closing;
+
+ if(! ws_.wr_close_)
+ {
+ ws_.wr_close_ = true;
+
+ // Serialize close frame
+ ws_.rd_fb_.reset();
+ ws_.template write_close<
+ flat_static_buffer_base>(
+ ws_.rd_fb_, code_);
+
+ // Send close frame
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(
+ ws_.stream_, ws_.rd_fb_.data(),
+ std::move(*this));
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ }
+
+ // Teardown
+ using beast::websocket::async_teardown;
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+ BOOST_ASIO_CORO_YIELD
+ async_teardown(ws_.role_,
+ ws_.stream_, std::move(*this));
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+ if(ec == boost::asio::error::eof)
+ {
+ // Rationale:
+ // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
+ ec.assign(0, ec.category());
+ }
+ if(! ec)
+ ec = ev_;
+ if(ec && ec != error::closed)
+ ws_.status_ = status::failed;
+ else
+ ws_.status_ = status::closed;
+ ws_.close();
+
+ upcall:
+ if(ws_.rd_block_ == tok_)
+ ws_.rd_block_.reset();
+ ws_.paused_r_close_.maybe_invoke();
+ if(ws_.wr_block_ == tok_)
+ {
+ ws_.wr_block_.reset();
+ ws_.paused_close_.maybe_invoke() ||
+ ws_.paused_ping_.maybe_invoke() ||
+ ws_.paused_wr_.maybe_invoke();
+ }
+ if(! cont_)
+ return boost::asio::post(
+ ws_.stream_.get_executor(),
+ bind_handler(std::move(h_),
+ ec, bytes_written_));
+ h_(ec, bytes_written_);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<
+ class DynamicBuffer,
+ class Handler>
+class stream<NextLayer>::read_op
+ : public boost::asio::coroutine
+{
+ Handler h_;
+ stream<NextLayer>& ws_;
+ DynamicBuffer& b_;
+ std::size_t limit_;
+ std::size_t bytes_written_ = 0;
+ bool some_;
+
+public:
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ read_op(read_op&&) = default;
+ read_op(read_op const&) = default;
+
+ template<class DeducedHandler>
+ read_op(
+ DeducedHandler&& h,
+ stream<NextLayer>& ws,
+ DynamicBuffer& b,
+ std::size_t limit,
+ bool some)
+ : h_(std::forward<DeducedHandler>(h))
+ , ws_(ws)
+ , b_(b)
+ , limit_(limit ? limit : (
+ std::numeric_limits<std::size_t>::max)())
+ , some_(some)
+ {
+ }
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, ws_.get_executor());
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(read_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<class NextLayer>
+template<class DynamicBuffer, class Handler>
+void
+stream<NextLayer>::
+read_op<DynamicBuffer, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred)
+{
+ using beast::detail::clamp;
+ using buffers_type = typename
+ DynamicBuffer::mutable_buffers_type;
+ boost::optional<buffers_type> mb;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ do
+ {
+ try
+ {
+ mb.emplace(b_.prepare(clamp(
+ ws_.read_size_hint(b_), limit_)));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ }
+ if(ec)
+ {
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ ws_.get_executor(),
+ bind_handler(std::move(*this),
+ error::buffer_overflow, 0));
+ break;
+ }
+ BOOST_ASIO_CORO_YIELD
+ read_some_op<buffers_type, read_op>{
+ std::move(*this), ws_, *mb}(
+ {}, 0, false);
+ if(ec)
+ break;
+ b_.commit(bytes_transferred);
+ bytes_written_ += bytes_transferred;
+ }
+ while(! some_ && ! ws_.is_message_done());
+ h_(ec, bytes_written_);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<class DynamicBuffer>
+std::size_t
+stream<NextLayer>::
+read(DynamicBuffer& buffer)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ error_code ec;
+ auto const bytes_written = read(buffer, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_written;
+}
+
+template<class NextLayer>
+template<class DynamicBuffer>
+std::size_t
+stream<NextLayer>::
+read(DynamicBuffer& buffer, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ std::size_t bytes_written = 0;
+ do
+ {
+ bytes_written += read_some(buffer, 0, ec);
+ if(ec)
+ return bytes_written;
+ }
+ while(! is_message_done());
+ return bytes_written;
+}
+
+template<class NextLayer>
+template<class DynamicBuffer, class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+stream<NextLayer>::
+async_read(DynamicBuffer& buffer, ReadHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ boost::asio::async_completion<
+ ReadHandler, void(error_code, std::size_t)> init{handler};
+ read_op<
+ DynamicBuffer,
+ BOOST_ASIO_HANDLER_TYPE(
+ ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler,
+ *this,
+ buffer,
+ 0,
+ false}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<class DynamicBuffer>
+std::size_t
+stream<NextLayer>::
+read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ error_code ec;
+ auto const bytes_written =
+ read_some(buffer, limit, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_written;
+}
+
+template<class NextLayer>
+template<class DynamicBuffer>
+std::size_t
+stream<NextLayer>::
+read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ using beast::detail::clamp;
+ if(! limit)
+ limit = (std::numeric_limits<std::size_t>::max)();
+ auto const size =
+ clamp(read_size_hint(buffer), limit);
+ BOOST_ASSERT(size > 0);
+ boost::optional<typename
+ DynamicBuffer::mutable_buffers_type> mb;
+ try
+ {
+ mb.emplace(buffer.prepare(size));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ return 0;
+ }
+ auto const bytes_written = read_some(*mb, ec);
+ buffer.commit(bytes_written);
+ return bytes_written;
+}
+
+template<class NextLayer>
+template<class DynamicBuffer, class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+stream<NextLayer>::
+async_read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ boost::asio::async_completion<ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ read_op<
+ DynamicBuffer,
+ BOOST_ASIO_HANDLER_TYPE(
+ ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler,
+ *this,
+ buffer,
+ limit,
+ true}({}, 0);
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<class MutableBufferSequence>
+std::size_t
+stream<NextLayer>::
+read_some(
+ MutableBufferSequence const& buffers)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence requirements not met");
+ error_code ec;
+ auto const bytes_written = read_some(buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_written;
+}
+
+template<class NextLayer>
+template<class MutableBufferSequence>
+std::size_t
+stream<NextLayer>::
+read_some(
+ MutableBufferSequence const& buffers,
+ error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence requirements not met");
+ using beast::detail::clamp;
+ using boost::asio::buffer;
+ using boost::asio::buffer_size;
+ close_code code{};
+ std::size_t bytes_written = 0;
+ ec.assign(0, ec.category());
+ // Make sure the stream is open
+ if(! check_open(ec))
+ return 0;
+loop:
+ // See if we need to read a frame header. This
+ // condition is structured to give the decompressor
+ // a chance to emit the final empty deflate block
+ //
+ if(rd_remain_ == 0 && (! rd_fh_.fin || rd_done_))
+ {
+ // Read frame header
+ while(! parse_fh(rd_fh_, rd_buf_, code))
+ {
+ if(code != close_code::none)
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(code, error::failed, ec);
+ return bytes_written;
+ }
+ auto const bytes_transferred =
+ stream_.read_some(
+ rd_buf_.prepare(read_size(
+ rd_buf_, rd_buf_.max_size())),
+ ec);
+ if(! check_ok(ec))
+ return bytes_written;
+ rd_buf_.commit(bytes_transferred);
+ }
+ // Immediately apply the mask to the portion
+ // of the buffer holding payload data.
+ if(rd_fh_.len > 0 && rd_fh_.mask)
+ detail::mask_inplace(buffers_prefix(
+ clamp(rd_fh_.len), rd_buf_.data()),
+ rd_key_);
+ if(detail::is_control(rd_fh_.op))
+ {
+ // Get control frame payload
+ auto const b = buffers_prefix(
+ clamp(rd_fh_.len), rd_buf_.data());
+ auto const len = buffer_size(b);
+ BOOST_ASSERT(len == rd_fh_.len);
+
+ // Clear this otherwise the next
+ // frame will be considered final.
+ rd_fh_.fin = false;
+
+ // Handle ping frame
+ if(rd_fh_.op == detail::opcode::ping)
+ {
+ ping_data payload;
+ detail::read_ping(payload, b);
+ rd_buf_.consume(len);
+ if(wr_close_)
+ {
+ // Ignore ping when closing
+ goto loop;
+ }
+ if(ctrl_cb_)
+ ctrl_cb_(frame_type::ping, payload);
+ detail::frame_buffer fb;
+ write_ping<flat_static_buffer_base>(fb,
+ detail::opcode::pong, payload);
+ boost::asio::write(stream_, fb.data(), ec);
+ if(! check_ok(ec))
+ return bytes_written;
+ goto loop;
+ }
+ // Handle pong frame
+ if(rd_fh_.op == detail::opcode::pong)
+ {
+ ping_data payload;
+ detail::read_ping(payload, b);
+ rd_buf_.consume(len);
+ if(ctrl_cb_)
+ ctrl_cb_(frame_type::pong, payload);
+ goto loop;
+ }
+ // Handle close frame
+ BOOST_ASSERT(rd_fh_.op == detail::opcode::close);
+ {
+ BOOST_ASSERT(! rd_close_);
+ rd_close_ = true;
+ close_reason cr;
+ detail::read_close(cr, b, code);
+ if(code != close_code::none)
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(code, error::failed, ec);
+ return bytes_written;
+ }
+ cr_ = cr;
+ rd_buf_.consume(len);
+ if(ctrl_cb_)
+ ctrl_cb_(frame_type::close, cr_.reason);
+ BOOST_ASSERT(! wr_close_);
+ // _Start the WebSocket Closing Handshake_
+ do_fail(
+ cr.code == close_code::none ?
+ close_code::normal :
+ static_cast<close_code>(cr.code),
+ error::closed, ec);
+ return bytes_written;
+ }
+ }
+ if(rd_fh_.len == 0 && ! rd_fh_.fin)
+ {
+ // Empty non-final frame
+ goto loop;
+ }
+ rd_done_ = false;
+ }
+ else
+ {
+ ec.assign(0, ec.category());
+ }
+ if(! pmd_ || ! pmd_->rd_set)
+ {
+ if(rd_remain_ > 0)
+ {
+ if(rd_buf_.size() == 0 && rd_buf_.max_size() >
+ (std::min)(clamp(rd_remain_),
+ buffer_size(buffers)))
+ {
+ // Fill the read buffer first, otherwise we
+ // get fewer bytes at the cost of one I/O.
+ rd_buf_.commit(stream_.read_some(
+ rd_buf_.prepare(read_size(rd_buf_,
+ rd_buf_.max_size())), ec));
+ if(! check_ok(ec))
+ return bytes_written;
+ if(rd_fh_.mask)
+ detail::mask_inplace(
+ buffers_prefix(clamp(rd_remain_),
+ rd_buf_.data()), rd_key_);
+ }
+ if(rd_buf_.size() > 0)
+ {
+ // Copy from the read buffer.
+ // The mask was already applied.
+ auto const bytes_transferred =
+ buffer_copy(buffers, rd_buf_.data(),
+ clamp(rd_remain_));
+ auto const mb = buffers_prefix(
+ bytes_transferred, buffers);
+ rd_remain_ -= bytes_transferred;
+ if(rd_op_ == detail::opcode::text)
+ {
+ if(! rd_utf8_.write(mb) ||
+ (rd_remain_ == 0 && rd_fh_.fin &&
+ ! rd_utf8_.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(
+ close_code::bad_payload,
+ error::failed,
+ ec);
+ return bytes_written;
+ }
+ }
+ bytes_written += bytes_transferred;
+ rd_size_ += bytes_transferred;
+ rd_buf_.consume(bytes_transferred);
+ }
+ else
+ {
+ // Read into caller's buffer
+ BOOST_ASSERT(rd_remain_ > 0);
+ BOOST_ASSERT(buffer_size(buffers) > 0);
+ BOOST_ASSERT(buffer_size(buffers_prefix(
+ clamp(rd_remain_), buffers)) > 0);
+ auto const bytes_transferred =
+ stream_.read_some(buffers_prefix(
+ clamp(rd_remain_), buffers), ec);
+ if(! check_ok(ec))
+ return bytes_written;
+ BOOST_ASSERT(bytes_transferred > 0);
+ auto const mb = buffers_prefix(
+ bytes_transferred, buffers);
+ rd_remain_ -= bytes_transferred;
+ if(rd_fh_.mask)
+ detail::mask_inplace(mb, rd_key_);
+ if(rd_op_ == detail::opcode::text)
+ {
+ if(! rd_utf8_.write(mb) ||
+ (rd_remain_ == 0 && rd_fh_.fin &&
+ ! rd_utf8_.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(close_code::bad_payload,
+ error::failed, ec);
+ return bytes_written;
+ }
+ }
+ bytes_written += bytes_transferred;
+ rd_size_ += bytes_transferred;
+ }
+ }
+ rd_done_ = rd_remain_ == 0 && rd_fh_.fin;
+ }
+ else
+ {
+ // Read compressed message frame payload:
+ // inflate even if rd_fh_.len == 0, otherwise we
+ // never emit the end-of-stream deflate block.
+ //
+ bool did_read = false;
+ buffers_suffix<MutableBufferSequence> cb{buffers};
+ while(buffer_size(cb) > 0)
+ {
+ zlib::z_params zs;
+ {
+ auto const out = buffers_front(cb);
+ zs.next_out = out.data();
+ zs.avail_out = out.size();
+ BOOST_ASSERT(zs.avail_out > 0);
+ }
+ if(rd_remain_ > 0)
+ {
+ if(rd_buf_.size() > 0)
+ {
+ // use what's there
+ auto const in = buffers_prefix(
+ clamp(rd_remain_), buffers_front(
+ rd_buf_.data()));
+ zs.avail_in = in.size();
+ zs.next_in = in.data();
+ }
+ else if(! did_read)
+ {
+ // read new
+ auto const bytes_transferred =
+ stream_.read_some(
+ rd_buf_.prepare(read_size(
+ rd_buf_, rd_buf_.max_size())),
+ ec);
+ if(! check_ok(ec))
+ return bytes_written;
+ BOOST_ASSERT(bytes_transferred > 0);
+ rd_buf_.commit(bytes_transferred);
+ if(rd_fh_.mask)
+ detail::mask_inplace(
+ buffers_prefix(clamp(rd_remain_),
+ rd_buf_.data()), rd_key_);
+ auto const in = buffers_prefix(
+ clamp(rd_remain_), buffers_front(
+ rd_buf_.data()));
+ zs.avail_in = in.size();
+ zs.next_in = in.data();
+ did_read = true;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if(rd_fh_.fin)
+ {
+ // append the empty block codes
+ static std::uint8_t constexpr
+ empty_block[4] = {
+ 0x00, 0x00, 0xff, 0xff };
+ zs.next_in = empty_block;
+ zs.avail_in = sizeof(empty_block);
+ pmd_->zi.write(zs, zlib::Flush::sync, ec);
+ if(! ec)
+ {
+ // https://github.com/madler/zlib/issues/280
+ if(zs.total_out > 0)
+ ec = error::partial_deflate_block;
+ }
+ 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();
+ rd_done_ = true;
+ break;
+ }
+ else
+ {
+ break;
+ }
+ pmd_->zi.write(zs, zlib::Flush::sync, ec);
+ if(! check_ok(ec))
+ return bytes_written;
+ if(rd_msg_max_ && beast::detail::sum_exceeds(
+ rd_size_, zs.total_out, rd_msg_max_))
+ {
+ do_fail(close_code::too_big,
+ error::failed, ec);
+ return bytes_written;
+ }
+ cb.consume(zs.total_out);
+ rd_size_ += zs.total_out;
+ rd_remain_ -= zs.total_in;
+ rd_buf_.consume(zs.total_in);
+ bytes_written += zs.total_out;
+ }
+ if(rd_op_ == detail::opcode::text)
+ {
+ // check utf8
+ if(! rd_utf8_.write(
+ buffers_prefix(bytes_written, buffers)) || (
+ rd_done_ && ! rd_utf8_.finish()))
+ {
+ // _Fail the WebSocket Connection_
+ do_fail(close_code::bad_payload,
+ error::failed, ec);
+ return bytes_written;
+ }
+ }
+ }
+ return bytes_written;
+}
+
+template<class NextLayer>
+template<class MutableBufferSequence, class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+stream<NextLayer>::
+async_read_some(
+ MutableBufferSequence const& buffers,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements requirements not met");
+ static_assert(boost::asio::is_mutable_buffer_sequence<
+ MutableBufferSequence>::value,
+ "MutableBufferSequence requirements not met");
+ boost::asio::async_completion<ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ read_some_op<MutableBufferSequence, BOOST_ASIO_HANDLER_TYPE(
+ ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler,*this, buffers}(
+ {}, 0, false);
+ return init.result.get();
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif \ No newline at end of file
diff --git a/boost/beast/websocket/impl/rfc6455.ipp b/boost/beast/websocket/impl/rfc6455.ipp
new file mode 100644
index 0000000000..d688b63d1b
--- /dev/null
+++ b/boost/beast/websocket/impl/rfc6455.ipp
@@ -0,0 +1,40 @@
+//
+// 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_IMPL_RFC6455_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_RFC6455_IPP
+
+#include <boost/beast/http/fields.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+template<class Allocator>
+bool
+is_upgrade(http::header<true,
+ http::basic_fields<Allocator>> const& req)
+{
+ if(req.version() < 11)
+ return false;
+ if(req.method() != http::verb::get)
+ return false;
+ if(! http::token_list{req["Connection"]}.exists("upgrade"))
+ return false;
+ if(! http::token_list{req["Upgrade"]}.exists("websocket"))
+ return false;
+ return true;
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/ssl.ipp b/boost/beast/websocket/impl/ssl.ipp
new file mode 100644
index 0000000000..442b45086a
--- /dev/null
+++ b/boost/beast/websocket/impl/ssl.ipp
@@ -0,0 +1,61 @@
+//
+// 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_IMPL_SSL_IPP_INCLUDED
+#define BOOST_BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
+
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/*
+
+See
+http://stackoverflow.com/questions/32046034/what-is-the-proper-way-to-securely-disconnect-an-asio-ssl-socket/32054476#32054476
+
+Behavior of ssl::stream regarding close_
+
+ If the remote host calls async_shutdown then the
+ local host's async_read will complete with eof.
+
+ If both hosts call async_shutdown then the calls
+ to async_shutdown will complete with eof.
+
+*/
+
+template<class AsyncStream>
+void
+teardown(
+ role_type,
+ boost::asio::ssl::stream<AsyncStream>& stream,
+ error_code& ec)
+{
+ stream.shutdown(ec);
+}
+
+template<
+ class AsyncStream,
+ class TeardownHandler>
+void
+async_teardown(
+ role_type,
+ boost::asio::ssl::stream<AsyncStream>& stream,
+ TeardownHandler&& handler)
+{
+ stream.async_shutdown(
+ std::forward<TeardownHandler>(handler));
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/stream.ipp b/boost/beast/websocket/impl/stream.ipp
new file mode 100644
index 0000000000..f8e4a5fc01
--- /dev/null
+++ b/boost/beast/websocket/impl/stream.ipp
@@ -0,0 +1,717 @@
+//
+// 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_IMPL_STREAM_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_STREAM_IPP
+
+#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/websocket/teardown.hpp>
+#include <boost/beast/websocket/detail/hybi13.hpp>
+#include <boost/beast/websocket/detail/pmd_extension.hpp>
+#include <boost/beast/version.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/http/write.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/assert.hpp>
+#include <boost/endian/buffers.hpp>
+#include <boost/make_unique.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <memory>
+#include <stdexcept>
+#include <utility>
+
+#include <iostream>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+template<class NextLayer>
+template<class... Args>
+stream<NextLayer>::
+stream(Args&&... args)
+ : stream_(std::forward<Args>(args)...)
+ , tok_(1)
+{
+ BOOST_ASSERT(rd_buf_.max_size() >=
+ max_control_frame_size);
+}
+
+template<class NextLayer>
+std::size_t
+stream<NextLayer>::
+read_size_hint(
+ std::size_t initial_size) const
+{
+ using beast::detail::clamp;
+ std::size_t result;
+ BOOST_ASSERT(initial_size > 0);
+ if(! pmd_ || (! rd_done_ && ! pmd_->rd_set))
+ {
+ // current message is uncompressed
+
+ if(rd_done_)
+ {
+ // first message frame
+ result = initial_size;
+ goto done;
+ }
+ else if(rd_fh_.fin)
+ {
+ // last message frame
+ BOOST_ASSERT(rd_remain_ > 0);
+ result = clamp(rd_remain_);
+ goto done;
+ }
+ }
+ result = (std::max)(
+ initial_size, clamp(rd_remain_));
+done:
+ BOOST_ASSERT(result != 0);
+ return result;
+}
+
+template<class NextLayer>
+template<class DynamicBuffer, class>
+std::size_t
+stream<NextLayer>::
+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>
+void
+stream<NextLayer>::
+set_option(permessage_deflate const& o)
+{
+ if( o.server_max_window_bits > 15 ||
+ o.server_max_window_bits < 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid server_max_window_bits"});
+ if( o.client_max_window_bits > 15 ||
+ o.client_max_window_bits < 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid client_max_window_bits"});
+ if( o.compLevel < 0 ||
+ o.compLevel > 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid compLevel"});
+ if( o.memLevel < 1 ||
+ o.memLevel > 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid memLevel"});
+ pmd_opts_ = o;
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+open(role_type role)
+{
+ // VFALCO TODO analyze and remove dupe code in reset()
+ role_ = role;
+ status_ = status::open;
+ rd_remain_ = 0;
+ rd_cont_ = false;
+ rd_done_ = true;
+ // Can't clear this because accept uses it
+ //rd_buf_.reset();
+ rd_fh_.fin = false;
+ rd_close_ = false;
+ wr_close_ = false;
+ wr_block_.reset();
+ rd_block_.reset();
+ cr_.code = close_code::none;
+
+ wr_cont_ = false;
+ wr_buf_size_ = 0;
+
+ if(((role_ == role_type::client && pmd_opts_.client_enable) ||
+ (role_ == role_type::server && pmd_opts_.server_enable)) &&
+ pmd_config_.accept)
+ {
+ pmd_normalize(pmd_config_);
+ 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);
+ }
+ }
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+close()
+{
+ wr_buf_.reset();
+ pmd_.reset();
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+reset()
+{
+ BOOST_ASSERT(status_ != status::open);
+ rd_remain_ = 0;
+ rd_cont_ = false;
+ rd_done_ = true;
+ rd_buf_.consume(rd_buf_.size());
+ rd_fh_.fin = false;
+ rd_close_ = false;
+ wr_close_ = false;
+ wr_cont_ = false;
+ wr_block_.reset();
+ rd_block_.reset();
+ cr_.code = close_code::none;
+}
+
+// Called before each write frame
+template<class NextLayer>
+void
+stream<NextLayer>::
+begin_msg()
+{
+ wr_frag_ = wr_frag_opt_;
+ wr_compress_ = static_cast<bool>(pmd_);
+
+ // Maintain the write buffer
+ if( wr_compress_ ||
+ role_ == role_type::client)
+ {
+ if(! wr_buf_ || wr_buf_size_ != wr_buf_opt_)
+ {
+ 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();
+ }
+}
+
+//------------------------------------------------------------------------------
+
+// Attempt to read a complete frame header.
+// Returns `false` if more bytes are needed
+template<class NextLayer>
+template<class DynamicBuffer>
+bool
+stream<NextLayer>::
+parse_fh(
+ detail::frame_header& fh,
+ DynamicBuffer& b,
+ close_code& code)
+{
+ using boost::asio::buffer;
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ auto const err =
+ [&](close_code cv)
+ {
+ code = cv;
+ return false;
+ };
+ if(buffer_size(b.data()) < 2)
+ {
+ code = close_code::none;
+ return false;
+ }
+ buffers_suffix<typename
+ DynamicBuffer::const_buffers_type> cb{
+ b.data()};
+ std::size_t need;
+ {
+ std::uint8_t tmp[2];
+ cb.consume(buffer_copy(buffer(tmp), cb));
+ fh.len = tmp[1] & 0x7f;
+ switch(fh.len)
+ {
+ case 126: need = 2; break;
+ case 127: need = 8; break;
+ default:
+ need = 0;
+ }
+ fh.mask = (tmp[1] & 0x80) != 0;
+ if(fh.mask)
+ need += 4;
+ if(buffer_size(cb) < need)
+ {
+ code = close_code::none;
+ return false;
+ }
+ fh.op = static_cast<
+ detail::opcode>(tmp[0] & 0x0f);
+ fh.fin = (tmp[0] & 0x80) != 0;
+ fh.rsv1 = (tmp[0] & 0x40) != 0;
+ fh.rsv2 = (tmp[0] & 0x20) != 0;
+ fh.rsv3 = (tmp[0] & 0x10) != 0;
+ }
+ switch(fh.op)
+ {
+ case detail::opcode::binary:
+ case detail::opcode::text:
+ if(rd_cont_)
+ {
+ // new data frame when continuation expected
+ return err(close_code::protocol_error);
+ }
+ if((fh.rsv1 && ! pmd_) ||
+ fh.rsv2 || fh.rsv3)
+ {
+ // reserved bits not cleared
+ return err(close_code::protocol_error);
+ }
+ if(pmd_)
+ pmd_->rd_set = fh.rsv1;
+ break;
+
+ case detail::opcode::cont:
+ if(! rd_cont_)
+ {
+ // continuation without an active message
+ return err(close_code::protocol_error);
+ }
+ if(fh.rsv1 || fh.rsv2 || fh.rsv3)
+ {
+ // reserved bits not cleared
+ return err(close_code::protocol_error);
+ }
+ break;
+
+ default:
+ if(detail::is_reserved(fh.op))
+ {
+ // reserved opcode
+ return err(close_code::protocol_error);
+ }
+ if(! fh.fin)
+ {
+ // fragmented control message
+ return err(close_code::protocol_error);
+ }
+ if(fh.len > 125)
+ {
+ // invalid length for control message
+ return err(close_code::protocol_error);
+ }
+ if(fh.rsv1 || fh.rsv2 || fh.rsv3)
+ {
+ // reserved bits not cleared
+ return err(close_code::protocol_error);
+ }
+ break;
+ }
+ // unmasked frame from client
+ if(role_ == role_type::server && ! fh.mask)
+ return err(close_code::protocol_error);
+ // masked frame from server
+ if(role_ == role_type::client && fh.mask)
+ return err(close_code::protocol_error);
+ if(detail::is_control(fh.op) &&
+ buffer_size(cb) < need + fh.len)
+ {
+ // Make the entire control frame payload
+ // get read in before we return `true`
+ return false;
+ }
+ switch(fh.len)
+ {
+ case 126:
+ {
+ std::uint8_t tmp[2];
+ BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
+ cb.consume(buffer_copy(buffer(tmp), cb));
+ fh.len = detail::big_uint16_to_native(&tmp[0]);
+ // length not canonical
+ if(fh.len < 126)
+ return err(close_code::protocol_error);
+ break;
+ }
+ case 127:
+ {
+ std::uint8_t tmp[8];
+ BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
+ cb.consume(buffer_copy(buffer(tmp), cb));
+ fh.len = detail::big_uint64_to_native(&tmp[0]);
+ // length not canonical
+ if(fh.len < 65536)
+ return err(close_code::protocol_error);
+ break;
+ }
+ }
+ if(fh.mask)
+ {
+ std::uint8_t tmp[4];
+ BOOST_ASSERT(buffer_size(cb) >= sizeof(tmp));
+ cb.consume(buffer_copy(buffer(tmp), cb));
+ fh.key = detail::little_uint32_to_native(&tmp[0]);
+ detail::prepare_key(rd_key_, fh.key);
+ }
+ else
+ {
+ // initialize this otherwise operator== breaks
+ fh.key = 0;
+ }
+ if(! detail::is_control(fh.op))
+ {
+ if(fh.op != detail::opcode::cont)
+ {
+ rd_size_ = 0;
+ rd_op_ = fh.op;
+ }
+ else
+ {
+ if(rd_size_ > (std::numeric_limits<
+ std::uint64_t>::max)() - fh.len)
+ return err(close_code::too_big);
+ }
+ if(! pmd_ || ! pmd_->rd_set)
+ {
+ if(rd_msg_max_ && beast::detail::sum_exceeds(
+ rd_size_, fh.len, rd_msg_max_))
+ return err(close_code::too_big);
+ }
+ rd_cont_ = ! fh.fin;
+ rd_remain_ = fh.len;
+ }
+ b.consume(b.size() - buffer_size(cb));
+ code = close_code::none;
+ return true;
+}
+
+template<class NextLayer>
+template<class DynamicBuffer>
+void
+stream<NextLayer>::
+write_close(DynamicBuffer& db, close_reason const& cr)
+{
+ using namespace boost::endian;
+ detail::frame_header fh;
+ fh.op = detail::opcode::close;
+ fh.fin = true;
+ fh.rsv1 = false;
+ fh.rsv2 = false;
+ fh.rsv3 = false;
+ fh.len = cr.code == close_code::none ?
+ 0 : 2 + cr.reason.size();
+ if(role_ == role_type::client)
+ {
+ fh.mask = true;
+ fh.key = wr_gen_();
+ }
+ else
+ {
+ fh.mask = false;
+ }
+ detail::write(db, fh);
+ if(cr.code != close_code::none)
+ {
+ detail::prepared_key key;
+ if(fh.mask)
+ detail::prepare_key(key, fh.key);
+ {
+ std::uint8_t tmp[2];
+ ::new(&tmp[0]) big_uint16_buf_t{
+ (std::uint16_t)cr.code};
+ auto mb = db.prepare(2);
+ boost::asio::buffer_copy(mb,
+ boost::asio::buffer(tmp));
+ if(fh.mask)
+ detail::mask_inplace(mb, key);
+ db.commit(2);
+ }
+ if(! cr.reason.empty())
+ {
+ auto mb = db.prepare(cr.reason.size());
+ boost::asio::buffer_copy(mb,
+ boost::asio::const_buffer(
+ cr.reason.data(), cr.reason.size()));
+ if(fh.mask)
+ detail::mask_inplace(mb, key);
+ db.commit(cr.reason.size());
+ }
+ }
+}
+
+template<class NextLayer>
+template<class DynamicBuffer>
+void
+stream<NextLayer>::
+write_ping(DynamicBuffer& db,
+ detail::opcode code, ping_data const& data)
+{
+ detail::frame_header fh;
+ fh.op = code;
+ fh.fin = true;
+ fh.rsv1 = false;
+ fh.rsv2 = false;
+ fh.rsv3 = false;
+ fh.len = data.size();
+ fh.mask = role_ == role_type::client;
+ if(fh.mask)
+ fh.key = wr_gen_();
+ detail::write(db, fh);
+ if(data.empty())
+ return;
+ detail::prepared_key key;
+ if(fh.mask)
+ detail::prepare_key(key, fh.key);
+ auto mb = db.prepare(data.size());
+ boost::asio::buffer_copy(mb,
+ boost::asio::const_buffer(
+ data.data(), data.size()));
+ if(fh.mask)
+ detail::mask_inplace(mb, key);
+ db.commit(data.size());
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<class Decorator>
+request_type
+stream<NextLayer>::
+build_request(detail::sec_ws_key_type& key,
+ string_view host,
+ string_view target,
+ Decorator const& decorator)
+{
+ request_type req;
+ req.target(target);
+ req.version(11);
+ req.method(http::verb::get);
+ req.set(http::field::host, host);
+ req.set(http::field::upgrade, "websocket");
+ req.set(http::field::connection, "upgrade");
+ 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);
+ }
+ decorator(req);
+ if(! req.count(http::field::user_agent))
+ req.set(http::field::user_agent,
+ BOOST_BEAST_VERSION_STRING);
+ return req;
+}
+
+template<class NextLayer>
+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)
+{
+ auto const decorate =
+ [&decorator](response_type& res)
+ {
+ decorator(res);
+ if(! res.count(http::field::server))
+ {
+ BOOST_STATIC_ASSERT(sizeof(BOOST_BEAST_VERSION_STRING) < 20);
+ static_string<20> s(BOOST_BEAST_VERSION_STRING);
+ res.set(http::field::server, s);
+ }
+ };
+ auto err =
+ [&](std::string const& text)
+ {
+ response_type res;
+ res.version(req.version());
+ res.result(http::status::bad_request);
+ res.body() = text;
+ res.prepare_payload();
+ decorate(res);
+ return res;
+ };
+ if(req.version() < 11)
+ return err("HTTP version 1.1 required");
+ if(req.method() != http::verb::get)
+ return err("Wrong method");
+ if(! is_upgrade(req))
+ return err("Expected Upgrade request");
+ if(! req.count(http::field::host))
+ return err("Missing Host");
+ if(! req.count(http::field::sec_websocket_key))
+ return err("Missing Sec-WebSocket-Key");
+ auto const key = req[http::field::sec_websocket_key];
+ if(key.size() > detail::sec_ws_key_type::max_size_n)
+ return err("Invalid Sec-WebSocket-Key");
+ {
+ auto const version =
+ req[http::field::sec_websocket_version];
+ if(version.empty())
+ return err("Missing Sec-WebSocket-Version");
+ if(version != "13")
+ {
+ response_type res;
+ res.result(http::status::upgrade_required);
+ res.version(req.version());
+ res.set(http::field::sec_websocket_version, "13");
+ res.prepare_payload();
+ decorate(res);
+ return res;
+ }
+ }
+
+ response_type res;
+ {
+ detail::pmd_offer offer;
+ detail::pmd_offer unused;
+ pmd_read(offer, req);
+ pmd_negotiate(res, unused, offer, pmd_opts_);
+ }
+ res.result(http::status::switching_protocols);
+ res.version(req.version());
+ res.set(http::field::upgrade, "websocket");
+ res.set(http::field::connection, "upgrade");
+ {
+ detail::sec_ws_accept_type acc;
+ detail::make_sec_ws_accept(acc, key);
+ res.set(http::field::sec_websocket_accept, acc);
+ }
+ decorate(res);
+ return res;
+}
+
+template<class NextLayer>
+void
+stream<NextLayer>::
+on_response(response_type const& res,
+ detail::sec_ws_key_type const& key, error_code& ec)
+{
+ bool const success = [&]()
+ {
+ if(res.version() < 11)
+ return false;
+ if(res.result() != http::status::switching_protocols)
+ return false;
+ if(! http::token_list{res[http::field::connection]}.exists("upgrade"))
+ return false;
+ if(! http::token_list{res[http::field::upgrade]}.exists("websocket"))
+ return false;
+ if(res.count(http::field::sec_websocket_accept) != 1)
+ return false;
+ detail::sec_ws_accept_type acc;
+ detail::make_sec_ws_accept(acc, key);
+ if(acc.compare(
+ res[http::field::sec_websocket_accept]) != 0)
+ return false;
+ return true;
+ }();
+ if(! success)
+ {
+ ec = error::handshake_failed;
+ return;
+ }
+ ec.assign(0, ec.category());
+ 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);
+}
+
+// _Fail the WebSocket Connection_
+template<class NextLayer>
+void
+stream<NextLayer>::
+do_fail(
+ std::uint16_t code, // if set, send a close frame first
+ error_code ev, // error code to use upon success
+ error_code& ec) // set to the error, else set to ev
+{
+ BOOST_ASSERT(ev);
+ status_ = status::closing;
+ if(code != close_code::none && ! wr_close_)
+ {
+ wr_close_ = true;
+ detail::frame_buffer fb;
+ write_close<
+ flat_static_buffer_base>(fb, code);
+ boost::asio::write(stream_, fb.data(), ec);
+ if(! check_ok(ec))
+ return;
+ }
+ using beast::websocket::teardown;
+ teardown(role_, stream_, ec);
+ if(ec == boost::asio::error::eof)
+ {
+ // Rationale:
+ // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
+ ec.assign(0, ec.category());
+ }
+ if(! ec)
+ ec = ev;
+ if(ec && ec != error::closed)
+ status_ = status::failed;
+ else
+ status_ = status::closed;
+ close();
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/teardown.ipp b/boost/beast/websocket/impl/teardown.ipp
new file mode 100644
index 0000000000..8f4475f246
--- /dev/null
+++ b/boost/beast/websocket/impl/teardown.ipp
@@ -0,0 +1,187 @@
+//
+// 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_IMPL_TEARDOWN_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
+
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+namespace detail {
+
+template<class Handler>
+class teardown_tcp_op
+{
+ using socket_type =
+ boost::asio::ip::tcp::socket;
+
+ Handler h_;
+ socket_type& s_;
+ role_type role_;
+ int step_ = 0;
+
+public:
+ teardown_tcp_op(teardown_tcp_op&& other) = default;
+ teardown_tcp_op(teardown_tcp_op const& other) = default;
+
+ template<class DeducedHandler>
+ teardown_tcp_op(
+ DeducedHandler&& h,
+ socket_type& s,
+ role_type role)
+ : h_(std::forward<DeducedHandler>(h))
+ , s_(s)
+ , role_(role)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<socket_type&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(teardown_tcp_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->step_ >= 3 ||
+ asio_handler_is_continuation(std::addressof(op->h_));
+ }
+};
+
+template<class Handler>
+void
+teardown_tcp_op<Handler>::
+operator()(error_code ec, std::size_t)
+{
+ using boost::asio::buffer;
+ using tcp = boost::asio::ip::tcp;
+ switch(step_)
+ {
+ case 0:
+ s_.non_blocking(true, ec);
+ if(ec)
+ {
+ step_ = 1;
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+ }
+ step_ = 2;
+ if(role_ == role_type::server)
+ s_.shutdown(tcp::socket::shutdown_send, ec);
+ goto do_read;
+
+ case 1:
+ break;
+
+ case 2:
+ step_ = 3;
+
+ case 3:
+ if(ec != boost::asio::error::would_block)
+ break;
+ {
+ char buf[2048];
+ s_.read_some(
+ boost::asio::buffer(buf), ec);
+ if(ec)
+ break;
+ }
+
+ do_read:
+ return s_.async_read_some(
+ boost::asio::null_buffers{},
+ std::move(*this));
+ }
+ if(role_ == role_type::client)
+ s_.shutdown(tcp::socket::shutdown_send, ec);
+ s_.close(ec);
+ h_(ec);
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+inline
+void
+teardown(
+ role_type role,
+ boost::asio::ip::tcp::socket& socket,
+ error_code& ec)
+{
+ using boost::asio::buffer;
+ if(role == role_type::server)
+ socket.shutdown(
+ boost::asio::ip::tcp::socket::shutdown_send, ec);
+ while(! ec)
+ {
+ char buf[8192];
+ auto const n = socket.read_some(
+ buffer(buf), ec);
+ if(! n)
+ break;
+ }
+ if(role == role_type::client)
+ socket.shutdown(
+ boost::asio::ip::tcp::socket::shutdown_send, ec);
+ socket.close(ec);
+}
+
+template<class TeardownHandler>
+inline
+void
+async_teardown(
+ role_type role,
+ boost::asio::ip::tcp::socket& socket,
+ TeardownHandler&& handler)
+{
+ static_assert(beast::is_completion_handler<
+ TeardownHandler, void(error_code)>::value,
+ "TeardownHandler requirements not met");
+ detail::teardown_tcp_op<typename std::decay<
+ TeardownHandler>::type>{std::forward<
+ TeardownHandler>(handler), socket,
+ role}();
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/impl/write.ipp b/boost/beast/websocket/impl/write.ipp
new file mode 100644
index 0000000000..b04f2826fc
--- /dev/null
+++ b/boost/beast/websocket/impl/write.ipp
@@ -0,0 +1,797 @@
+//
+// 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_IMPL_WRITE_IPP
+#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_IPP
+
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/buffers_prefix.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/flat_static_buffer.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/websocket/detail/frame.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/coroutine.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/assert.hpp>
+#include <boost/config.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+template<class NextLayer>
+template<class Buffers, class Handler>
+class stream<NextLayer>::write_some_op
+ : public boost::asio::coroutine
+{
+ Handler h_;
+ stream<NextLayer>& ws_;
+ buffers_suffix<Buffers> cb_;
+ detail::frame_header fh_;
+ detail::prepared_key key_;
+ std::size_t bytes_transferred_ = 0;
+ std::size_t remain_;
+ std::size_t in_;
+ token tok_;
+ int how_;
+ bool fin_;
+ bool more_;
+ bool cont_ = false;
+
+public:
+ write_some_op(write_some_op&&) = default;
+ write_some_op(write_some_op const&) = default;
+
+ template<class DeducedHandler>
+ write_some_op(
+ DeducedHandler&& h,
+ stream<NextLayer>& ws,
+ bool fin,
+ Buffers const& bs)
+ : h_(std::forward<DeducedHandler>(h))
+ , ws_(ws)
+ , cb_(bs)
+ , tok_(ws_.tok_.unique())
+ , fin_(fin)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, ws_.get_executor());
+ }
+
+ Handler&
+ handler()
+ {
+ return h_;
+ }
+
+ void operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0,
+ bool cont = true);
+
+ friend
+ bool asio_handler_is_continuation(write_some_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->cont_ || asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<class NextLayer>
+template<class Buffers, class Handler>
+void
+stream<NextLayer>::
+write_some_op<Buffers, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred,
+ bool cont)
+{
+ using beast::detail::clamp;
+ using boost::asio::buffer;
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ using boost::asio::mutable_buffer;
+ enum
+ {
+ do_nomask_nofrag,
+ do_nomask_frag,
+ do_mask_nofrag,
+ do_mask_frag,
+ do_deflate
+ };
+ std::size_t n;
+ boost::asio::mutable_buffer b;
+ cont_ = cont;
+ BOOST_ASIO_CORO_REENTER(*this)
+ {
+ // Set up the outgoing frame header
+ if(! ws_.wr_cont_)
+ {
+ ws_.begin_msg();
+ fh_.rsv1 = ws_.wr_compress_;
+ }
+ else
+ {
+ fh_.rsv1 = false;
+ }
+ fh_.rsv2 = false;
+ fh_.rsv3 = false;
+ fh_.op = ws_.wr_cont_ ?
+ detail::opcode::cont : ws_.wr_opcode_;
+ fh_.mask =
+ ws_.role_ == role_type::client;
+
+ // Choose a write algorithm
+ if(ws_.wr_compress_)
+ {
+ how_ = do_deflate;
+ }
+ else if(! fh_.mask)
+ {
+ if(! ws_.wr_frag_)
+ {
+ how_ = do_nomask_nofrag;
+ }
+ else
+ {
+ BOOST_ASSERT(ws_.wr_buf_size_ != 0);
+ remain_ = buffer_size(cb_);
+ if(remain_ > ws_.wr_buf_size_)
+ how_ = do_nomask_frag;
+ else
+ how_ = do_nomask_nofrag;
+ }
+ }
+ else
+ {
+ if(! ws_.wr_frag_)
+ {
+ how_ = do_mask_nofrag;
+ }
+ else
+ {
+ BOOST_ASSERT(ws_.wr_buf_size_ != 0);
+ remain_ = buffer_size(cb_);
+ if(remain_ > ws_.wr_buf_size_)
+ how_ = do_mask_frag;
+ else
+ how_ = do_mask_nofrag;
+ }
+ }
+
+ // Maybe suspend
+ if(! ws_.wr_block_)
+ {
+ // Acquire the write block
+ ws_.wr_block_ = tok_;
+
+ // Make sure the stream is open
+ if(! ws_.check_open(ec))
+ goto upcall;
+ }
+ else
+ {
+ do_suspend:
+ // Suspend
+ BOOST_ASSERT(ws_.wr_block_ != tok_);
+ BOOST_ASIO_CORO_YIELD
+ ws_.paused_wr_.save(std::move(*this));
+
+ // Acquire the write block
+ BOOST_ASSERT(! ws_.wr_block_);
+ ws_.wr_block_ = tok_;
+
+ // Resume
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::post(
+ ws_.get_executor(), std::move(*this));
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+
+ // Make sure the stream is open
+ if(! ws_.check_open(ec))
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ if(how_ == do_nomask_nofrag)
+ {
+ fh_.fin = fin_;
+ fh_.len = buffer_size(cb_);
+ ws_.wr_fb_.reset();
+ detail::write<flat_static_buffer_base>(
+ ws_.wr_fb_, fh_);
+ ws_.wr_cont_ = ! fin_;
+ // Send frame
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(ws_.stream_,
+ buffers_cat(ws_.wr_fb_.data(), cb_),
+ std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ bytes_transferred_ += clamp(fh_.len);
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ else if(how_ == do_nomask_frag)
+ {
+ for(;;)
+ {
+ n = clamp(remain_, ws_.wr_buf_size_);
+ fh_.len = n;
+ remain_ -= n;
+ fh_.fin = fin_ ? remain_ == 0 : false;
+ ws_.wr_fb_.reset();
+ detail::write<flat_static_buffer_base>(
+ ws_.wr_fb_, fh_);
+ ws_.wr_cont_ = ! fin_;
+ // Send frame
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(
+ ws_.stream_, buffers_cat(
+ ws_.wr_fb_.data(), buffers_prefix(
+ clamp(fh_.len), cb_)),
+ std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ n = clamp(fh_.len); // because yield
+ bytes_transferred_ += n;
+ if(remain_ == 0)
+ break;
+ cb_.consume(n);
+ fh_.op = detail::opcode::cont;
+ // Allow outgoing control frames to
+ // be sent in between message frames
+ ws_.wr_block_.reset();
+ if( ws_.paused_close_.maybe_invoke() ||
+ ws_.paused_rd_.maybe_invoke() ||
+ ws_.paused_ping_.maybe_invoke())
+ {
+ BOOST_ASSERT(ws_.wr_block_);
+ goto do_suspend;
+ }
+ ws_.wr_block_ = tok_;
+ }
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ else if(how_ == do_mask_nofrag)
+ {
+ remain_ = buffer_size(cb_);
+ fh_.fin = fin_;
+ fh_.len = remain_;
+ fh_.key = ws_.wr_gen_();
+ detail::prepare_key(key_, fh_.key);
+ ws_.wr_fb_.reset();
+ detail::write<flat_static_buffer_base>(
+ ws_.wr_fb_, fh_);
+ n = clamp(remain_, ws_.wr_buf_size_);
+ buffer_copy(buffer(
+ ws_.wr_buf_.get(), n), cb_);
+ detail::mask_inplace(buffer(
+ ws_.wr_buf_.get(), n), key_);
+ remain_ -= n;
+ ws_.wr_cont_ = ! fin_;
+ // Send frame header and partial payload
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(
+ ws_.stream_, buffers_cat(ws_.wr_fb_.data(),
+ buffer(ws_.wr_buf_.get(), n)),
+ std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ bytes_transferred_ +=
+ bytes_transferred - ws_.wr_fb_.size();
+ while(remain_ > 0)
+ {
+ cb_.consume(ws_.wr_buf_size_);
+ n = clamp(remain_, ws_.wr_buf_size_);
+ buffer_copy(buffer(
+ ws_.wr_buf_.get(), n), cb_);
+ detail::mask_inplace(buffer(
+ ws_.wr_buf_.get(), n), key_);
+ remain_ -= n;
+ // Send partial payload
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(ws_.stream_,
+ buffer(ws_.wr_buf_.get(), n),
+ std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ bytes_transferred_ += bytes_transferred;
+ }
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ else if(how_ == do_mask_frag)
+ {
+ for(;;)
+ {
+ n = clamp(remain_, ws_.wr_buf_size_);
+ remain_ -= n;
+ fh_.len = n;
+ fh_.key = ws_.wr_gen_();
+ fh_.fin = fin_ ? remain_ == 0 : false;
+ detail::prepare_key(key_, fh_.key);
+ buffer_copy(buffer(
+ ws_.wr_buf_.get(), n), cb_);
+ detail::mask_inplace(buffer(
+ ws_.wr_buf_.get(), n), key_);
+ ws_.wr_fb_.reset();
+ detail::write<flat_static_buffer_base>(
+ ws_.wr_fb_, fh_);
+ ws_.wr_cont_ = ! fin_;
+ // Send frame
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(ws_.stream_,
+ buffers_cat(ws_.wr_fb_.data(),
+ buffer(ws_.wr_buf_.get(), n)),
+ std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ n = bytes_transferred - ws_.wr_fb_.size();
+ bytes_transferred_ += n;
+ if(remain_ == 0)
+ break;
+ cb_.consume(n);
+ fh_.op = detail::opcode::cont;
+ // Allow outgoing control frames to
+ // be sent in between message frames:
+ ws_.wr_block_.reset();
+ if( ws_.paused_close_.maybe_invoke() ||
+ ws_.paused_rd_.maybe_invoke() ||
+ ws_.paused_ping_.maybe_invoke())
+ {
+ BOOST_ASSERT(ws_.wr_block_);
+ goto do_suspend;
+ }
+ ws_.wr_block_ = tok_;
+ }
+ goto upcall;
+ }
+
+ //------------------------------------------------------------------
+
+ else if(how_ == do_deflate)
+ {
+ for(;;)
+ {
+ b = buffer(ws_.wr_buf_.get(),
+ ws_.wr_buf_size_);
+ more_ = detail::deflate(ws_.pmd_->zo,
+ b, cb_, fin_, in_, ec);
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ n = buffer_size(b);
+ if(n == 0)
+ {
+ // The input was consumed, but there
+ // is no output due to compression
+ // latency.
+ BOOST_ASSERT(! fin_);
+ BOOST_ASSERT(buffer_size(cb_) == 0);
+ goto upcall;
+ }
+ if(fh_.mask)
+ {
+ fh_.key = ws_.wr_gen_();
+ detail::prepared_key key;
+ detail::prepare_key(key, fh_.key);
+ detail::mask_inplace(b, key);
+ }
+ fh_.fin = ! more_;
+ fh_.len = n;
+ ws_.wr_fb_.reset();
+ detail::write<
+ flat_static_buffer_base>(ws_.wr_fb_, fh_);
+ ws_.wr_cont_ = ! fin_;
+ // Send frame
+ BOOST_ASIO_CORO_YIELD
+ boost::asio::async_write(ws_.stream_,
+ buffers_cat(ws_.wr_fb_.data(), b),
+ std::move(*this));
+ if(! ws_.check_ok(ec))
+ goto upcall;
+ bytes_transferred_ += in_;
+ if(more_)
+ {
+ fh_.op = detail::opcode::cont;
+ fh_.rsv1 = false;
+ // Allow outgoing control frames to
+ // be sent in between message frames:
+ ws_.wr_block_.reset();
+ if( ws_.paused_close_.maybe_invoke() ||
+ ws_.paused_rd_.maybe_invoke() ||
+ ws_.paused_ping_.maybe_invoke())
+ {
+ BOOST_ASSERT(ws_.wr_block_);
+ goto do_suspend;
+ }
+ ws_.wr_block_ = tok_;
+ }
+ 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();
+ goto upcall;
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+
+ upcall:
+ BOOST_ASSERT(ws_.wr_block_ == tok_);
+ ws_.wr_block_.reset();
+ ws_.paused_close_.maybe_invoke() ||
+ ws_.paused_rd_.maybe_invoke() ||
+ ws_.paused_ping_.maybe_invoke();
+ if(! cont_)
+ return boost::asio::post(
+ ws_.stream_.get_executor(),
+ bind_handler(h_, ec, bytes_transferred_));
+ h_(ec, bytes_transferred_);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<class ConstBufferSequence>
+std::size_t
+stream<NextLayer>::
+write_some(bool fin, ConstBufferSequence const& buffers)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ write_some(fin, buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<class NextLayer>
+template<class ConstBufferSequence>
+std::size_t
+stream<NextLayer>::
+write_some(bool fin,
+ ConstBufferSequence const& buffers, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ using beast::detail::clamp;
+ using boost::asio::buffer;
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ std::size_t bytes_transferred = 0;
+ ec.assign(0, ec.category());
+ // Make sure the stream is open
+ if(! check_open(ec))
+ return bytes_transferred;
+ detail::frame_header fh;
+ if(! wr_cont_)
+ {
+ begin_msg();
+ fh.rsv1 = wr_compress_;
+ }
+ else
+ {
+ fh.rsv1 = false;
+ }
+ fh.rsv2 = false;
+ fh.rsv3 = false;
+ fh.op = wr_cont_ ?
+ detail::opcode::cont : wr_opcode_;
+ fh.mask = role_ == role_type::client;
+ auto remain = buffer_size(buffers);
+ if(wr_compress_)
+ {
+ buffers_suffix<
+ ConstBufferSequence> cb{buffers};
+ for(;;)
+ {
+ auto b = buffer(
+ wr_buf_.get(), wr_buf_size_);
+ auto const more = detail::deflate(
+ pmd_->zo, b, cb, fin,
+ bytes_transferred, ec);
+ if(! check_ok(ec))
+ return bytes_transferred;
+ auto const n = buffer_size(b);
+ if(n == 0)
+ {
+ // The input was consumed, but there
+ // is no output due to compression
+ // latency.
+ BOOST_ASSERT(! fin);
+ BOOST_ASSERT(buffer_size(cb) == 0);
+ fh.fin = false;
+ break;
+ }
+ if(fh.mask)
+ {
+ fh.key = wr_gen_();
+ detail::prepared_key key;
+ detail::prepare_key(key, fh.key);
+ detail::mask_inplace(b, key);
+ }
+ fh.fin = ! more;
+ fh.len = n;
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ wr_cont_ = ! fin;
+ boost::asio::write(stream_,
+ buffers_cat(fh_buf.data(), b), ec);
+ if(! check_ok(ec))
+ return bytes_transferred;
+ if(! more)
+ break;
+ 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();
+ }
+ else if(! fh.mask)
+ {
+ if(! wr_frag_)
+ {
+ // no mask, no autofrag
+ fh.fin = fin;
+ fh.len = remain;
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ wr_cont_ = ! fin;
+ boost::asio::write(stream_,
+ buffers_cat(fh_buf.data(), buffers), ec);
+ if(! check_ok(ec))
+ return bytes_transferred;
+ bytes_transferred += remain;
+ }
+ else
+ {
+ // no mask, autofrag
+ BOOST_ASSERT(wr_buf_size_ != 0);
+ buffers_suffix<
+ ConstBufferSequence> cb{buffers};
+ for(;;)
+ {
+ auto const n = clamp(remain, wr_buf_size_);
+ remain -= n;
+ fh.len = n;
+ fh.fin = fin ? remain == 0 : false;
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ wr_cont_ = ! fin;
+ boost::asio::write(stream_,
+ buffers_cat(fh_buf.data(),
+ buffers_prefix(n, cb)), ec);
+ if(! check_ok(ec))
+ return bytes_transferred;
+ bytes_transferred += n;
+ if(remain == 0)
+ break;
+ fh.op = detail::opcode::cont;
+ cb.consume(n);
+ }
+ }
+ }
+ else if(! wr_frag_)
+ {
+ // mask, no autofrag
+ fh.fin = fin;
+ fh.len = remain;
+ fh.key = wr_gen_();
+ detail::prepared_key key;
+ detail::prepare_key(key, fh.key);
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ buffers_suffix<
+ ConstBufferSequence> cb{buffers};
+ {
+ auto const n = clamp(remain, wr_buf_size_);
+ auto const b = buffer(wr_buf_.get(), n);
+ buffer_copy(b, cb);
+ cb.consume(n);
+ remain -= n;
+ detail::mask_inplace(b, key);
+ wr_cont_ = ! fin;
+ boost::asio::write(stream_,
+ buffers_cat(fh_buf.data(), b), ec);
+ if(! check_ok(ec))
+ return bytes_transferred;
+ bytes_transferred += n;
+ }
+ while(remain > 0)
+ {
+ auto const n = clamp(remain, wr_buf_size_);
+ auto const b = buffer(wr_buf_.get(), n);
+ buffer_copy(b, cb);
+ cb.consume(n);
+ remain -= n;
+ detail::mask_inplace(b, key);
+ boost::asio::write(stream_, b, ec);
+ if(! check_ok(ec))
+ return bytes_transferred;
+ bytes_transferred += n;
+ }
+ }
+ else
+ {
+ // mask, autofrag
+ BOOST_ASSERT(wr_buf_size_ != 0);
+ buffers_suffix<
+ ConstBufferSequence> cb{buffers};
+ for(;;)
+ {
+ fh.key = wr_gen_();
+ detail::prepared_key key;
+ detail::prepare_key(key, fh.key);
+ auto const n = clamp(remain, wr_buf_size_);
+ auto const b = buffer(wr_buf_.get(), n);
+ buffer_copy(b, cb);
+ detail::mask_inplace(b, key);
+ fh.len = n;
+ remain -= n;
+ fh.fin = fin ? remain == 0 : false;
+ wr_cont_ = ! fh.fin;
+ detail::fh_buffer fh_buf;
+ detail::write<
+ flat_static_buffer_base>(fh_buf, fh);
+ boost::asio::write(stream_,
+ buffers_cat(fh_buf.data(), b), ec);
+ if(! check_ok(ec))
+ return bytes_transferred;
+ bytes_transferred += n;
+ if(remain == 0)
+ break;
+ fh.op = detail::opcode::cont;
+ cb.consume(n);
+ }
+ }
+ return bytes_transferred;
+}
+
+template<class NextLayer>
+template<class ConstBufferSequence, class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+stream<NextLayer>::
+async_write_some(bool fin,
+ ConstBufferSequence const& bs, WriteHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ boost::asio::async_completion<WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
+ WriteHandler, void(error_code, std::size_t))>{
+ init.completion_handler, *this, fin, bs}(
+ {}, 0, false);
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<class NextLayer>
+template<class ConstBufferSequence>
+std::size_t
+stream<NextLayer>::
+write(ConstBufferSequence const& buffers)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ error_code ec;
+ auto const bytes_transferred = write(buffers, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<class NextLayer>
+template<class ConstBufferSequence>
+std::size_t
+stream<NextLayer>::
+write(ConstBufferSequence const& buffers, error_code& ec)
+{
+ static_assert(is_sync_stream<next_layer_type>::value,
+ "SyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ return write_some(true, buffers, ec);
+}
+
+template<class NextLayer>
+template<class ConstBufferSequence, class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+stream<NextLayer>::
+async_write(
+ ConstBufferSequence const& bs, WriteHandler&& handler)
+{
+ static_assert(is_async_stream<next_layer_type>::value,
+ "AsyncStream requirements not met");
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ boost::asio::async_completion<WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ write_some_op<ConstBufferSequence, BOOST_ASIO_HANDLER_TYPE(
+ WriteHandler, void(error_code, std::size_t))>{
+ init.completion_handler, *this, true, bs}(
+ {}, 0, false);
+ return init.result.get();
+}
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/option.hpp b/boost/beast/websocket/option.hpp
new file mode 100644
index 0000000000..83788994d1
--- /dev/null
+++ b/boost/beast/websocket/option.hpp
@@ -0,0 +1,73 @@
+//
+// 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_OPTION_HPP
+#define BOOST_BEAST_WEBSOCKET_OPTION_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/** permessage-deflate extension options.
+
+ These settings control the permessage-deflate extension,
+ which allows messages to be compressed.
+
+ @note Objects of this type are used with
+ @ref beast::websocket::stream::set_option.
+*/
+struct permessage_deflate
+{
+ /// `true` to offer the extension in the server role
+ bool server_enable = false;
+
+ /// `true` to offer the extension in the client role
+ bool client_enable = false;
+
+ /** Maximum server window bits to offer
+
+ @note Due to a bug in ZLib, this value must be greater than 8.
+ */
+ int server_max_window_bits = 15;
+
+ /** Maximum client window bits to offer
+
+ @note Due to a bug in ZLib, this value must be greater than 8.
+ */
+ int client_max_window_bits = 15;
+
+ /// `true` if server_no_context_takeover desired
+ bool server_no_context_takeover = false;
+
+ /// `true` if client_no_context_takeover desired
+ bool client_no_context_takeover = false;
+
+ /// Deflate compression level 0..9
+ int compLevel = 8;
+
+ /// Deflate memory level, 1..9
+ int memLevel = 4;
+};
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/rfc6455.hpp b/boost/beast/websocket/rfc6455.hpp
new file mode 100644
index 0000000000..921f896351
--- /dev/null
+++ b/boost/beast/websocket/rfc6455.hpp
@@ -0,0 +1,215 @@
+//
+// 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_RFC6455_HPP
+#define BOOST_BEAST_WEBSOCKET_RFC6455_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/http/message.hpp>
+#include <array>
+#include <cstdint>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/** Returns `true` if the specified HTTP request is a WebSocket Upgrade.
+
+ This function returns `true` when the passed HTTP Request
+ indicates a WebSocket Upgrade. It does not validate the
+ contents of the fields: it just trivially accepts requests
+ which could only possibly be a valid or invalid WebSocket
+ Upgrade message.
+
+ Callers who wish to manually read HTTP requests in their
+ server implementation can use this function to determine if
+ the request should be routed to an instance of
+ @ref websocket::stream.
+
+ @par Example
+ @code
+ void handle_connection(boost::asio::ip::tcp::socket& sock)
+ {
+ boost::beast::flat_buffer buffer;
+ boost::beast::http::request<boost::beast::http::string_body> req;
+ boost::beast::http::read(sock, buffer, req);
+ if(boost::beast::websocket::is_upgrade(req))
+ {
+ boost::beast::websocket::stream<decltype(sock)> ws{std::move(sock)};
+ ws.accept(req);
+ }
+ }
+ @endcode
+
+ @param req The HTTP Request object to check.
+
+ @return `true` if the request is a WebSocket Upgrade.
+*/
+template<class Allocator>
+bool
+is_upgrade(beast::http::header<true,
+ http::basic_fields<Allocator>> const& req);
+
+/** Close status codes.
+
+ These codes accompany close frames.
+
+ @see <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455 7.4.1 Defined Status Codes</a>
+*/
+enum close_code : std::uint16_t
+{
+ /// Normal closure; the connection successfully completed whatever purpose for which it was created.
+ normal = 1000,
+
+ /// The endpoint is going away, either because of a server failure or because the browser is navigating away from the page that opened the connection.
+ going_away = 1001,
+
+ /// The endpoint is terminating the connection due to a protocol error.
+ protocol_error = 1002,
+
+ /// The connection is being terminated because the endpoint received data of a type it cannot accept (for example, a text-only endpoint received binary data).
+ unknown_data = 1003,
+
+ /// The endpoint is terminating the connection because a message was received that contained inconsistent data (e.g., non-UTF-8 data within a text message).
+ bad_payload = 1007,
+
+ /// The endpoint is terminating the connection because it received a message that violates its policy. This is a generic status code, used when codes 1003 and 1009 are not suitable.
+ policy_error = 1008,
+
+ /// The endpoint is terminating the connection because a data frame was received that is too large.
+ too_big = 1009,
+
+ /// The client is terminating the connection because it expected the server to negotiate one or more extension, but the server didn't.
+ needs_extension = 1010,
+
+ /// The server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request.
+ internal_error = 1011,
+
+ /// The server is terminating the connection because it is restarting.
+ service_restart = 1012,
+
+ /// The server is terminating the connection due to a temporary condition, e.g. it is overloaded and is casting off some of its clients.
+ try_again_later = 1013,
+
+ //----
+ //
+ // The following are illegal on the wire
+ //
+
+ /** Used internally to mean "no error"
+
+ This code is reserved and may not be sent.
+ */
+ none = 0,
+
+ /** Reserved for future use by the WebSocket standard.
+
+ This code is reserved and may not be sent.
+ */
+ reserved1 = 1004,
+
+ /** No status code was provided even though one was expected.
+
+ This code is reserved and may not be sent.
+ */
+ no_status = 1005,
+
+ /** Connection was closed without receiving a close frame
+
+ This code is reserved and may not be sent.
+ */
+ abnormal = 1006,
+
+ /** Reserved for future use by the WebSocket standard.
+
+ This code is reserved and may not be sent.
+ */
+ reserved2 = 1014,
+
+ /** Reserved for future use by the WebSocket standard.
+
+ This code is reserved and may not be sent.
+ */
+ reserved3 = 1015
+
+ //
+ //----
+
+ //last = 5000 // satisfy warnings
+};
+
+/// The type representing the reason string in a close frame.
+using reason_string = static_string<123, char>;
+
+/// The type representing the payload of ping and pong messages.
+using ping_data = static_string<125, char>;
+
+/** Description of the close reason.
+
+ This object stores the close code (if any) and the optional
+ utf-8 encoded implementation defined reason string.
+*/
+struct close_reason
+{
+ /// The close code.
+ std::uint16_t code = close_code::none;
+
+ /// The optional utf8-encoded reason string.
+ reason_string reason;
+
+ /** Default constructor.
+
+ The code will be none. Default constructed objects
+ will explicitly convert to bool as `false`.
+ */
+ close_reason() = default;
+
+ /// Construct from a code.
+ close_reason(std::uint16_t code_)
+ : code(code_)
+ {
+ }
+
+ /// Construct from a reason string. code is @ref close_code::normal.
+ close_reason(string_view s)
+ : code(close_code::normal)
+ , reason(s)
+ {
+ }
+
+ /// Construct from a reason string literal. code is @ref close_code::normal.
+ close_reason(char const* s)
+ : code(close_code::normal)
+ , reason(s)
+ {
+ }
+
+ /// Construct from a close code and reason string.
+ close_reason(close_code code_, string_view s)
+ : code(code_)
+ , reason(s)
+ {
+ }
+
+ /// Returns `true` if a code was specified
+ operator bool() const
+ {
+ return code != close_code::none;
+ }
+};
+
+} // websocket
+} // beast
+} // boost
+
+#include <boost/beast/websocket/impl/rfc6455.ipp>
+
+#endif
diff --git a/boost/beast/websocket/role.hpp b/boost/beast/websocket/role.hpp
new file mode 100644
index 0000000000..d886936585
--- /dev/null
+++ b/boost/beast/websocket/role.hpp
@@ -0,0 +1,59 @@
+//
+// 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_ROLE_HPP
+#define BOOST_BEAST_WEBSOCKET_ROLE_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/** The role of the websocket stream endpoint.
+
+ Whether the endpoint is a client or server affects the
+ behavior of the <em>Close the WebSocket Connection</em>
+ operation described in rfc6455 section 7.1.1.
+ The shutdown behavior depends on the type of the next
+ layer template parameter used to construct the @ref stream.
+ Other next layer types including user-defined types
+ may implement different role-based behavior when
+ performing the close operation.
+
+ The default implementation for @ref stream when the next
+ layer type is a `boost::asio::ip::tcp::socket` behaves
+ as follows:
+
+ @li In the client role, a TCP/IP shutdown is sent after
+ reading all remaining data on the connection.
+
+ @li In the server role, a TCP/IP shutdown is sent before
+ reading all remaining data on the connection.
+
+ When the next layer type is a `boost::asio::ssl::stream`,
+ the connection is closed by performing the SSL closing
+ handshake corresponding to the role type, client or server.
+
+ @see https://tools.ietf.org/html/rfc6455#section-7.1.1
+*/
+enum class role_type
+{
+ /// The stream is operating as a client.
+ client,
+
+ /// The stream is operating as a server.
+ server
+};
+
+} // websocket
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/websocket/ssl.hpp b/boost/beast/websocket/ssl.hpp
new file mode 100644
index 0000000000..44d5dc8082
--- /dev/null
+++ b/boost/beast/websocket/ssl.hpp
@@ -0,0 +1,84 @@
+//
+// 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_SSL_HPP
+#define BOOST_BEAST_WEBSOCKET_SSL_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/websocket/teardown.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/ssl/stream.hpp>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/** Tear down a `boost::asio::ssl::stream`.
+
+ This tears down a connection. The implementation will call
+ the overload of this function based on the `Stream` parameter
+ used to consruct the socket. When `Stream` is a user defined
+ type, and not a `boost::asio::ip::tcp::socket` or any
+ `boost::asio::ssl::stream`, callers are responsible for
+ providing a suitable overload of this function.
+
+ @param role The role of the local endpoint
+
+ @param stream The stream to tear down.
+
+ @param ec Set to the error if any occurred.
+*/
+template<class SyncStream>
+void
+teardown(
+ role_type role,
+ boost::asio::ssl::stream<SyncStream>& stream,
+ error_code& ec);
+
+/** Start tearing down a `boost::asio::ssl::stream`.
+
+ This begins tearing down a connection asynchronously.
+ The implementation will call the overload of this function
+ based on the `Stream` parameter used to consruct the socket.
+ When `Stream` is a user defined type, and not a
+ `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
+ callers are responsible for providing a suitable overload
+ of this function.
+
+ @param role The role of the local endpoint
+
+ @param stream The stream to tear down.
+
+ @param handler The handler to be called when the request completes.
+ Copies will be made of the handler as required. The equivalent
+ function signature of the handler must be:
+ @code void handler(
+ error_code const& error // result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using boost::asio::io_context::post().
+
+*/
+template<class AsyncStream, class TeardownHandler>
+inline
+void
+async_teardown(
+ role_type role,
+ boost::asio::ssl::stream<AsyncStream>& stream,
+ TeardownHandler&& handler);
+
+} // websocket
+} // beast
+} // boost
+
+#include <boost/beast/websocket/impl/ssl.ipp>
+
+#endif
diff --git a/boost/beast/websocket/stream.hpp b/boost/beast/websocket/stream.hpp
new file mode 100644
index 0000000000..b6d10834ef
--- /dev/null
+++ b/boost/beast/websocket/stream.hpp
@@ -0,0 +1,3458 @@
+//
+// 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_HPP
+#define BOOST_BEAST_WEBSOCKET_STREAM_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/websocket/error.hpp>
+#include <boost/beast/websocket/option.hpp>
+#include <boost/beast/websocket/role.hpp>
+#include <boost/beast/websocket/rfc6455.hpp>
+#include <boost/beast/websocket/detail/frame.hpp>
+#include <boost/beast/websocket/detail/hybi13.hpp>
+#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/utf8_checker.hpp>
+#include <boost/beast/core/static_buffer.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/beast/http/empty_body.hpp>
+#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>
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <type_traits>
+
+#include <boost/asio/io_context.hpp> // DEPRECATED
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+namespace detail {
+class frame_test;
+}
+
+/// The type of object holding HTTP Upgrade requests
+using request_type = http::request<http::empty_body>;
+
+/// The type of object holding HTTP Upgrade responses
+using response_type = http::response<http::string_body>;
+
+/** The type of received control frame.
+
+ Values of this type are passed to the control frame
+ callback set using @ref stream::control_callback.
+*/
+enum class frame_type
+{
+ /// A close frame was received
+ close,
+
+ /// A ping frame was received
+ ping,
+
+ /// A pong frame was received
+ pong
+};
+
+//--------------------------------------------------------------------
+
+/** Provides message-oriented functionality using WebSocket.
+
+ The @ref stream class template provides asynchronous and blocking
+ message-oriented functionality necessary for clients and servers
+ to utilize the WebSocket protocol.
+
+ For asynchronous operations, the application must ensure
+ that they are are all performed within the same implicit
+ or explicit strand.
+
+ @par Thread Safety
+ @e Distinct @e objects: Safe.@n
+ @e Shared @e objects: Unsafe.
+ The application must also ensure that all asynchronous
+ operations are performed within the same implicit or explicit strand.
+
+ @par Example
+
+ To use the @ref stream template with an `ip::tcp::socket`,
+ you would write:
+
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ @endcode
+ Alternatively, you can write:
+ @code
+ ip::tcp::socket sock{io_context};
+ websocket::stream<ip::tcp::socket&> ws{sock};
+ @endcode
+
+ @tparam NextLayer The type representing the next layer, to which
+ data will be read and written during operations. For synchronous
+ operations, the type must support the @b SyncStream concept.
+ For asynchronous operations, the type must support the
+ @b AsyncStream concept.
+
+ @note A stream object must not be moved or destroyed while there
+ are pending asynchronous operations associated with it.
+
+ @par Concepts
+ @b AsyncStream,
+ @b DynamicBuffer,
+ @b SyncStream
+*/
+template<class NextLayer>
+class stream
+{
+ friend class close_test;
+ friend class frame_test;
+ friend class ping_test;
+ friend class read_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.
+ */
+ 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)>;
+
+ // tokens are used to order reads and writes
+ class token
+ {
+ unsigned char id_ = 0;
+ public:
+ token() = default;
+ token(token const&) = default;
+ explicit token(unsigned char id) : id_(id) {}
+ operator bool() const { return id_ != 0; }
+ bool operator==(token const& t) { return id_ == t.id_; }
+ bool operator!=(token const& t) { return id_ != t.id_; }
+ token unique() { token t{id_++}; if(id_ == 0) ++id_; return t; }
+ 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,
+ closing,
+ closed,
+ failed
+ };
+
+ NextLayer stream_; // the wrapped stream
+ close_reason cr_; // set from received close frame
+ control_cb_type ctrl_cb_; // control callback
+
+ std::size_t rd_msg_max_ // max message size
+ = 16 * 1024 * 1024;
+ std::uint64_t rd_size_ // total size of current message so far
+ = 0;
+ std::uint64_t rd_remain_ // message frame bytes left in current frame
+ = 0;
+ detail::frame_header rd_fh_; // current frame header
+ detail::prepared_key rd_key_ // current stateful mask key
+ = 0;
+ detail::frame_buffer rd_fb_; // to write control frames (during reads)
+ detail::utf8_checker rd_utf8_; // to validate utf8
+ static_buffer<
+ +tcp_frame_size> rd_buf_; // buffer for reads
+ detail::opcode rd_op_ // current message binary or text
+ = detail::opcode::text;
+ bool rd_cont_ // `true` if the next frame is a continuation
+ = false;
+ bool rd_done_ // set when a message is done
+ = true;
+ bool rd_close_ // did we read a close frame?
+ = false;
+ token rd_block_; // op currenly reading
+
+ token tok_; // used to order asynchronous ops
+ role_type role_ // server or client
+ = role_type::client;
+ status status_
+ = status::closed;
+
+ token wr_block_; // op currenly writing
+ bool wr_close_ // did we write a close frame?
+ = false;
+ bool wr_cont_ // next write is a continuation
+ = false;
+ bool wr_frag_ // autofrag the current message
+ = false;
+ bool wr_frag_opt_ // autofrag option setting
+ = true;
+ bool wr_compress_ // compress current message
+ = false;
+ detail::opcode wr_opcode_ // message type
+ = detail::opcode::text;
+ std::unique_ptr<
+ std::uint8_t[]> wr_buf_; // write buffer
+ std::size_t wr_buf_size_ // write buffer size (current message)
+ = 0;
+ std::size_t wr_buf_opt_ // write buffer size option setting
+ = 4096;
+ detail::fh_buffer wr_fb_; // header buffer used for writes
+ detail::maskgen wr_gen_; // source of mask keys
+
+ detail::pausation paused_rd_; // paused read op
+ 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)
+
+public:
+ /// The type of the next layer.
+ using next_layer_type =
+ typename std::remove_reference<NextLayer>::type;
+
+ /// The type of the lowest layer.
+ using lowest_layer_type =
+ typename get_lowest_layer<next_layer_type>::type;
+
+ /// The type of the executor associated with the object.
+ using executor_type = typename next_layer_type::executor_type;
+
+ /** Destructor
+
+ Destroys the stream and all associated resources.
+
+ @note A stream object must not be destroyed while there
+ are pending asynchronous operations associated with it.
+ */
+ ~stream() = default;
+
+ /** Constructor
+
+ If `NextLayer` is move constructible, this function
+ will move-construct a new stream from the existing stream.
+
+ @note The behavior of move assignment on or from streams
+ with active or pending operations is undefined.
+ */
+ stream(stream&&) = default;
+
+ /** Assignment
+
+ If `NextLayer` is move assignable, this function
+ will move-assign a new stream from the existing stream.
+
+ @note The behavior of move assignment on or from streams
+ with active or pending operations is undefined.
+ */
+ stream& operator=(stream&&) = default;
+
+ /** Constructor
+
+ This constructor creates a websocket stream and initializes
+ the next layer object.
+
+ @throws Any exceptions thrown by the NextLayer constructor.
+
+ @param args The arguments to be passed to initialize the
+ next layer object. The arguments are forwarded to the next
+ layer's constructor.
+ */
+ template<class... Args>
+ explicit
+ stream(Args&&... args);
+
+ //--------------------------------------------------------------------------
+
+ /** Get the executor associated with the object.
+
+ This function may be used to obtain the executor object that the
+ stream uses to dispatch handlers for asynchronous operations.
+
+ @return A copy of the executor that stream will use to dispatch handlers.
+ */
+ executor_type
+ get_executor() noexcept
+ {
+ return stream_.get_executor();
+ }
+
+ /** Get a reference to the next layer
+
+ This function returns a reference to the next layer
+ in a stack of stream layers.
+
+ @return A reference to the next layer in the stack of
+ stream layers.
+ */
+ next_layer_type&
+ next_layer()
+ {
+ return stream_;
+ }
+
+ /** Get a reference to the next layer
+
+ This function returns a reference to the next layer in a
+ stack of stream layers.
+
+ @return A reference to the next layer in the stack of
+ stream layers.
+ */
+ next_layer_type const&
+ next_layer() const
+ {
+ return stream_;
+ }
+
+ /** Get a reference to the lowest layer
+
+ This function returns a reference to the lowest layer
+ in a stack of stream layers.
+
+ @return A reference to the lowest layer in the stack of
+ stream layers.
+ */
+ lowest_layer_type&
+ lowest_layer()
+ {
+ return stream_.lowest_layer();
+ }
+
+ /** Get a reference to the lowest layer
+
+ This function returns a reference to the lowest layer
+ in a stack of stream layers.
+
+ @return A reference to the lowest layer in the stack of
+ stream layers. Ownership is not transferred to the caller.
+ */
+ lowest_layer_type const&
+ lowest_layer() const
+ {
+ return stream_.lowest_layer();
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Observers
+ //
+ //--------------------------------------------------------------------------
+
+ /** Returns `true` if the stream is open.
+
+ The stream is open after a successful handshake, and when
+ no error has occurred.
+ */
+ bool
+ is_open() const
+ {
+ return status_ == status::open;
+ }
+
+ /** Returns `true` if the latest message data indicates binary.
+
+ This function informs the caller of whether the last
+ received message frame represents a message with the
+ binary opcode.
+
+ If there is no last message frame, the return value is
+ undefined.
+ */
+ bool
+ got_binary() const
+ {
+ return rd_op_ == detail::opcode::binary;
+ }
+
+ /** Returns `true` if the latest message data indicates text.
+
+ This function informs the caller of whether the last
+ received message frame represents a message with the
+ text opcode.
+
+ If there is no last message frame, the return value is
+ undefined.
+ */
+ bool
+ got_text() const
+ {
+ return ! got_binary();
+ }
+
+ /// Returns `true` if the last completed read finished the current message.
+ bool
+ is_message_done() const
+ {
+ return rd_done_;
+ }
+
+ /** Returns the close reason received from the peer.
+
+ This is only valid after a read completes with error::closed.
+ */
+ close_reason const&
+ reason() const
+ {
+ return cr_;
+ }
+
+ /** Returns a suggested maximum buffer size for the next call to read.
+
+ This function returns a reasonable upper limit on the number
+ of bytes for the size of the buffer passed in the next call
+ to read. The number is determined by the state of the current
+ frame and whether or not the permessage-deflate extension is
+ enabled.
+
+ @param initial_size A non-zero size representing the caller's
+ desired buffer size for when there is no information which may
+ be used to calculate a more specific value. For example, when
+ reading the first frame header of a message.
+ */
+ std::size_t
+ read_size_hint(
+ std::size_t initial_size = +tcp_frame_size) const;
+
+ /** Returns a suggested maximum buffer size for the next call to read.
+
+ This function returns a reasonable upper limit on the number
+ of bytes for the size of the buffer passed in the next call
+ to read. The number is determined by the state of the current
+ frame and whether or not the permessage-deflate extension is
+ enabled.
+
+ @param buffer The buffer which will be used for reading. The
+ implementation will query the buffer to obtain the optimum
+ size of a subsequent call to `buffer.prepare` based on the
+ state of the current frame, if any.
+ */
+ template<class DynamicBuffer
+#if ! BOOST_BEAST_DOXYGEN
+ , class = typename std::enable_if<
+ ! std::is_integral<DynamicBuffer>::value>::type
+#endif
+ >
+ std::size_t
+ read_size_hint(
+ DynamicBuffer& buffer) const;
+
+ //--------------------------------------------------------------------------
+ //
+ // Settings
+ //
+ //--------------------------------------------------------------------------
+
+ /// Set the permessage-deflate extension options
+ void
+ set_option(permessage_deflate const& o);
+
+ /// Get the permessage-deflate extension options
+ void
+ get_option(permessage_deflate& o)
+ {
+ o = pmd_opts_;
+ }
+
+ /** Set the automatic fragmentation option.
+
+ Determines if outgoing message payloads are broken up into
+ multiple pieces.
+
+ When the automatic fragmentation size is turned on, outgoing
+ message payloads are broken up into multiple frames no larger
+ than the write buffer size.
+
+ The default setting is to fragment messages.
+
+ @param value A `bool` indicating if auto fragmentation should be on.
+
+ @par Example
+ Setting the automatic fragmentation option:
+ @code
+ ws.auto_fragment(true);
+ @endcode
+ */
+ void
+ auto_fragment(bool value)
+ {
+ wr_frag_opt_ = value;
+ }
+
+ /// Returns `true` if the automatic fragmentation option is set.
+ bool
+ auto_fragment() const
+ {
+ return wr_frag_opt_;
+ }
+
+ /** Set the binary message option.
+
+ This controls whether or not outgoing message opcodes
+ are set to binary or text. The setting is only applied
+ at the start when a caller begins a new message. Changing
+ the opcode after a message is started will only take effect
+ after the current message being sent is complete.
+
+ The default setting is to send text messages.
+
+ @param value `true` if outgoing messages should indicate
+ binary, or `false` if they should indicate text.
+
+ @par Example
+ Setting the message type to binary.
+ @code
+ ws.binary(true);
+ @endcode
+ */
+ void
+ binary(bool value)
+ {
+ wr_opcode_ = value ?
+ detail::opcode::binary :
+ detail::opcode::text;
+ }
+
+ /// Returns `true` if the binary message option is set.
+ bool
+ binary() const
+ {
+ return wr_opcode_ == detail::opcode::binary;
+ }
+
+ /** Set a callback to be invoked on each incoming control frame.
+
+ Sets the callback to be invoked whenever a ping, pong,
+ or close control frame is received during a call to one
+ of the following functions:
+
+ @li @ref beast::websocket::stream::read
+ @li @ref beast::websocket::stream::read_some
+ @li @ref beast::websocket::stream::async_read
+ @li @ref beast::websocket::stream::async_read_some
+
+ Unlike completion handlers, the callback will be invoked
+ for each control frame during a call to any synchronous
+ or asynchronous read function. The operation is passive,
+ with no associated error code, and triggered by reads.
+
+ For close frames, the close reason code may be obtained by
+ calling the function @ref reason.
+
+ @param cb The function object to call, which must be
+ invocable with this equivalent signature:
+ @code
+ void
+ callback(
+ frame_type kind, // The type of frame
+ string_view payload // The payload in the frame
+ );
+ @endcode
+ The implementation type-erases the callback without requiring
+ a dynamic allocation. For this reason, the callback object is
+ passed by a non-constant reference.
+ If the read operation which receives the control frame is
+ an asynchronous operation, the callback will be invoked using
+ the same method as that used to invoke the final handler.
+
+ @note It is not necessary to send a close frame upon receipt
+ of a close frame. The implementation does this automatically.
+ Attempting to send a close frame after a close frame is
+ received will result in undefined behavior.
+ */
+ template<class Callback>
+ void
+ control_callback(Callback& cb)
+ {
+ // Callback may not be constant, caller is responsible for
+ // managing the lifetime of the callback. Copies are not made.
+ BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
+
+ ctrl_cb_ = std::ref(cb);
+ }
+
+ /** Reset the control frame callback.
+
+ This function removes any previously set control frame callback.
+ */
+ void
+ control_callback()
+ {
+ ctrl_cb_ = {};
+ }
+
+ /** Set the maximum incoming message size option.
+
+ Sets the largest permissible incoming message size. Message
+ frame fields indicating a size that would bring the total
+ message size over this limit will cause a protocol failure.
+
+ The default setting is 16 megabytes. A value of zero indicates
+ a limit of the maximum value of a `std::uint64_t`.
+
+ @par Example
+ Setting the maximum read message size.
+ @code
+ ws.read_message_max(65536);
+ @endcode
+
+ @param amount The limit on the size of incoming messages.
+ */
+ void
+ read_message_max(std::size_t amount)
+ {
+ rd_msg_max_ = amount;
+ }
+
+ /// Returns the maximum incoming message size setting.
+ std::size_t
+ read_message_max() const
+ {
+ return rd_msg_max_;
+ }
+
+ /** Set the write buffer size option.
+
+ Sets the size of the write buffer used by the implementation to
+ send frames. The write buffer is needed when masking payload data
+ in the client role, compressing frames, or auto-fragmenting message
+ data.
+
+ Lowering the size of the buffer can decrease the memory requirements
+ for each connection, while increasing the size of the buffer can reduce
+ the number of calls made to the next layer to write data.
+
+ The default setting is 4096. The minimum value is 8.
+
+ The write buffer size can only be changed when the stream is not
+ open. Undefined behavior results if the option is modified after a
+ successful WebSocket handshake.
+
+ @par Example
+ Setting the write buffer size.
+ @code
+ ws.write_buffer_size(8192);
+ @endcode
+
+ @param amount The size of the write buffer in bytes.
+ */
+ void
+ write_buffer_size(std::size_t amount)
+ {
+ if(amount < 8)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "write buffer size underflow"});
+ wr_buf_opt_ = amount;
+ };
+
+ /// Returns the size of the write buffer.
+ std::size_t
+ write_buffer_size() const
+ {
+ return wr_buf_opt_;
+ }
+
+ /** Set the text message option.
+
+ This controls whether or not outgoing message opcodes
+ are set to binary or text. The setting is only applied
+ at the start when a caller begins a new message. Changing
+ the opcode after a message is started will only take effect
+ after the current message being sent is complete.
+
+ The default setting is to send text messages.
+
+ @param value `true` if outgoing messages should indicate
+ text, or `false` if they should indicate binary.
+
+ @par Example
+ Setting the message type to text.
+ @code
+ ws.text(true);
+ @endcode
+ */
+ void
+ text(bool value)
+ {
+ wr_opcode_ = value ?
+ detail::opcode::text :
+ detail::opcode::binary;
+ }
+
+ /// Returns `true` if the text message option is set.
+ bool
+ text() const
+ {
+ return wr_opcode_ == detail::opcode::text;
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Handshaking (Client)
+ //
+ //--------------------------------------------------------------------------
+
+ /** Send an HTTP WebSocket Upgrade request and receive the response.
+
+ This function is used to synchronously send the WebSocket
+ upgrade HTTP request. The call blocks until one of the
+ following conditions is true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `read_some` and `write_some` functions.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param host The name of the remote host,
+ required by the HTTP protocol.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol.
+
+ @throws system_error Thrown on failure.
+
+ @par Example
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ ...
+ try
+ {
+ ws.handshake("localhost", "/");
+ }
+ catch(...)
+ {
+ // An error occurred.
+ }
+ @endcode
+ */
+ void
+ handshake(
+ string_view host,
+ string_view target);
+
+ /** Send an HTTP WebSocket Upgrade request and receive the response.
+
+ This function is used to synchronously send the WebSocket
+ upgrade HTTP request. The call blocks until one of the
+ following conditions is true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `read_some` and `write_some` functions.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param res The HTTP Upgrade response returned by the remote
+ endpoint.
+
+ @param host The name of the remote host,
+ required by the HTTP protocol.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol.
+
+ @throws system_error Thrown on failure.
+
+ @par Example
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ ...
+ try
+ {
+ response_type res;
+ ws.handshake(res, "localhost", "/");
+ }
+ catch(...)
+ {
+ // An error occurred.
+ }
+ @endcode
+ */
+ void
+ handshake(
+ response_type& res,
+ string_view host,
+ string_view target);
+
+ /** Send an HTTP WebSocket Upgrade request and receive the response.
+
+ This function is used to synchronously send the WebSocket
+ upgrade HTTP request. The call blocks until one of the
+ following conditions is true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `read_some` and `write_some` functions.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param host The name of the remote host,
+ required by the HTTP protocol.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol.
+
+ @param decorator A function object which will be called to modify
+ the HTTP request object generated by the implementation. This
+ could be used to set the User-Agent field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ request_type& req
+ ); @endcode
+
+ @throws system_error Thrown on failure.
+
+ @par Example
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ ...
+ try
+ {
+ ws.handshake("localhost", "/",
+ [](request_type& req)
+ {
+ req.set(field::user_agent, "Beast");
+ });
+ }
+ catch(...)
+ {
+ // An error occurred.
+ }
+ @endcode
+ */
+ template<class RequestDecorator>
+ void
+ handshake_ex(
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator);
+
+ /** Send an HTTP WebSocket Upgrade request and receive the response.
+
+ This function is used to synchronously send the WebSocket
+ upgrade HTTP request. The call blocks until one of the
+ following conditions is true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `read_some` and `write_some` functions.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param res The HTTP Upgrade response returned by the remote
+ endpoint.
+
+ @param host The name of the remote host,
+ required by the HTTP protocol.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol.
+
+ @param decorator A function object which will be called to modify
+ the HTTP request object generated by the implementation. This
+ could be used to set the User-Agent field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ request_type& req
+ ); @endcode
+
+ @throws system_error Thrown on failure.
+
+ @par Example
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ ...
+ try
+ {
+ response_type res;
+ ws.handshake(res, "localhost", "/",
+ [](request_type& req)
+ {
+ req.set(field::user_agent, "Beast");
+ });
+ }
+ catch(...)
+ {
+ // An error occurred.
+ }
+ @endcode
+ */
+ template<class RequestDecorator>
+ void
+ handshake_ex(
+ response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator);
+
+ /** Send an HTTP WebSocket Upgrade request and receive the response.
+
+ This function is used to synchronously send the WebSocket
+ upgrade HTTP request. The call blocks until one of the
+ following conditions is true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `read_some` and `write_some` functions.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param host The name of the remote host,
+ required by the HTTP protocol.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol.
+
+ @param ec Set to indicate what error occurred, if any.
+
+ @par Example
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ ...
+ error_code ec;
+ ws.handshake(host, target, ec);
+ if(ec)
+ {
+ // An error occurred.
+ }
+ @endcode
+ */
+ void
+ handshake(
+ string_view host,
+ string_view target,
+ error_code& ec);
+
+ /** Send an HTTP WebSocket Upgrade request and receive the response.
+
+ This function is used to synchronously send the WebSocket
+ upgrade HTTP request. The call blocks until one of the
+ following conditions is true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `read_some` and `write_some` functions.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param res The HTTP Upgrade response returned by the remote
+ endpoint. If `ec` is set, the returned value is undefined.
+
+ @param host The name of the remote host,
+ required by the HTTP protocol.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol.
+
+ @param ec Set to indicate what error occurred, if any.
+
+ @par Example
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ ...
+ error_code ec;
+ response_type res;
+ ws.handshake(res, host, target, ec);
+ if(ec)
+ {
+ // An error occurred.
+ }
+ @endcode
+ */
+ void
+ handshake(
+ response_type& res,
+ string_view host,
+ string_view target,
+ error_code& ec);
+
+ /** Send an HTTP WebSocket Upgrade request and receive the response.
+
+ This function is used to synchronously send the WebSocket
+ upgrade HTTP request. The call blocks until one of the
+ following conditions is true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `read_some` and `write_some` functions.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param host The name of the remote host,
+ required by the HTTP protocol.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol.
+
+ @param decorator A function object which will be called to modify
+ the HTTP request object generated by the implementation. This
+ could be used to set the User-Agent field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ request_type& req
+ ); @endcode
+
+ @param ec Set to indicate what error occurred, if any.
+
+ @par Example
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ ...
+ error_code ec;
+ ws.handshake("localhost", "/",
+ [](request_type& req)
+ {
+ req.set(field::user_agent, "Beast");
+ },
+ ec);
+ if(ec)
+ {
+ // An error occurred.
+ }
+ @endcode
+ */
+ template<class RequestDecorator>
+ void
+ handshake_ex(
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec);
+
+ /** Send an HTTP WebSocket Upgrade request and receive the response.
+
+ This function is used to synchronously send the WebSocket
+ upgrade HTTP request. The call blocks until one of the
+ following conditions is true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `read_some` and `write_some` functions.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param res The HTTP Upgrade response returned by the remote
+ endpoint.
+
+ @param host The name of the remote host,
+ required by the HTTP protocol.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol.
+
+ @param decorator A function object which will be called to modify
+ the HTTP request object generated by the implementation. This
+ could be used to set the User-Agent field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ request_type& req
+ ); @endcode
+
+ @param ec Set to indicate what error occurred, if any.
+
+ @par Example
+ @code
+ websocket::stream<ip::tcp::socket> ws{io_context};
+ ...
+ error_code ec;
+ response_type res;
+ ws.handshake(res, "localhost", "/",
+ [](request_type& req)
+ {
+ req.set(field::user_agent, "Beast");
+ },
+ ec);
+ if(ec)
+ {
+ // An error occurred.
+ }
+ @endcode
+ */
+ template<class RequestDecorator>
+ void
+ handshake_ex(
+ response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec);
+
+ /** Start an asynchronous operation to send an upgrade request and receive the response.
+
+ This function is used to asynchronously send the HTTP WebSocket
+ upgrade request and receive the HTTP WebSocket Upgrade response.
+ This function call always returns immediately. The asynchronous
+ operation will continue until one of the following conditions is
+ true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `async_read_some` and `async_write_some` functions, and
+ is known as a <em>composed operation</em>. The program must ensure
+ that the stream performs no other operations until this operation
+ completes.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param host The name of the remote host, required by
+ the HTTP protocol. Copies may be made as needed.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol. Copies of this parameter may
+ be made as needed.
+
+ @param handler The handler to be called when the request completes.
+ Copies will be made of the handler as required. The equivalent
+ function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class HandshakeHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ HandshakeHandler, void(error_code))
+ async_handshake(
+ string_view host,
+ string_view target,
+ HandshakeHandler&& handler);
+
+ /** Start an asynchronous operation to send an upgrade request and receive the response.
+
+ This function is used to asynchronously send the HTTP WebSocket
+ upgrade request and receive the HTTP WebSocket Upgrade response.
+ This function call always returns immediately. The asynchronous
+ operation will continue until one of the following conditions is
+ true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `async_read_some` and `async_write_some` functions, and
+ is known as a <em>composed operation</em>. The program must ensure
+ that the stream performs no other operations until this operation
+ completes.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param res The HTTP Upgrade response returned by the remote
+ endpoint. The caller must ensure this object is valid for at
+ least until the completion handler is invoked.
+
+ @param host The name of the remote host, required by
+ the HTTP protocol. Copies may be made as needed.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol. Copies of this parameter may
+ be made as needed.
+
+ @param handler The handler to be called when the request completes.
+ Copies will be made of the handler as required. The equivalent
+ function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class HandshakeHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ HandshakeHandler, void(error_code))
+ async_handshake(
+ response_type& res,
+ string_view host,
+ string_view target,
+ HandshakeHandler&& handler);
+
+ /** Start an asynchronous operation to send an upgrade request and receive the response.
+
+ This function is used to asynchronously send the HTTP WebSocket
+ upgrade request and receive the HTTP WebSocket Upgrade response.
+ This function call always returns immediately. The asynchronous
+ operation will continue until one of the following conditions is
+ true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `async_read_some` and `async_write_some` functions, and
+ is known as a <em>composed operation</em>. The program must ensure
+ that the stream performs no other operations until this operation
+ completes.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param host The name of the remote host, required by
+ the HTTP protocol. Copies may be made as needed.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol. Copies of this parameter may
+ be made as needed.
+
+ @param decorator A function object which will be called to modify
+ the HTTP request object generated by the implementation. This
+ could be used to set the User-Agent field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ request_type& req
+ ); @endcode
+
+ @param handler The handler to be called when the request completes.
+ Copies will be made of the handler as required. The equivalent
+ function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class RequestDecorator, class HandshakeHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ HandshakeHandler, void(error_code))
+ async_handshake_ex(
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ HandshakeHandler&& handler);
+
+ /** Start an asynchronous operation to send an upgrade request and receive the response.
+
+ This function is used to asynchronously send the HTTP WebSocket
+ upgrade request and receive the HTTP WebSocket Upgrade response.
+ This function call always returns immediately. The asynchronous
+ operation will continue until one of the following conditions is
+ true:
+
+ @li The request is sent and the response is received.
+
+ @li An error occurs on the stream
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `async_read_some` and `async_write_some` functions, and
+ is known as a <em>composed operation</em>. The program must ensure
+ that the stream performs no other operations until this operation
+ completes.
+
+ The operation is successful if the received HTTP response indicates
+ a successful HTTP Upgrade (represented by a Status-Code of 101,
+ "switching protocols").
+
+ @param res The HTTP Upgrade response returned by the remote
+ endpoint. The caller must ensure this object is valid for at
+ least until the completion handler is invoked.
+
+ @param host The name of the remote host, required by
+ the HTTP protocol. Copies may be made as needed.
+
+ @param target The Request Target, which may not be empty,
+ required by the HTTP protocol. Copies of this parameter may
+ be made as needed.
+
+ @param decorator A function object which will be called to modify
+ the HTTP request object generated by the implementation. This
+ could be used to set the User-Agent field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ request_type& req
+ ); @endcode
+
+ @param handler The handler to be called when the request completes.
+ Copies will be made of the handler as required. The equivalent
+ function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class RequestDecorator, class HandshakeHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ HandshakeHandler, void(error_code))
+ async_handshake_ex(
+ response_type& res,
+ string_view host,
+ string_view target,
+ RequestDecorator const& decorator,
+ HandshakeHandler&& handler);
+
+ //--------------------------------------------------------------------------
+ //
+ // Handshaking (Server)
+ //
+ //--------------------------------------------------------------------------
+
+ /** Read and respond to a WebSocket HTTP Upgrade request.
+
+ This function is used to synchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The call blocks
+ until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @throws system_error Thrown on failure.
+ */
+ void
+ accept();
+
+ /** Read and respond to a WebSocket HTTP Upgrade request.
+
+ This function is used to synchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The call blocks
+ until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @throws system_error Thrown on failure.
+ */
+ template<class ResponseDecorator>
+ void
+ accept_ex(ResponseDecorator const& decorator);
+
+ /** Read and respond to a WebSocket HTTP Upgrade request.
+
+ This function is used to synchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The call blocks
+ until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ void
+ accept(error_code& ec);
+
+ /** Read and respond to a WebSocket HTTP Upgrade request.
+
+ This function is used to synchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The call blocks
+ until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ template<class ResponseDecorator>
+ void
+ accept_ex(
+ ResponseDecorator const& decorator,
+ error_code& ec);
+
+ /** Read and respond to a WebSocket HTTP Upgrade request.
+
+ This function is used to synchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The call blocks
+ until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param buffers Caller provided data that has already been
+ received on the stream. The implementation will copy the
+ caller provided data before the function returns.
+
+ @throws system_error Thrown on failure.
+ */
+ template<class ConstBufferSequence>
+#if BOOST_BEAST_DOXYGEN
+ void
+#else
+ typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+#endif
+ accept(ConstBufferSequence const& buffers);
+
+ /** Read and respond to a WebSocket HTTP Upgrade request.
+
+ This function is used to synchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The call blocks
+ until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param buffers Caller provided data that has already been
+ received on the stream. The implementation will copy the
+ caller provided data before the function returns.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @throws system_error Thrown on failure.
+ */
+ template<class ConstBufferSequence,
+ class ResponseDecorator>
+#if BOOST_BEAST_DOXYGEN
+ void
+#else
+ typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+#endif
+ accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator);
+
+ /** Read and respond to a WebSocket HTTP Upgrade request.
+
+ This function is used to synchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The call blocks
+ until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param buffers Caller provided data that has already been
+ received on the stream. The implementation will copy the
+ caller provided data before the function returns.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ template<class ConstBufferSequence>
+#if BOOST_BEAST_DOXYGEN
+ void
+#else
+ typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+#endif
+ accept(
+ ConstBufferSequence const& buffers,
+ error_code& ec);
+
+ /** Read and respond to a WebSocket HTTP Upgrade request.
+
+ This function is used to synchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The call blocks
+ until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param buffers Caller provided data that has already been
+ received on the stream. The implementation will copy the
+ caller provided data before the function returns.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ template<class ConstBufferSequence, class ResponseDecorator>
+#if BOOST_BEAST_DOXYGEN
+ void
+#else
+ typename std::enable_if<! http::detail::is_header<
+ ConstBufferSequence>::value>::type
+#endif
+ accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator,
+ error_code& ec);
+
+ /** Respond to a WebSocket HTTP Upgrade request
+
+ This function is used to synchronously send the HTTP response
+ to an HTTP request possibly containing a WebSocket Upgrade.
+ The call blocks until one of the following conditions is true:
+
+ @li The response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ @param req An object containing the HTTP Upgrade request.
+ Ownership is not transferred, the implementation will not
+ access this object from other threads.
+
+ @throws system_error Thrown on failure.
+ */
+ template<class Body, class Allocator>
+ void
+ accept(http::request<Body,
+ http::basic_fields<Allocator>> const& req);
+
+ /** Respond to a WebSocket HTTP Upgrade request
+
+ This function is used to synchronously send the HTTP response
+ to an HTTP request possibly containing a WebSocket Upgrade.
+ The call blocks until one of the following conditions is true:
+
+ @li The response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ @param req An object containing the HTTP Upgrade request.
+ Ownership is not transferred, the implementation will not
+ access this object from other threads.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @throws system_error Thrown on failure.
+ */
+ template<class Body, class Allocator,
+ class ResponseDecorator>
+ void
+ accept_ex(http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator);
+
+ /** Respond to a WebSocket HTTP Upgrade request
+
+ This function is used to synchronously send the HTTP response
+ to an HTTP request possibly containing a WebSocket Upgrade.
+ The call blocks until one of the following conditions is true:
+
+ @li The response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ @param req An object containing the HTTP Upgrade request.
+ Ownership is not transferred, the implementation will not
+ access this object from other threads.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ template<class Body, class Allocator>
+ void
+ accept(http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ error_code& ec);
+
+ /** Respond to a WebSocket HTTP Upgrade request
+
+ This function is used to synchronously send the HTTP response
+ to an HTTP request possibly containing a WebSocket Upgrade.
+ The call blocks until one of the following conditions is true:
+
+ @li The response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to
+ the next layer's `read_some` and `write_some` functions.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When this call returns, the stream is then ready to send and
+ receive WebSocket protocol frames and messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure.
+
+ @param req An object containing the HTTP Upgrade request.
+ Ownership is not transferred, the implementation will not
+ access this object from other threads.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ template<class Body, class Allocator,
+ class ResponseDecorator>
+ void
+ accept_ex(http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator,
+ error_code& ec);
+
+ /** Start reading and responding to a WebSocket HTTP Upgrade request.
+
+ This function is used to asynchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The function call
+ always returns immediately. The asynchronous operation will
+ continue until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to
+ the next layer's `async_read_some` and `async_write_some`
+ functions, and is known as a <em>composed operation</em>. The
+ program must ensure that the stream performs no other
+ asynchronous operations until this operation completes.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When the completion handler is invoked, the stream is then
+ ready to send and receive WebSocket protocol frames and
+ messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure, and
+ the completion handler will be invoked with a suitable error
+ code set.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class AcceptHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))
+ async_accept(AcceptHandler&& handler);
+
+ /** Start reading and responding to a WebSocket HTTP Upgrade request.
+
+ This function is used to asynchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The function call
+ always returns immediately. The asynchronous operation will
+ continue until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to
+ the next layer's `async_read_some` and `async_write_some`
+ functions, and is known as a <em>composed operation</em>. The
+ program must ensure that the stream performs no other
+ asynchronous operations until this operation completes.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When the completion handler is invoked, the stream is then
+ ready to send and receive WebSocket protocol frames and
+ messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure, and
+ the completion handler will be invoked with a suitable error
+ code set.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<
+ class ResponseDecorator,
+ class AcceptHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))
+ async_accept_ex(
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler);
+
+ /** Start reading and responding to a WebSocket HTTP Upgrade request.
+
+ This function is used to asynchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The function call
+ always returns immediately. The asynchronous operation will
+ continue until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to
+ the next layer's `async_read_some` and `async_write_some`
+ functions, and is known as a <em>composed operation</em>. The
+ program must ensure that the stream performs no other
+ asynchronous operations until this operation completes.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When the completion handler is invoked, the stream is then
+ ready to send and receive WebSocket protocol frames and
+ messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure, and
+ the completion handler will be invoked with a suitable error
+ code set.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param buffers Caller provided data that has already been
+ received on the stream. This may be used for implementations
+ allowing multiple protocols on the same stream. The
+ buffered data will first be applied to the handshake, and
+ then to received WebSocket frames. The implementation will
+ copy the caller provided data before the function returns.
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<
+ class ConstBufferSequence,
+ class AcceptHandler>
+#if BOOST_BEAST_DOXYGEN
+ void_or_deduced
+#else
+ typename std::enable_if<
+ ! http::detail::is_header<ConstBufferSequence>::value,
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))>::type
+#endif
+ async_accept(
+ ConstBufferSequence const& buffers,
+ AcceptHandler&& handler);
+
+ /** Start reading and responding to a WebSocket HTTP Upgrade request.
+
+ This function is used to asynchronously read an HTTP WebSocket
+ Upgrade request and send the HTTP response. The function call
+ always returns immediately. The asynchronous operation will
+ continue until one of the following conditions is true:
+
+ @li The request is received and the response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to
+ the next layer's `async_read_some` and `async_write_some`
+ functions, and is known as a <em>composed operation</em>. The
+ program must ensure that the stream performs no other
+ asynchronous operations until this operation completes.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When the completion handler is invoked, the stream is then
+ ready to send and receive WebSocket protocol frames and
+ messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure, and
+ the completion handler will be invoked with a suitable error
+ code set.
+
+ The implementation uses fixed size internal storage to
+ receive the request. If the request is too large, the error
+ @ref error::buffer_overflow will be indicated. Applications
+ that wish to receive larger requests should first read the
+ request using their own buffer and a suitable overload of
+ @ref http::read or @ref http::async_read, then call @ref accept
+ or @ref async_accept with the request.
+
+ @param buffers Caller provided data that has already been
+ received on the stream. This may be used for implementations
+ allowing multiple protocols on the same stream. The
+ buffered data will first be applied to the handshake, and
+ then to received WebSocket frames. The implementation will
+ copy the caller provided data before the function returns.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<
+ class ConstBufferSequence,
+ class ResponseDecorator,
+ class AcceptHandler>
+#if BOOST_BEAST_DOXYGEN
+ void_or_deduced
+#else
+ typename std::enable_if<
+ ! http::detail::is_header<ConstBufferSequence>::value,
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))>::type
+#endif
+ async_accept_ex(
+ ConstBufferSequence const& buffers,
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler);
+
+ /** Start responding to a WebSocket HTTP Upgrade request.
+
+ This function is used to asynchronously send the HTTP response
+ to an HTTP request possibly containing a WebSocket Upgrade
+ request. The function call always returns immediately. The
+ asynchronous operation will continue until one of the following
+ conditions is true:
+
+ @li The response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls 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 operations until this operation
+ completes.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When the completion handler is invoked, the stream is then
+ ready to send and receive WebSocket protocol frames and
+ messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure, and
+ the completion handler will be invoked with a suitable error
+ code set.
+
+ @param req An object containing the HTTP Upgrade request.
+ Ownership is not transferred, the implementation will not access
+ this object from other threads.
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<
+ class Body, class Allocator,
+ class AcceptHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))
+ async_accept(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ AcceptHandler&& handler);
+
+ /** Start responding to a WebSocket HTTP Upgrade request.
+
+ This function is used to asynchronously send the HTTP response
+ to an HTTP request possibly containing a WebSocket Upgrade
+ request. The function call always returns immediately. The
+ asynchronous operation will continue until one of the following
+ conditions is true:
+
+ @li The response finishes sending.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls 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 operations until this operation
+ completes.
+
+ If the stream receives a valid HTTP WebSocket Upgrade request,
+ an HTTP response is sent back indicating a successful upgrade.
+ When the completion handler is invoked, the stream is then
+ ready to send and receive WebSocket protocol frames and
+ messages.
+ If the HTTP Upgrade request is invalid or cannot be satisfied,
+ an HTTP response is sent indicating the reason and status code
+ (typically 400, "Bad Request"). This counts as a failure, and
+ the completion handler will be invoked with a suitable error
+ code set.
+
+ @param req An object containing the HTTP Upgrade request.
+ Ownership is not transferred, the implementation will not access
+ this object from other threads.
+
+ @param decorator A function object which will be called to modify
+ the HTTP response object delivered by the implementation. This
+ could be used to set the Server field, subprotocols, or other
+ application or HTTP specific fields. The object will be called
+ with this equivalent signature:
+ @code void decorator(
+ response_type& res
+ ); @endcode
+
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<
+ class Body, class Allocator,
+ class ResponseDecorator,
+ class AcceptHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ AcceptHandler, void(error_code))
+ async_accept_ex(
+ http::request<Body,
+ http::basic_fields<Allocator>> const& req,
+ ResponseDecorator const& decorator,
+ AcceptHandler&& handler);
+
+ //--------------------------------------------------------------------------
+ //
+ // Control Frames
+ //
+ //--------------------------------------------------------------------------
+
+ /** Send a WebSocket close frame.
+
+ This function is used to synchronously send a close frame on
+ the stream. The call blocks until one of the following is true:
+
+ @li The close frame finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls
+ to the next layer's `write_some` functions.
+
+ If the close reason specifies a close code other than
+ @ref beast::websocket::close_code::none, the close frame is
+ sent with the close code and optional reason string. Otherwise,
+ the close frame is sent with no payload.
+
+ Callers should not attempt to write WebSocket data after
+ initiating the close. Instead, callers should continue
+ reading until an error occurs. A read returning @ref error::closed
+ indicates a successful connection closure.
+
+ @param cr The reason for the close.
+
+ @throws system_error Thrown on failure.
+ */
+ void
+ close(close_reason const& cr);
+
+ /** Send a WebSocket close frame.
+
+ This function is used to synchronously send a close frame on
+ the stream. The call blocks until one of the following is true:
+
+ @li The close frame finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls
+ to the next layer's `write_some` functions.
+
+ If the close reason specifies a close code other than
+ @ref beast::websocket::close_code::none, the close frame is
+ sent with the close code and optional reason string. Otherwise,
+ the close frame is sent with no payload.
+
+ Callers should not attempt to write WebSocket data after
+ initiating the close. Instead, callers should continue
+ reading until an error occurs. A read returning @ref error::closed
+ indicates a successful connection closure.
+
+ @param cr The reason for the close.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ void
+ close(close_reason const& cr, error_code& ec);
+
+ /** Start an asynchronous operation to send a WebSocket close frame.
+
+ This function is used to asynchronously send a close frame on
+ the stream. This function call always returns immediately. The
+ asynchronous operation will continue until one of the following
+ conditions is true:
+
+ @li The close frame finishes sending.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls 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 @ref async_ping,
+ @ref stream::async_write, @ref stream::async_write_some, or
+ @ref stream::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
+ sent with the close code and optional reason string. Otherwise,
+ the close frame is sent with no payload.
+
+ Callers should not attempt to write WebSocket data after
+ initiating the close. Instead, callers should continue
+ reading until an error occurs. A read returning @ref error::closed
+ indicates a successful connection closure.
+
+ @param cr The reason for the close.
+
+ @param handler The handler to be called when the close operation
+ completes. Copies will be made of the handler as required. The
+ function signature of the handler must be:
+ @code
+ void handler(
+ error_code const& ec // Result of operation
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class CloseHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ CloseHandler, void(error_code))
+ async_close(close_reason const& cr, CloseHandler&& handler);
+
+ /** Send a WebSocket ping frame.
+
+ This function is used to synchronously send a ping frame on
+ the stream. The call blocks until one of the following is true:
+
+ @li The ping frame finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `write_some` functions.
+
+ @param payload The payload of the ping message, which may be empty.
+
+ @throws system_error Thrown on failure.
+ */
+ void
+ ping(ping_data const& payload);
+
+ /** Send a WebSocket ping frame.
+
+ This function is used to synchronously send a ping frame on
+ the stream. The call blocks until one of the following is true:
+
+ @li The ping frame finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `write_some` functions.
+
+ @param payload The payload of the ping message, which may be empty.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ void
+ ping(ping_data const& payload, error_code& ec);
+
+ /** Start an asynchronous operation to send a WebSocket ping frame.
+
+ This function is used to asynchronously send a ping frame to
+ the stream. The function call always returns immediately. The
+ asynchronous operation will continue until one of the following
+ is true:
+
+ @li The entire ping frame is sent.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls 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 writes until this operation completes.
+
+ If a close frame is sent or received before the ping frame is
+ sent, the completion handler will be called with the error
+ set to `boost::asio::error::operation_aborted`.
+
+ @param payload The payload of the ping message, which may be empty.
+
+ @param handler The handler to be called when the read operation
+ completes. Copies will be made of the handler as required. The
+ function signature of the handler must be:
+ @code
+ void handler(
+ error_code const& ec // Result of operation
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class WriteHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code))
+ async_ping(ping_data const& payload, WriteHandler&& handler);
+
+ /** Send a WebSocket pong frame.
+
+ This function is used to synchronously send a pong frame on
+ the stream. The call blocks until one of the following is true:
+
+ @li The pong frame finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `write_some` functions.
+
+ The WebSocket protocol allows pong frames to be sent from either
+ end at any time. It is not necessary to first receive a ping in
+ order to send a pong. The remote peer may use the receipt of a
+ pong frame as an indication that the connection is not dead.
+
+ @param payload The payload of the pong message, which may be empty.
+
+ @throws system_error Thrown on failure.
+ */
+ void
+ pong(ping_data const& payload);
+
+ /** Send a WebSocket pong frame.
+
+ This function is used to synchronously send a pong frame on
+ the stream. The call blocks until one of the following is true:
+
+ @li The pong frame finishes sending.
+
+ @li An error occurs on the stream.
+
+ This function is implemented in terms of one or more calls to the
+ next layer's `write_some` functions.
+
+ The WebSocket protocol allows pong frames to be sent from either
+ end at any time. It is not necessary to first receive a ping in
+ order to send a pong. The remote peer may use the receipt of a
+ pong frame as an indication that the connection is not dead.
+
+ @param payload The payload of the pong message, which may be empty.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ void
+ pong(ping_data const& payload, error_code& ec);
+
+ /** Start an asynchronous operation to send a WebSocket pong frame.
+
+ This function is used to asynchronously send a pong frame to
+ the stream. The function call always returns immediately. The
+ asynchronous operation will continue until one of the following
+ is true:
+
+ @li The entire pong frame is sent.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls 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 writes until this operation completes.
+
+ The WebSocket protocol allows pong frames to be sent from either
+ end at any time. It is not necessary to first receive a ping in
+ order to send a pong. The remote peer may use the receipt of a
+ pong frame as an indication that the connection is not dead.
+
+ If a close frame is sent or received before the pong frame is
+ sent, the completion handler will be called with the error
+ set to `boost::asio::error::operation_aborted`.
+
+ @param payload The payload of the pong message, which may be empty.
+
+ @param handler The handler to be called when the read operation
+ completes. Copies will be made of the handler as required. The
+ function signature of the handler must be:
+ @code
+ void handler(
+ error_code const& ec // Result of operation
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class WriteHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code))
+ async_pong(ping_data const& payload, WriteHandler&& handler);
+
+ //--------------------------------------------------------------------------
+ //
+ // Reading
+ //
+ //--------------------------------------------------------------------------
+
+ /** Read a message
+
+ This function is used to synchronously read a complete
+ message from the stream.
+ The call blocks until one of the following is true:
+
+ @li A complete message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the next
+ layer's `read_some` and `write_some` functions.
+
+ Received message data, if any, is appended to the input area of the
+ buffer. The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ @return The number of message payload bytes appended to the buffer.
+
+ @param buffer A dynamic buffer to hold the message data after any
+ masking or decompression has been applied.
+
+ @throws system_error Thrown to indicate an error. The corresponding
+ error code may be retrieved from the exception object for inspection.
+ */
+ template<class DynamicBuffer>
+ std::size_t
+ read(DynamicBuffer& buffer);
+
+ /** Read a message
+
+ This function is used to synchronously read a complete
+ message from the stream.
+ The call blocks until one of the following is true:
+
+ @li A complete message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the next
+ layer's `read_some` and `write_some` functions.
+
+ Received message data, if any, is appended to the input area of the
+ buffer. The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ @return The number of message payload bytes appended to the buffer.
+
+ @param buffer A dynamic buffer to hold the message data after any
+ masking or decompression has been applied.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ template<class DynamicBuffer>
+ std::size_t
+ read(DynamicBuffer& buffer, error_code& ec);
+
+ /** Read a message asynchronously
+
+ This function is used to asynchronously read a complete
+ message from the stream.
+ The function call always returns immediately.
+ The asynchronous operation will continue until one of the
+ following is true:
+
+ @li A complete message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `async_read_some` and `async_write_some` functions,
+ and is known as a <em>composed operation</em>. The program must
+ ensure that the stream performs no other reads until this operation
+ completes.
+
+ Received message data, if any, is appended to the input area of the
+ buffer. The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ Because of the need to handle control frames, asynchronous read
+ operations can cause writes to take place. These writes are managed
+ transparently; callers can still have one active asynchronous
+ read and asynchronous write operation pending simultaneously
+ (a user initiated call to @ref async_close counts as a write).
+
+ @param buffer A dynamic buffer to hold the message data after
+ any masking or decompression has been applied. This object must
+ remain valid until the handler is called.
+
+ @param handler The handler to be called when the read operation
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code
+ void handler(
+ error_code const& ec, // Result of operation
+ std::size_t bytes_written // Number of bytes appended to buffer
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class DynamicBuffer, class ReadHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+ async_read(
+ DynamicBuffer& buffer,
+ ReadHandler&& handler);
+
+ //--------------------------------------------------------------------------
+
+ /** Read part of a message
+
+ This function is used to synchronously read some
+ message data from the stream.
+ The call blocks until one of the following is true:
+
+ @li Some or all of the message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the next
+ layer's `read_some` and `write_some` functions.
+
+ Received message data, if any, is appended to the input area of the
+ buffer. The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+ The function @ref is_message_done may be called to determine if the
+ message received by the last read operation is complete.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ @return The number of message payload bytes appended to the buffer.
+
+ @param buffer A dynamic buffer to hold the message data after any
+ masking or decompression has been applied.
+
+ @param limit An upper limit on the number of bytes this function
+ will append into the buffer. If this value is zero, then a reasonable
+ size will be chosen automatically.
+
+ @throws system_error Thrown to indicate an error. The corresponding
+ error code may be retrieved from the exception object for inspection.
+ */
+ template<class DynamicBuffer>
+ std::size_t
+ read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit);
+
+ /** Read part of a message
+
+ This function is used to synchronously read some
+ message data from the stream.
+ The call blocks until one of the following is true:
+
+ @li Some or all of the message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the next
+ layer's `read_some` and `write_some` functions.
+
+ Received message data, if any, is appended to the input area of the
+ buffer. The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+ The function @ref is_message_done may be called to determine if the
+ message received by the last read operation is complete.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ @return The number of message payload bytes appended to the buffer.
+
+ @param buffer A dynamic buffer to hold the message data after any
+ masking or decompression has been applied.
+
+ @param limit An upper limit on the number of bytes this function
+ will append into the buffer. If this value is zero, then a reasonable
+ size will be chosen automatically.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ template<class DynamicBuffer>
+ std::size_t
+ read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit,
+ error_code& ec);
+
+ /** Read part of a message asynchronously
+
+ This function is used to asynchronously read part of a
+ message from the stream.
+ The function call always returns immediately.
+ The asynchronous operation will continue until one of the
+ following is true:
+
+ @li Some or all of the message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `async_read_some` and `async_write_some` functions,
+ and is known as a <em>composed operation</em>. The program must
+ ensure that the stream performs no other reads until this operation
+ completes.
+
+ Received message data, if any, is appended to the input area of the
+ buffer. The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+ The function @ref is_message_done may be called to determine if the
+ message received by the last read operation is complete.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ Because of the need to handle control frames, asynchronous read
+ operations can cause writes to take place. These writes are managed
+ transparently; callers can still have one active asynchronous
+ read and asynchronous write operation pending simultaneously
+ (a user initiated call to @ref async_close counts as a write).
+
+ @param buffer A dynamic buffer to hold the message data after
+ any masking or decompression has been applied. This object must
+ remain valid until the handler is called.
+
+ @param limit An upper limit on the number of bytes this function
+ will append into the buffer. If this value is zero, then a reasonable
+ size will be chosen automatically.
+
+ @param handler The handler to be called when the read operation
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code
+ void handler(
+ error_code const& ec, // Result of operation
+ std::size_t bytes_written // Number of bytes appended to buffer
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class DynamicBuffer, class ReadHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+ async_read_some(
+ DynamicBuffer& buffer,
+ std::size_t limit,
+ ReadHandler&& handler);
+
+ //--------------------------------------------------------------------------
+
+ /** Read part of a message
+
+ This function is used to synchronously read some
+ message data from the stream.
+ The call blocks until one of the following is true:
+
+ @li Some or all of the message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the next
+ layer's `read_some` and `write_some` functions.
+
+ Received message data, if any, is written to the buffer sequence.
+ The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+ The function @ref is_message_done may be called to determine if the
+ message received by the last read operation is complete.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ @return The number of message payload bytes written to the
+ buffer sequence.
+
+ @param buffers A buffer sequence to hold the message data after any
+ masking or decompression has been applied.
+
+ @throws system_error Thrown to indicate an error. The corresponding
+ error code may be retrieved from the exception object for inspection.
+ */
+ template<class MutableBufferSequence>
+ std::size_t
+ read_some(
+ MutableBufferSequence const& buffers);
+
+ /** Read part of a message
+
+ This function is used to synchronously read some
+ message data from the stream.
+ The call blocks until one of the following is true:
+
+ @li Some or all of the message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the next
+ layer's `read_some` and `write_some` functions.
+
+ Received message data, if any, is written to the buffer sequence.
+ The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+ The function @ref is_message_done may be called to determine if the
+ message received by the last read operation is complete.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ @return The number of message payload bytes written to the
+ buffer sequence.
+
+ @param buffers A buffer sequence to hold the message data after any
+ masking or decompression has been applied.
+
+ @param ec Set to indicate what error occurred, if any.
+ */
+ template<class MutableBufferSequence>
+ std::size_t
+ read_some(
+ MutableBufferSequence const& buffers,
+ error_code& ec);
+
+ /** Read part of a message asynchronously
+
+ This function is used to asynchronously read part of a
+ message from the stream.
+ The function call always returns immediately.
+ The asynchronous operation will continue until one of the
+ following is true:
+
+ @li Some or all of the message is received.
+
+ @li A close frame is received. In this case the error indicated by
+ the function will be @ref error::closed.
+
+ @li An error occurs on the stream.
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `async_read_some` and `async_write_some` functions,
+ and is known as a <em>composed operation</em>. The program must
+ ensure that the stream performs no other reads until this operation
+ completes.
+
+ Received message data, if any, is written to the buffer sequence.
+ The functions @ref got_binary and @ref got_text may be used
+ to query the stream and determine the type of the last received message.
+ The function @ref is_message_done may be called to determine if the
+ message received by the last read operation is complete.
+
+ While this operation is active, the implementation will read incoming
+ control frames and handle them automatically as follows:
+
+ @li The @ref control_callback will be invoked for each control frame.
+
+ @li For each received ping frame, a pong frame will be
+ automatically sent.
+
+ @li If a close frame is received, the WebSocket close procedure is
+ performed. In this case, when the function returns, the error
+ @ref error::closed will be indicated.
+
+ Because of the need to handle control frames, asynchronous read
+ operations can cause writes to take place. These writes are managed
+ transparently; callers can still have one active asynchronous
+ read and asynchronous write operation pending simultaneously
+ (a user initiated call to @ref async_close counts as a write).
+
+ @param buffers The buffer sequence into which message data will
+ be placed after any masking or decompresison has been applied.
+ The implementation will make copies of this object as needed,
+ but ownership of the underlying memory is not transferred.
+ The caller is responsible for ensuring that the memory
+ locations pointed to by the buffer sequence remains valid
+ until the completion handler is called.
+
+ @param handler The handler to be called when the read operation
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code
+ void handler(
+ error_code const& ec, // Result of operation
+ std::size_t bytes_written // Number of bytes written to the buffer sequence
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<class MutableBufferSequence, class ReadHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+ async_read_some(
+ MutableBufferSequence const& buffers,
+ ReadHandler&& handler);
+
+ //--------------------------------------------------------------------------
+ //
+ // Writing
+ //
+ //--------------------------------------------------------------------------
+
+ /** Write a message to the stream.
+
+ This function is used to synchronously write a message to
+ the stream. The call blocks until one of the following conditions
+ is met:
+
+ @li The entire message is sent.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `write_some` function.
+
+ The current setting of the @ref binary option controls
+ whether the message opcode is set to text or binary. If the
+ @ref auto_fragment option is set, the message will be split
+ into one or more frames as necessary. The actual payload contents
+ sent may be transformed as per the WebSocket protocol settings.
+
+ @param buffers The buffers containing the entire message
+ payload. The implementation will make copies of this object
+ as needed, but ownership of the underlying memory is not
+ transferred. The caller is responsible for ensuring that
+ the memory locations pointed to by buffers remains valid
+ until the completion handler is called.
+
+ @return The number of bytes written from the buffers.
+ If an error occurred, this will be less than the sum
+ of the buffer sizes.
+
+ @throws system_error Thrown on failure.
+
+ @note This function always sends an entire message. To
+ send a message in fragments, use @ref write_some.
+ */
+ template<class ConstBufferSequence>
+ std::size_t
+ write(ConstBufferSequence const& buffers);
+
+ /** Write a message to the stream.
+
+ This function is used to synchronously write a message to
+ the stream. The call blocks until one of the following conditions
+ is met:
+
+ @li The entire message is sent.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls to the
+ next layer's `write_some` function.
+
+ The current setting of the @ref binary option controls
+ whether the message opcode is set to text or binary. If the
+ @ref auto_fragment option is set, the message will be split
+ into one or more frames as necessary. The actual payload contents
+ sent may be transformed as per the WebSocket protocol settings.
+
+ @param buffers The buffers containing the entire message
+ payload. The implementation will make copies of this object
+ as needed, but ownership of the underlying memory is not
+ transferred. The caller is responsible for ensuring that
+ the memory locations pointed to by buffers remains valid
+ until the completion handler is called.
+
+ @return The number of bytes written from the buffers.
+ If an error occurred, this will be less than the sum
+ of the buffer sizes.
+
+ @param ec Set to indicate what error occurred, if any.
+
+ @throws system_error Thrown on failure.
+
+ @note This function always sends an entire message. To
+ send a message in fragments, use @ref write_some.
+ */
+ template<class ConstBufferSequence>
+ std::size_t
+ write(ConstBufferSequence const& buffers, error_code& ec);
+
+ /** Start an asynchronous operation to write a message to the stream.
+
+ This function is used to asynchronously write a message to
+ the stream. The function call always returns immediately.
+ The asynchronous operation will continue until one of the
+ following conditions is true:
+
+ @li The entire message is sent.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls
+ 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).
+
+ The current setting of the @ref binary option controls
+ whether the message opcode is set to text or binary. If the
+ @ref auto_fragment option is set, the message will be split
+ into one or more frames as necessary. The actual payload contents
+ sent may be transformed as per the WebSocket protocol settings.
+
+ @param buffers The buffers containing the entire message
+ payload. The implementation will make copies of this object
+ as needed, but ownership of the underlying memory is not
+ transferred. The caller is responsible for ensuring that
+ the memory locations pointed to by buffers remains valid
+ until the completion handler is called.
+
+ @param handler The handler to be called when the write operation
+ completes. Copies will be made of the handler as required. The
+ function signature of the handler must be:
+ @code
+ void handler(
+ error_code const& ec, // Result of operation
+ std::size_t bytes_transferred // Number of bytes written from the
+ // buffers. If an error occurred,
+ // this will be less than the sum
+ // of the buffer sizes.
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_context::post`.
+ */
+ template<
+ class ConstBufferSequence,
+ class WriteHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+ async_write(
+ ConstBufferSequence const& buffers,
+ WriteHandler&& handler);
+
+ /** Write partial message data on the stream.
+
+ This function is used to write some or all of a message's
+ payload to the stream. The call will block until one of the
+ following conditions is true:
+
+ @li A frame is sent.
+
+ @li Message data is transferred to the write buffer.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls
+ to the stream's `write_some` function.
+
+ 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
+ the @ref binary option. The actual payload sent may be
+ transformed as per the WebSocket protocol settings.
+
+ @param fin `true` if this is the last part of the message.
+
+ @param buffers The input buffer sequence holding the data to write.
+
+ @return The number of bytes written from the buffers.
+ If an error occurred, this will be less than the sum
+ of the buffer sizes.
+
+ @throws system_error Thrown on failure.
+ */
+ template<class ConstBufferSequence>
+ std::size_t
+ write_some(bool fin, ConstBufferSequence const& buffers);
+
+ /** Write partial message data on the stream.
+
+ This function is used to write some or all of a message's
+ payload to the stream. The call will block until one of the
+ following conditions is true:
+
+ @li A frame is sent.
+
+ @li Message data is transferred to the write buffer.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls
+ to the stream's `write_some` function.
+
+ 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
+ the @ref binary option. The actual payload sent may be
+ transformed as per the WebSocket protocol settings.
+
+ @param fin `true` if this is the last part of the message.
+
+ @param buffers The input buffer sequence holding the data to write.
+
+ @param ec Set to indicate what error occurred, if any.
+
+ @return The number of bytes written from the buffers.
+ If an error occurred, this will be less than the sum
+ of the buffer sizes.
+
+ @return The number of bytes consumed in the input buffers.
+ */
+ template<class ConstBufferSequence>
+ std::size_t
+ write_some(bool fin,
+ ConstBufferSequence const& buffers, error_code& ec);
+
+ /** Start an asynchronous operation to send a message frame on the stream.
+
+ This function is used to asynchronously write a message frame
+ on the stream. This function call always returns immediately.
+ The asynchronous operation will continue until one of the following
+ conditions is true:
+
+ @li The entire frame is sent.
+
+ @li An error occurs.
+
+ This operation is implemented in terms of one or more calls
+ to the next layer's `async_write_some` functions, and is known
+ 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).
+
+ 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
+ the @ref binary option. The actual payload sent may be
+ transformed as per the WebSocket protocol settings.
+
+ @param fin `true` if this is the last part of the message.
+
+ @param buffers A object meeting the requirements of
+ ConstBufferSequence which holds the payload data before any
+ masking or compression. Although the buffers object may be copied
+ as necessary, ownership of the underlying buffers is retained by
+ the caller, which must guarantee that they remain valid until
+ the handler is called.
+
+ @param handler The handler to be called when the write completes.
+ Copies will be made of the handler as required. The equivalent
+ function signature of the handler must be:
+ @code void handler(
+ error_code const& ec, // Result of operation
+ std::size_t bytes_transferred // Number of bytes written from the
+ // buffers. If an error occurred,
+ // this will be less than the sum
+ // of the buffer sizes.
+ ); @endcode
+ */
+ template<class ConstBufferSequence, class WriteHandler>
+ BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+ async_write_some(bool fin,
+ ConstBufferSequence const& buffers, WriteHandler&& handler);
+
+private:
+ template<class, class> class accept_op;
+ template<class> class close_op;
+ template<class> class fail_op;
+ template<class> class handshake_op;
+ template<class> class ping_op;
+ template<class> class read_fh_op;
+ template<class, class> class read_some_op;
+ template<class, class> class read_op;
+ template<class> class response_op;
+ template<class, class> class write_some_op;
+ template<class, class> class write_op;
+
+ static void default_decorate_req(request_type&) {}
+ static void default_decorate_res(response_type&) {}
+
+ void open(role_type role);
+ void close();
+ void reset();
+ void begin_msg();
+
+ bool
+ check_open(error_code& ec)
+ {
+ if(status_ != status::open)
+ {
+ ec = boost::asio::error::operation_aborted;
+ return false;
+ }
+ ec.assign(0, ec.category());
+ return true;
+ }
+
+ bool
+ check_ok(error_code& ec)
+ {
+ if(ec)
+ {
+ if(status_ != status::closed)
+ status_ = status::failed;
+ return false;
+ }
+ return true;
+ }
+
+ template<class DynamicBuffer>
+ bool
+ parse_fh(detail::frame_header& fh,
+ DynamicBuffer& b, close_code& code);
+
+ template<class DynamicBuffer>
+ void
+ write_close(DynamicBuffer& b, close_reason const& rc);
+
+ template<class DynamicBuffer>
+ void
+ write_ping(DynamicBuffer& b,
+ detail::opcode op, ping_data const& data);
+
+ template<class Decorator>
+ request_type
+ build_request(detail::sec_ws_key_type& key,
+ string_view host,
+ 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
+ on_response(response_type const& resp,
+ detail::sec_ws_key_type const& key, error_code& ec);
+
+ template<class Decorator>
+ void
+ do_accept(Decorator const& decorator,
+ error_code& ec);
+
+ 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);
+
+ template<class RequestDecorator>
+ void
+ do_handshake(response_type* res_p,
+ string_view host, string_view target,
+ RequestDecorator const& decorator,
+ error_code& ec);
+
+ void
+ do_fail(
+ std::uint16_t code,
+ error_code ev,
+ error_code& ec);
+};
+
+} // websocket
+} // beast
+} // boost
+
+#include <boost/beast/websocket/impl/accept.ipp>
+#include <boost/beast/websocket/impl/close.ipp>
+#include <boost/beast/websocket/impl/handshake.ipp>
+#include <boost/beast/websocket/impl/ping.ipp>
+#include <boost/beast/websocket/impl/read.ipp>
+#include <boost/beast/websocket/impl/stream.ipp>
+#include <boost/beast/websocket/impl/write.ipp>
+
+#endif
diff --git a/boost/beast/websocket/teardown.hpp b/boost/beast/websocket/teardown.hpp
new file mode 100644
index 0000000000..289b57c853
--- /dev/null
+++ b/boost/beast/websocket/teardown.hpp
@@ -0,0 +1,174 @@
+//
+// 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_TEARDOWN_HPP
+#define BOOST_BEAST_WEBSOCKET_TEARDOWN_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/websocket/role.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace websocket {
+
+/** Tear down a connection.
+
+ This tears down a connection. The implementation will call
+ the overload of this function based on the `Socket` parameter
+ used to consruct the socket. When `Socket` is a user defined
+ type, and not a `boost::asio::ip::tcp::socket` or any
+ `boost::asio::ssl::stream`, callers are responsible for
+ providing a suitable overload of this function.
+
+ @param role The role of the local endpoint
+
+ @param socket The socket to tear down.
+
+ @param ec Set to the error if any occurred.
+*/
+template<class Socket>
+void
+teardown(
+ role_type role,
+ Socket& socket,
+ error_code& ec)
+{
+ boost::ignore_unused(role, socket, ec);
+/*
+ If you are trying to use OpenSSL and this goes off, you need to
+ add an include for <boost/beast/websocket/ssl.hpp>.
+
+ If you are creating an instance of beast::websocket::stream with your
+ own user defined type, you must provide an overload of teardown with
+ the corresponding signature (including the role_type).
+*/
+ static_assert(sizeof(Socket)==-1,
+ "Unknown Socket type in teardown.");
+}
+
+/** Start tearing down a connection.
+
+ This begins tearing down a connection asynchronously.
+ The implementation will call the overload of this function
+ based on the `Socket` parameter used to consruct the socket.
+ When `Stream` is a user defined type, and not a
+ `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
+ callers are responsible for providing a suitable overload
+ of this function.
+
+ @param role The role of the local endpoint
+
+ @param socket The socket to tear down.
+
+ @param handler The handler to be called when the request completes.
+ Copies will be made of the handler as required. The equivalent
+ function signature of the handler must be:
+ @code void handler(
+ error_code const& error // result of operation
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using boost::asio::io_context::post().
+
+*/
+template<
+ class Socket,
+ class TeardownHandler>
+void
+async_teardown(
+ role_type role,
+ Socket& socket,
+ TeardownHandler&& handler)
+{
+ boost::ignore_unused(role, socket, handler);
+/*
+ If you are trying to use OpenSSL and this goes off, you need to
+ add an include for <boost/beast/websocket/ssl.hpp>.
+
+ If you are creating an instance of beast::websocket::stream with your
+ own user defined type, you must provide an overload of teardown with
+ the corresponding signature (including the role_type).
+*/
+ static_assert(sizeof(Socket)==-1,
+ "Unknown Socket type in async_teardown.");
+}
+
+} // websocket
+
+//------------------------------------------------------------------------------
+
+namespace websocket {
+
+/** Tear down a `boost::asio::ip::tcp::socket`.
+
+ This tears down a connection. The implementation will call
+ the overload of this function based on the `Stream` parameter
+ used to consruct the socket. When `Stream` is a user defined
+ type, and not a `boost::asio::ip::tcp::socket` or any
+ `boost::asio::ssl::stream`, callers are responsible for
+ providing a suitable overload of this function.
+
+ @param role The role of the local endpoint
+
+ @param socket The socket to tear down.
+
+ @param ec Set to the error if any occurred.
+*/
+void
+teardown(
+ role_type role,
+ boost::asio::ip::tcp::socket& socket,
+ error_code& ec);
+
+/** Start tearing down a `boost::asio::ip::tcp::socket`.
+
+ This begins tearing down a connection asynchronously.
+ The implementation will call the overload of this function
+ based on the `Stream` parameter used to consruct the socket.
+ When `Stream` is a user defined type, and not a
+ `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`,
+ callers are responsible for providing a suitable overload
+ of this function.
+
+ @param role The role of the local endpoint
+
+ @param socket The socket to tear down.
+
+ @param handler The handler to be called when the request completes.
+ Copies will be made of the handler as required. The equivalent
+ function signature of the handler must be:
+ @code void handler(
+ error_code const& error // result of operation
+ );
+ @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using boost::asio::io_context::post().
+
+*/
+template<class TeardownHandler>
+void
+async_teardown(
+ role_type role,
+ boost::asio::ip::tcp::socket& socket,
+ TeardownHandler&& handler);
+
+} // websocket
+} // beast
+} // boost
+
+#include <boost/beast/websocket/impl/teardown.ipp>
+
+#endif
diff --git a/boost/beast/zlib.hpp b/boost/beast/zlib.hpp
new file mode 100644
index 0000000000..87ce609625
--- /dev/null
+++ b/boost/beast/zlib.hpp
@@ -0,0 +1,20 @@
+//
+// 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_ZLIB_HPP
+#define BOOST_BEAST_ZLIB_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+
+#include <boost/beast/zlib/deflate_stream.hpp>
+#include <boost/beast/zlib/error.hpp>
+#include <boost/beast/zlib/inflate_stream.hpp>
+#include <boost/beast/zlib/zlib.hpp>
+
+#endif
diff --git a/boost/beast/zlib/deflate_stream.hpp b/boost/beast/zlib/deflate_stream.hpp
new file mode 100644
index 0000000000..f57a0a5042
--- /dev/null
+++ b/boost/beast/zlib/deflate_stream.hpp
@@ -0,0 +1,404 @@
+//
+// 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_ZLIB_DEFLATE_STREAM_HPP
+#define BOOST_BEAST_ZLIB_DEFLATE_STREAM_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/zlib/error.hpp>
+#include <boost/beast/zlib/zlib.hpp>
+#include <boost/beast/zlib/detail/deflate_stream.hpp>
+#include <algorithm>
+#include <cstdlib>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace zlib {
+
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+/** Raw deflate compressor.
+
+ This is a port of zlib's "deflate" functionality to C++.
+*/
+class deflate_stream
+ : private detail::deflate_stream
+{
+public:
+ /** Construct a default deflate stream.
+
+ Upon construction, the stream settings will be set
+ to these default values:
+
+ @li `level = 6`
+
+ @li `windowBits = 15`
+
+ @li `memLevel = 8`
+
+ @li `strategy = Strategy::normal`
+
+ Although the stream is ready to be used immediately
+ after construction, any required internal buffers are
+ not dynamically allocated until needed.
+ */
+ deflate_stream()
+ {
+ reset(6, 15, DEF_MEM_LEVEL, Strategy::normal);
+ }
+
+ /** Reset the stream and compression settings.
+
+ This function initializes the stream to the specified
+ compression settings.
+
+ Although the stream is ready to be used immediately
+ after a reset, any required internal buffers are not
+ dynamically allocated until needed.
+
+ @note Any unprocessed input or pending output from
+ previous calls are discarded.
+ */
+ void
+ reset(
+ int level,
+ int windowBits,
+ int memLevel,
+ Strategy strategy)
+ {
+ doReset(level, windowBits, memLevel, strategy);
+ }
+
+ /** Reset the stream without deallocating memory.
+
+ This function performs the equivalent of calling `clear`
+ followed by `reset` with the same compression settings,
+ without deallocating the internal buffers.
+
+ @note Any unprocessed input or pending output from
+ previous calls are discarded.
+ */
+ void
+ reset()
+ {
+ doReset();
+ }
+
+ /** Clear the stream.
+
+ This function resets the stream and frees all dynamically
+ allocated internal buffers. The compression settings are
+ left unchanged.
+
+ @note Any unprocessed input or pending output from
+ previous calls are discarded.
+ */
+ void
+ clear()
+ {
+ doClear();
+ }
+
+ /** Returns the upper limit on the size of a compressed block.
+
+ This function makes a conservative estimate of the maximum number
+ of bytes needed to store the result of compressing a block of
+ data based on the current compression level and strategy.
+
+ @param sourceLen The size of the uncompressed data.
+
+ @return The maximum number of resulting compressed bytes.
+ */
+ std::size_t
+ upper_bound(std::size_t sourceLen) const
+ {
+ return doUpperBound(sourceLen);
+ }
+
+ /** Fine tune internal compression parameters.
+
+ Compression parameters should only be tuned by someone who
+ understands the algorithm used by zlib's deflate for searching
+ for the best matching string, and even then only by the most
+ fanatic optimizer trying to squeeze out the last compressed bit
+ for their specific input data. Read the deflate.c source code
+ (ZLib) for the meaning of the max_lazy, good_length, nice_length,
+ and max_chain parameters.
+ */
+ void
+ tune(
+ int good_length,
+ int max_lazy,
+ int nice_length,
+ int max_chain)
+ {
+ doTune(good_length, max_lazy, nice_length, max_chain);
+ }
+
+ /** Compress input and write output.
+
+ This function compresses as much data as possible, and stops when
+ the input buffer becomes empty or the output buffer becomes full.
+ It may introduce some output latency (reading input without
+ producing any output) except when forced to flush.
+
+ In each call, one or both of these actions are performed:
+
+ @li Compress more input starting at `zs.next_in` and update
+ `zs.next_in` and `zs.avail_in` accordingly. If not all
+ input can be processed (because there is not enough room in
+ the output buffer), `zs.next_in` and `zs.avail_in` are updated
+ and processing will resume at this point for the next call.
+
+ @li Provide more output starting at `zs.next_out` and update
+ `zs.next_out` and `zs.avail_out` accordingly. This action is
+ forced if the parameter flush is not `Flush::none`. Forcing
+ flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call, the application must ensure that at least one
+ of the actions is possible, by providing more input and/or
+ consuming more output, and updating `zs.avail_in` or `zs.avail_out`
+ accordingly; `zs.avail_out` should never be zero before the call.
+ The application can consume the compressed output when it wants,
+ for example when the output buffer is full (`zs.avail_out == 0`),
+ or after each call of `write`. If `write` returns no error
+ with zero `zs.avail_out`, it must be called again after making
+ room in the output buffer because there might be more output
+ pending.
+
+ Normally the parameter flush is set to `Flush::none`, which allows
+ deflate to decide how much data to accumulate before producing
+ output, in order to maximize compression.
+
+ If the parameter flush is set to `Flush::sync`, all pending output
+ is flushed to the output buffer and the output is aligned on a
+ byte boundary, so that the decompressor can get all input data
+ available so far. In particular `zs.avail_in` is zero after the
+ call if enough output space has been provided before the call.
+ Flushing may degrade compression for some compression algorithms
+ and so it should be used only when necessary. This completes the
+ current deflate block and follows it with an empty stored block
+ that is three bits plus filler bits to the next byte, followed
+ by the four bytes `{ 0x00, 0x00 0xff 0xff }`.
+
+ If flush is set to `Flush::partial`, all pending output is flushed
+ to the output buffer, but the output is not aligned to a byte
+ boundary. All of the input data so far will be available to the
+ decompressor, as for Z_SYNC_FLUSH. This completes the current
+ deflate block and follows it with an empty fixed codes block that
+ is 10 bits long. This assures that enough bytes are output in order
+ for the decompressor to finish the block before the empty fixed
+ code block.
+
+ If flush is set to `Flush::block`, a deflate block is completed
+ and emitted, as for `Flush::sync`, but the output is not aligned
+ on a byte boundary, and up to seven bits of the current block are
+ held to be written as the next byte after the next deflate block
+ is completed. In this case, the decompressor may not be provided
+ enough bits at this point in order to complete decompression of
+ the data provided so far to the compressor. It may need to wait
+ for the next block to be emitted. This is for advanced applications
+ that need to control the emission of deflate blocks.
+
+ If flush is set to `Flush::full`, all output is flushed as with
+ `Flush::sync`, and the compression state is reset so that
+ decompression can restart from this point if previous compressed
+ data has been damaged or if random access is desired. Using
+ `Flush::full` too often can seriously degrade compression.
+
+ If `write` returns with `zs.avail_out == 0`, this function must
+ be called again with the same value of the flush parameter and
+ more output space (updated `zs.avail_out`), until the flush is
+ complete (`write` returns with non-zero `zs.avail_out`). In the
+ case of a `Flush::full`or `Flush::sync`, make sure that
+ `zs.avail_out` is greater than six to avoid repeated flush markers
+ due to `zs.avail_out == 0` on return.
+
+ If the parameter flush is set to `Flush::finish`, pending input
+ is processed, pending output is flushed and deflate returns the
+ error `error::end_of_stream` if there was enough output space;
+ if deflate returns with no error, this function must be called
+ again with `Flush::finish` and more output space (updated
+ `zs.avail_out`) but no more input data, until it returns the
+ error `error::end_of_stream` or another error. After `write` has
+ returned the `error::end_of_stream` error, the only possible
+ operations on the stream are to reset or destroy.
+
+ `Flush::finish` can be used immediately after initialization
+ if all the compression is to be done in a single step. In this
+ case, `zs.avail_out` must be at least value returned by
+ `upper_bound` (see below). Then `write` is guaranteed to return
+ the `error::end_of_stream` error. If not enough output space
+ is provided, deflate will not return `error::end_of_stream`,
+ and it must be called again as described above.
+
+ `write` returns no error if some progress has been made (more
+ input processed or more output produced), `error::end_of_stream`
+ if all input has been consumed and all output has been produced
+ (only when flush is set to `Flush::finish`), `error::stream_error`
+ if the stream state was inconsistent (for example if `zs.next_in`
+ or `zs.next_out` was `nullptr`), `error::need_buffers` if no
+ progress is possible (for example `zs.avail_in` or `zs.avail_out`
+ was zero). Note that `error::need_buffers` is not fatal, and
+ `write` can be called again with more input and more output space
+ to continue compressing.
+ */
+ void
+ write(
+ z_params& zs,
+ Flush flush,
+ error_code& ec)
+ {
+ doWrite(zs, flush, ec);
+ }
+
+ /** Update the compression level and strategy.
+
+ This function dynamically updates the compression level and
+ compression strategy. The interpretation of level and strategy
+ is as in @ref reset. This can be used to switch between compression
+ and straight copy of the input data, or to switch to a different kind
+ of input data requiring a different strategy. If the compression level
+ is changed, the input available so far is compressed with the old level
+ (and may be flushed); the new level will take effect only at the next
+ call of @ref write.
+
+ Before the call of `params`, the stream state must be set as for a
+ call of @ref write, since the currently available input may have to be
+ compressed and flushed. In particular, `zs.avail_out` must be non-zero.
+
+ @return `Z_OK` if success, `Z_STREAM_ERROR` if the source stream state
+ was inconsistent or if a parameter was invalid, `error::need_buffers`
+ if `zs.avail_out` was zero.
+ */
+ void
+ params(
+ z_params& zs,
+ int level,
+ Strategy strategy,
+ error_code& ec)
+ {
+ doParams(zs, level, strategy, ec);
+ }
+
+ /** Return bits pending in the output.
+
+ This function returns the number of bytes and bits of output
+ that have been generated, but not yet provided in the available
+ output. The bytes not provided would be due to the available
+ output space having being consumed. The number of bits of output
+ not provided are between 0 and 7, where they await more bits to
+ join them in order to fill out a full byte. If pending or bits
+ are `nullptr`, then those values are not set.
+
+ @return `Z_OK` if success, or `Z_STREAM_ERROR` if the source
+ stream state was inconsistent.
+ */
+ void
+ pending(unsigned *value, int *bits)
+ {
+ doPending(value, bits);
+ }
+
+ /** Insert bits into the compressed output stream.
+
+ This function inserts bits in the deflate output stream. The
+ intent is that this function is used to start off the deflate
+ output with the bits leftover from a previous deflate stream when
+ appending to it. As such, this function can only be used for raw
+ deflate, and must be used before the first `write` call after an
+ initialization. `bits` must be less than or equal to 16, and that
+ many of the least significant bits of `value` will be inserted in
+ the output.
+
+ @return `error::need_buffers` if there was not enough room in
+ the internal buffer to insert the bits.
+ */
+ void
+ prime(int bits, int value, error_code& ec)
+ {
+ return doPrime(bits, value, ec);
+ }
+};
+
+/** Returns the upper limit on the size of a compressed block.
+
+ This function makes a conservative estimate of the maximum number
+ of bytes needed to store the result of compressing a block of
+ data.
+
+ @param bytes The size of the uncompressed data.
+
+ @return The maximum number of resulting compressed bytes.
+*/
+std::size_t
+deflate_upper_bound(std::size_t bytes);
+
+/* For the default windowBits of 15 and memLevel of 8, this function returns
+ a close to exact, as well as small, upper bound on the compressed size.
+ They are coded as constants here for a reason--if the #define's are
+ changed, then this function needs to be changed as well. The return
+ value for 15 and 8 only works for those exact settings.
+
+ For any setting other than those defaults for windowBits and memLevel,
+ the value returned is a conservative worst case for the maximum expansion
+ resulting from using fixed blocks instead of stored blocks, which deflate
+ can emit on compressed data for some combinations of the parameters.
+
+ This function could be more sophisticated to provide closer upper bounds for
+ every combination of windowBits and memLevel. But even the conservative
+ upper bound of about 14% expansion does not seem onerous for output buffer
+ allocation.
+*/
+inline
+std::size_t
+deflate_upper_bound(std::size_t bytes)
+{
+ return bytes +
+ ((bytes + 7) >> 3) +
+ ((bytes + 63) >> 6) + 5 +
+ 6;
+}
+
+} // zlib
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/zlib/detail/bitstream.hpp b/boost/beast/zlib/detail/bitstream.hpp
new file mode 100644
index 0000000000..eca32bc424
--- /dev/null
+++ b/boost/beast/zlib/detail/bitstream.hpp
@@ -0,0 +1,207 @@
+//
+// 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
+//
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef BOOST_BEAST_ZLIB_DETAIL_BITSTREAM_HPP
+#define BOOST_BEAST_ZLIB_DETAIL_BITSTREAM_HPP
+
+#include <boost/assert.hpp>
+#include <cstdint>
+#include <iterator>
+
+namespace boost {
+namespace beast {
+namespace zlib {
+namespace detail {
+
+class bitstream
+{
+ using value_type = std::uint32_t;
+
+ value_type v_ = 0;
+ unsigned n_ = 0;
+
+public:
+ // returns the number of bits in the reservoir
+ unsigned
+ size() const
+ {
+ return n_;
+ }
+
+ // discard n bits
+ void
+ drop(std::size_t n)
+ {
+ BOOST_ASSERT(n <= n_);
+ n_ -= static_cast<unsigned>(n);
+ v_ >>= n;
+ }
+
+ // flush everything
+ void
+ flush()
+ {
+ n_ = 0;
+ v_ = 0;
+ }
+
+ // flush to the next byte boundary
+ void
+ flush_byte()
+ {
+ drop(n_ % 8);
+ }
+
+ // ensure at least n bits
+ template<class FwdIt>
+ bool
+ fill(std::size_t n, FwdIt& first, FwdIt const& last);
+
+ // fill 8 bits, unchecked
+ template<class FwdIt>
+ void
+ fill_8(FwdIt& it);
+
+ // fill 16 bits, unchecked
+ template<class FwdIt>
+ void
+ fill_16(FwdIt& it);
+
+ // return n bits
+ template<class Unsigned>
+ void
+ peek(Unsigned& value, std::size_t n);
+
+ // return everything in the reservoir
+ value_type
+ peek_fast() const
+ {
+ return v_;
+ }
+
+ // return n bits, and consume
+ template<class Unsigned>
+ void
+ read(Unsigned& value, std::size_t n);
+
+ // rewind by the number of whole bytes stored (unchecked)
+ template<class BidirIt>
+ void
+ rewind(BidirIt& it);
+};
+
+template<class FwdIt>
+inline
+bool
+bitstream::
+fill(std::size_t n, FwdIt& first, FwdIt const& last)
+{
+ while(n_ < n)
+ {
+ if(first == last)
+ return false;
+ v_ += static_cast<value_type>(*first++) << n_;
+ n_ += 8;
+ }
+ return true;
+}
+
+template<class FwdIt>
+inline
+void
+bitstream::
+fill_8(FwdIt& it)
+{
+ v_ += static_cast<value_type>(*it++) << n_;
+ n_ += 8;
+}
+
+template<class FwdIt>
+inline
+void
+bitstream::
+fill_16(FwdIt& it)
+{
+ v_ += static_cast<value_type>(*it++) << n_;
+ n_ += 8;
+ v_ += static_cast<value_type>(*it++) << n_;
+ n_ += 8;
+}
+
+template<class Unsigned>
+inline
+void
+bitstream::
+peek(Unsigned& value, std::size_t n)
+{
+ BOOST_ASSERT(n <= sizeof(value)*8);
+ BOOST_ASSERT(n <= n_);
+ value = static_cast<Unsigned>(
+ v_ & ((1ULL << n) - 1));
+}
+
+template<class Unsigned>
+inline
+void
+bitstream::
+read(Unsigned& value, std::size_t n)
+{
+ BOOST_ASSERT(n < sizeof(v_)*8);
+ BOOST_ASSERT(n <= n_);
+ value = static_cast<Unsigned>(
+ v_ & ((1ULL << n) - 1));
+ v_ >>= n;
+ n_ -= static_cast<unsigned>(n);
+}
+
+template<class BidirIt>
+inline
+void
+bitstream::
+rewind(BidirIt& it)
+{
+ auto len = n_ >> 3;
+ it = std::prev(it, len);
+ n_ &= 7;
+ v_ &= (1U << n_) - 1;
+}
+
+} // detail
+} // zlib
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/zlib/detail/deflate_stream.hpp b/boost/beast/zlib/detail/deflate_stream.hpp
new file mode 100644
index 0000000000..f0a91e8ad1
--- /dev/null
+++ b/boost/beast/zlib/detail/deflate_stream.hpp
@@ -0,0 +1,3006 @@
+//
+// 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
+//
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef BOOST_BEAST_ZLIB_DETAIL_DEFLATE_STREAM_HPP
+#define BOOST_BEAST_ZLIB_DETAIL_DEFLATE_STREAM_HPP
+
+#include <boost/beast/zlib/zlib.hpp>
+#include <boost/beast/zlib/detail/ranges.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/assert.hpp>
+#include <boost/config.hpp>
+#include <boost/make_unique.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <memory>
+#include <stdexcept>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace zlib {
+namespace detail {
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in http://tools.ietf.org/html/rfc1951
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+class deflate_stream
+{
+protected:
+ // Upper limit on code length
+ static std::uint8_t constexpr maxBits = 15;
+
+ // Number of length codes, not counting the special END_BLOCK code
+ static std::uint16_t constexpr lengthCodes = 29;
+
+ // Number of literal bytes 0..255
+ static std::uint16_t constexpr literals = 256;
+
+ // Number of Literal or Length codes, including the END_BLOCK code
+ static std::uint16_t constexpr lCodes = literals + 1 + lengthCodes;
+
+ // Number of distance code lengths
+ static std::uint16_t constexpr dCodes = 30;
+
+ // Number of codes used to transfer the bit lengths
+ static std::uint16_t constexpr blCodes = 19;
+
+ // Number of distance codes
+ static std::uint16_t constexpr distCodeLen = 512;
+
+ // Size limit on bit length codes
+ static std::uint8_t constexpr maxBlBits= 7;
+
+ static std::uint16_t constexpr minMatch = 3;
+ static std::uint16_t constexpr maxMatch = 258;
+
+ // Can't change minMatch without also changing code, see original zlib
+ BOOST_STATIC_ASSERT(minMatch == 3);
+
+ // end of block literal code
+ static std::uint16_t constexpr END_BLOCK = 256;
+
+ // repeat previous bit length 3-6 times (2 bits of repeat count)
+ static std::uint8_t constexpr REP_3_6 = 16;
+
+ // repeat a zero length 3-10 times (3 bits of repeat count)
+ static std::uint8_t constexpr REPZ_3_10 = 17;
+
+ // repeat a zero length 11-138 times (7 bits of repeat count)
+ static std::uint8_t constexpr REPZ_11_138 = 18;
+
+ // The three kinds of block type
+ static std::uint8_t constexpr STORED_BLOCK = 0;
+ static std::uint8_t constexpr STATIC_TREES = 1;
+ static std::uint8_t constexpr DYN_TREES = 2;
+
+ // Maximum value for memLevel in deflateInit2
+ static std::uint8_t constexpr max_mem_level = 9;
+
+ // Default memLevel
+ static std::uint8_t constexpr DEF_MEM_LEVEL = max_mem_level;
+
+ /* Note: the deflate() code requires max_lazy >= minMatch and max_chain >= 4
+ For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ meaning.
+ */
+
+ // maximum heap size
+ static std::uint16_t constexpr HEAP_SIZE = 2 * lCodes + 1;
+
+ // size of bit buffer in bi_buf
+ static std::uint8_t constexpr Buf_size = 16;
+
+ // Matches of length 3 are discarded if their distance exceeds kTooFar
+ static std::size_t constexpr kTooFar = 4096;
+
+ /* Minimum amount of lookahead, except at the end of the input file.
+ See deflate.c for comments about the minMatch+1.
+ */
+ static std::size_t constexpr kMinLookahead = maxMatch + minMatch+1;
+
+ /* Number of bytes after end of data in window to initialize in order
+ to avoid memory checker errors from longest match routines
+ */
+ static std::size_t constexpr kWinInit = maxMatch;
+
+ // Describes a single value and its code string.
+ struct ct_data
+ {
+ std::uint16_t fc; // frequency count or bit string
+ std::uint16_t dl; // parent node in tree or length of bit string
+
+ bool
+ operator==(ct_data const& rhs) const
+ {
+ return fc == rhs.fc && dl == rhs.dl;
+ }
+ };
+
+ struct static_desc
+ {
+ ct_data const* static_tree;// static tree or NULL
+ std::uint8_t const* extra_bits; // extra bits for each code or NULL
+ std::uint16_t extra_base; // base index for extra_bits
+ std::uint16_t elems; // max number of elements in the tree
+ std::uint8_t max_length; // max bit length for the codes
+ };
+
+ struct lut_type
+ {
+ // Number of extra bits for each length code
+ std::uint8_t const extra_lbits[lengthCodes] = {
+ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0
+ };
+
+ // Number of extra bits for each distance code
+ std::uint8_t const extra_dbits[dCodes] = {
+ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13
+ };
+
+ // Number of extra bits for each bit length code
+ std::uint8_t const extra_blbits[blCodes] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7
+ };
+
+ // The lengths of the bit length codes are sent in order
+ // of decreasing probability, to avoid transmitting the
+ // lengths for unused bit length codes.
+ std::uint8_t const bl_order[blCodes] = {
+ 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15
+ };
+
+ ct_data ltree[lCodes + 2];
+
+ ct_data dtree[dCodes];
+
+ // Distance codes. The first 256 values correspond to the distances
+ // 3 .. 258, the last 256 values correspond to the top 8 bits of
+ // the 15 bit distances.
+ std::uint8_t dist_code[distCodeLen];
+
+ std::uint8_t length_code[maxMatch-minMatch+1];
+
+ std::uint8_t base_length[lengthCodes];
+
+ std::uint16_t base_dist[dCodes];
+
+ static_desc l_desc = {
+ ltree, extra_lbits, literals+1, lCodes, maxBits
+ };
+
+ static_desc d_desc = {
+ dtree, extra_dbits, 0, dCodes, maxBits
+ };
+
+ static_desc bl_desc =
+ {
+ nullptr, extra_blbits, 0, blCodes, maxBlBits
+ };
+ };
+
+ struct tree_desc
+ {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_desc const* stat_desc; /* the corresponding static tree */
+ };
+
+ enum block_state
+ {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+ };
+
+ // VFALCO This might not be needed, e.g. for zip/gzip
+ enum StreamStatus
+ {
+ EXTRA_STATE = 69,
+ NAME_STATE = 73,
+ COMMENT_STATE = 91,
+ HCRC_STATE = 103,
+ BUSY_STATE = 113,
+ FINISH_STATE = 666
+ };
+
+ /* A std::uint16_t is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+ using IPos = unsigned;
+
+ using self = deflate_stream;
+ typedef block_state(self::*compress_func)(z_params& zs, Flush flush);
+
+ //--------------------------------------------------------------------------
+
+ lut_type const& lut_;
+
+ bool inited_ = false;
+ std::size_t buf_size_;
+ std::unique_ptr<std::uint8_t[]> buf_;
+
+ int status_; // as the name implies
+ Byte* pending_buf_; // output still pending
+ std::uint32_t
+ pending_buf_size_; // size of pending_buf
+ Byte* pending_out_; // next pending byte to output to the stream
+ uInt pending_; // nb of bytes in the pending buffer
+ boost::optional<Flush>
+ last_flush_; // value of flush param for previous deflate call
+
+ uInt w_size_; // LZ77 window size (32K by default)
+ uInt w_bits_; // log2(w_size) (8..16)
+ uInt w_mask_; // w_size - 1
+
+ /* Sliding window. Input bytes are read into the second half of the window,
+ and move to the first half later to keep a dictionary of at least wSize
+ bytes. With this organization, matches are limited to a distance of
+ wSize-maxMatch bytes, but this ensures that IO is always
+ performed with a length multiple of the block size. Also, it limits
+ the window size to 64K.
+ To do: use the user input buffer as sliding window.
+ */
+ Byte *window_ = nullptr;
+
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ is directly used as sliding window.
+ */
+ std::uint32_t window_size_;
+
+ /* Link to older string with same hash index. To limit the size of this
+ array to 64K, this link is maintained only for the last 32K strings.
+ An index in this array is thus a window index modulo 32K.
+ */
+ std::uint16_t* prev_;
+
+ std::uint16_t* head_; // Heads of the hash chains or 0
+
+ uInt ins_h_; // hash index of string to be inserted
+ uInt hash_size_; // number of elements in hash table
+ uInt hash_bits_; // log2(hash_size)
+ uInt hash_mask_; // hash_size-1
+
+ /* Number of bits by which ins_h must be shifted at each input
+ step. It must be such that after minMatch steps,
+ the oldest byte no longer takes part in the hash key, that is:
+ hash_shift * minMatch >= hash_bits
+ */
+ uInt hash_shift_;
+
+ /* Window position at the beginning of the current output block.
+ Gets negative when the window is moved backwards.
+ */
+ long block_start_;
+
+ uInt match_length_; // length of best match
+ IPos prev_match_; // previous match
+ int match_available_; // set if previous match exists
+ uInt strstart_; // start of string to insert
+ uInt match_start_; // start of matching string
+ uInt lookahead_; // number of valid bytes ahead in window
+
+ /* Length of the best match at previous step. Matches not greater
+ than this are discarded. This is used in the lazy match evaluation.
+ */
+ uInt prev_length_;
+
+ /* To speed up deflation, hash chains are never searched beyond
+ this length. A higher limit improves compression ratio but
+ degrades the speed.
+ */
+ uInt max_chain_length_;
+
+ /* Attempt to find a better match only when the current match is strictly
+ smaller than this value. This mechanism is used only for compression
+ levels >= 4.
+
+ OR Insert new strings in the hash table only if the match length is not
+ greater than this length. This saves time but degrades compression.
+ used only for compression levels <= 3.
+ */
+ uInt max_lazy_match_;
+
+ int level_; // compression level (1..9)
+ Strategy strategy_; // favor or force Huffman coding
+
+ // Use a faster search when the previous match is longer than this
+ uInt good_match_;
+
+ int nice_match_; // Stop searching when current match exceeds this
+
+ ct_data dyn_ltree_[
+ HEAP_SIZE]; // literal and length tree
+ ct_data dyn_dtree_[
+ 2*dCodes+1]; // distance tree
+ ct_data bl_tree_[
+ 2*blCodes+1]; // Huffman tree for bit lengths
+
+ tree_desc l_desc_; // desc. for literal tree
+ tree_desc d_desc_; // desc. for distance tree
+ tree_desc bl_desc_; // desc. for bit length tree
+
+ // number of codes at each bit length for an optimal tree
+ std::uint16_t bl_count_[maxBits+1];
+
+ // Index within the heap array of least frequent node in the Huffman tree
+ static std::size_t constexpr kSmallest = 1;
+
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ heap[0] is not used. The same heap array is used to build all trees.
+ */
+
+ int heap_[2*lCodes+1]; // heap used to build the Huffman trees
+ int heap_len_; // number of elements in the heap
+ int heap_max_; // element of largest frequency
+
+ // Depth of each subtree used as tie breaker for trees of equal frequency
+ std::uint8_t depth_[2*lCodes+1];
+
+ std::uint8_t *l_buf_; // buffer for literals or lengths
+
+ /* Size of match buffer for literals/lengths.
+ There are 4 reasons for limiting lit_bufsize to 64K:
+ - frequencies can be kept in 16 bit counters
+ - if compression is not successful for the first block, all input
+ data is still in the window so we can still emit a stored block even
+ when input comes from standard input. (This can also be done for
+ all blocks if lit_bufsize is not greater than 32K.)
+ - if compression is not successful for a file smaller than 64K, we can
+ even emit a stored file instead of a stored block (saving 5 bytes).
+ This is applicable only for zip (not gzip or zlib).
+ - creating new Huffman trees less frequently may not provide fast
+ adaptation to changes in the input data statistics. (Take for
+ example a binary file with poorly compressible code followed by
+ a highly compressible string table.) Smaller buffer sizes give
+ fast adaptation but have of course the overhead of transmitting
+ trees more frequently.
+ - I can't count above 4
+ */
+ uInt lit_bufsize_;
+ uInt last_lit_; // running index in l_buf_
+
+ /* Buffer for distances. To simplify the code, d_buf_ and l_buf_
+ have the same number of elements. To use different lengths, an
+ extra flag array would be necessary.
+ */
+ std::uint16_t* d_buf_;
+
+ std::uint32_t opt_len_; // bit length of current block with optimal trees
+ std::uint32_t static_len_; // bit length of current block with static trees
+ uInt matches_; // number of string matches in current block
+ uInt insert_; // bytes at end of window left to insert
+
+ /* Output buffer.
+ Bits are inserted starting at the bottom (least significant bits).
+ */
+ std::uint16_t bi_buf_;
+
+ /* Number of valid bits in bi_buf._ All bits above the last valid
+ bit are always zero.
+ */
+ int bi_valid_;
+
+ /* High water mark offset in window for initialized bytes -- bytes
+ above this are set to zero in order to avoid memory check warnings
+ when longest match routines access bytes past the input. This is
+ then updated to the new high water mark.
+ */
+ std::uint32_t high_water_;
+
+ //--------------------------------------------------------------------------
+
+ deflate_stream()
+ : lut_(get_lut())
+ {
+ }
+
+ /* In order to simplify the code, particularly on 16 bit machines, match
+ distances are limited to MAX_DIST instead of WSIZE.
+ */
+ std::size_t
+ max_dist() const
+ {
+ return w_size_ - kMinLookahead;
+ }
+
+ void
+ put_byte(std::uint8_t c)
+ {
+ pending_buf_[pending_++] = c;
+ }
+
+ void
+ put_short(std::uint16_t w)
+ {
+ put_byte(w & 0xff);
+ put_byte(w >> 8);
+ }
+
+ /* Send a value on a given number of bits.
+ IN assertion: length <= 16 and value fits in length bits.
+ */
+ void
+ send_bits(int value, int length)
+ {
+ if(bi_valid_ > (int)Buf_size - length)
+ {
+ bi_buf_ |= (std::uint16_t)value << bi_valid_;
+ put_short(bi_buf_);
+ bi_buf_ = (std::uint16_t)value >> (Buf_size - bi_valid_);
+ bi_valid_ += length - Buf_size;
+ }
+ else
+ {
+ bi_buf_ |= (std::uint16_t)(value) << bi_valid_;
+ bi_valid_ += length;
+ }
+ }
+
+ // Send a code of the given tree
+ void
+ send_code(int value, ct_data const* tree)
+ {
+ send_bits(tree[value].fc, tree[value].dl);
+ }
+
+ /* Mapping from a distance to a distance code. dist is the
+ distance - 1 and must not have side effects. _dist_code[256]
+ and _dist_code[257] are never used.
+ */
+ std::uint8_t
+ d_code(unsigned dist)
+ {
+ if(dist < 256)
+ return lut_.dist_code[dist];
+ return lut_.dist_code[256+(dist>>7)];
+ }
+
+ /* Update a hash value with the given input byte
+ IN assertion: all calls to to update_hash are made with
+ consecutive input characters, so that a running hash
+ key can be computed from the previous key instead of
+ complete recalculation each time.
+ */
+ void
+ update_hash(uInt& h, std::uint8_t c)
+ {
+ h = ((h << hash_shift_) ^ c) & hash_mask_;
+ }
+
+ /* Initialize the hash table (avoiding 64K overflow for 16
+ bit systems). prev[] will be initialized on the fly.
+ */
+ void
+ clear_hash()
+ {
+ head_[hash_size_-1] = 0;
+ std::memset((Byte *)head_, 0,
+ (unsigned)(hash_size_-1)*sizeof(*head_));
+ }
+
+ /* Compares two subtrees, using the tree depth as tie breaker
+ when the subtrees have equal frequency. This minimizes the
+ worst case length.
+ */
+ bool
+ smaller(ct_data const* tree, int n, int m)
+ {
+ return tree[n].fc < tree[m].fc ||
+ (tree[n].fc == tree[m].fc &&
+ depth_[n] <= depth_[m]);
+ }
+
+ /* Insert string str in the dictionary and set match_head to the
+ previous head of the hash chain (the most recent string with
+ same hash key). Return the previous length of the hash chain.
+ If this file is compiled with -DFASTEST, the compression level
+ is forced to 1, and no hash chains are maintained.
+ IN assertion: all calls to to INSERT_STRING are made with
+ consecutive input characters and the first minMatch
+ bytes of str are valid (except for the last minMatch-1
+ bytes of the input file).
+ */
+ void
+ insert_string(IPos& hash_head)
+ {
+ update_hash(ins_h_, window_[strstart_ + (minMatch-1)]);
+ hash_head = prev_[strstart_ & w_mask_] = head_[ins_h_];
+ head_[ins_h_] = (std::uint16_t)strstart_;
+ }
+
+ //--------------------------------------------------------------------------
+
+ /* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+ struct config
+ {
+ std::uint16_t good_length; /* reduce lazy search above this match length */
+ std::uint16_t max_lazy; /* do not perform lazy search above this match length */
+ std::uint16_t nice_length; /* quit search above this match length */
+ std::uint16_t max_chain;
+ compress_func func;
+
+ config(
+ std::uint16_t good_length_,
+ std::uint16_t max_lazy_,
+ std::uint16_t nice_length_,
+ std::uint16_t max_chain_,
+ compress_func func_)
+ : good_length(good_length_)
+ , max_lazy(max_lazy_)
+ , nice_length(nice_length_)
+ , max_chain(max_chain_)
+ , func(func_)
+ {
+ }
+ };
+
+ static
+ config
+ get_config(std::size_t level)
+ {
+ switch(level)
+ {
+ // good lazy nice chain
+ case 0: return { 0, 0, 0, 0, &self::deflate_stored}; // store only
+ case 1: return { 4, 4, 8, 4, &self::deflate_fast}; // max speed, no lazy matches
+ case 2: return { 4, 5, 16, 8, &self::deflate_fast};
+ case 3: return { 4, 6, 32, 32, &self::deflate_fast};
+ case 4: return { 4, 4, 16, 16, &self::deflate_slow}; // lazy matches
+ case 5: return { 8, 16, 32, 32, &self::deflate_slow};
+ case 6: return { 8, 16, 128, 128, &self::deflate_slow};
+ case 7: return { 8, 32, 128, 256, &self::deflate_slow};
+ case 8: return { 32, 128, 258, 1024, &self::deflate_slow};
+ default:
+ case 9: return { 32, 258, 258, 4096, &self::deflate_slow}; // max compression
+ }
+ }
+
+ void
+ maybe_init()
+ {
+ if(! inited_)
+ init();
+ }
+
+ template<class Unsigned>
+ static
+ Unsigned
+ bi_reverse(Unsigned code, unsigned len);
+
+ template<class = void>
+ static
+ void
+ gen_codes(ct_data *tree, int max_code, std::uint16_t *bl_count);
+
+ template<class = void>
+ static
+ lut_type const&
+ get_lut();
+
+ template<class = void> void doReset (int level, int windowBits, int memLevel, Strategy strategy);
+ template<class = void> void doReset ();
+ template<class = void> void doClear ();
+ template<class = void> std::size_t doUpperBound (std::size_t sourceLen) const;
+ template<class = void> void doTune (int good_length, int max_lazy, int nice_length, int max_chain);
+ template<class = void> void doParams (z_params& zs, int level, Strategy strategy, error_code& ec);
+ template<class = void> void doWrite (z_params& zs, boost::optional<Flush> flush, error_code& ec);
+ template<class = void> void doDictionary (Byte const* dict, uInt dictLength, error_code& ec);
+ template<class = void> void doPrime (int bits, int value, error_code& ec);
+ template<class = void> void doPending (unsigned* value, int* bits);
+
+ template<class = void> void init ();
+ template<class = void> void lm_init ();
+ template<class = void> void init_block ();
+ template<class = void> void pqdownheap (ct_data const* tree, int k);
+ template<class = void> void pqremove (ct_data const* tree, int& top);
+ template<class = void> void gen_bitlen (tree_desc *desc);
+ template<class = void> void build_tree (tree_desc *desc);
+ template<class = void> void scan_tree (ct_data *tree, int max_code);
+ template<class = void> void send_tree (ct_data *tree, int max_code);
+ template<class = void> int build_bl_tree ();
+ template<class = void> void send_all_trees (int lcodes, int dcodes, int blcodes);
+ template<class = void> void compress_block (ct_data const* ltree, ct_data const* dtree);
+ template<class = void> int detect_data_type ();
+ template<class = void> void bi_windup ();
+ template<class = void> void bi_flush ();
+ template<class = void> void copy_block (char *buf, unsigned len, int header);
+
+ template<class = void> void tr_init ();
+ template<class = void> void tr_align ();
+ template<class = void> void tr_flush_bits ();
+ template<class = void> void tr_stored_block (char *bu, std::uint32_t stored_len, int last);
+ template<class = void> void tr_tally_dist (std::uint16_t dist, std::uint8_t len, bool& flush);
+ template<class = void> void tr_tally_lit (std::uint8_t c, bool& flush);
+
+ template<class = void> void tr_flush_block (z_params& zs, char *buf, std::uint32_t stored_len, int last);
+ template<class = void> void fill_window (z_params& zs);
+ template<class = void> void flush_pending (z_params& zs);
+ template<class = void> void flush_block (z_params& zs, bool last);
+ template<class = void> int read_buf (z_params& zs, Byte *buf, unsigned size);
+ template<class = void> uInt longest_match (IPos cur_match);
+
+ template<class = void> block_state f_stored (z_params& zs, Flush flush);
+ template<class = void> block_state f_fast (z_params& zs, Flush flush);
+ template<class = void> block_state f_slow (z_params& zs, Flush flush);
+ template<class = void> block_state f_rle (z_params& zs, Flush flush);
+ template<class = void> block_state f_huff (z_params& zs, Flush flush);
+
+ block_state
+ deflate_stored(z_params& zs, Flush flush)
+ {
+ return f_stored(zs, flush);
+ }
+
+ block_state
+ deflate_fast(z_params& zs, Flush flush)
+ {
+ return f_fast(zs, flush);
+ }
+
+ block_state
+ deflate_slow(z_params& zs, Flush flush)
+ {
+ return f_slow(zs, flush);
+ }
+
+ block_state
+ deflate_rle(z_params& zs, Flush flush)
+ {
+ return f_rle(zs, flush);
+ }
+
+ block_state
+ deflate_huff(z_params& zs, Flush flush)
+ {
+ return f_huff(zs, flush);
+ }
+};
+
+//--------------------------------------------------------------------------
+
+// Reverse the first len bits of a code
+template<class Unsigned>
+inline
+Unsigned
+deflate_stream::
+bi_reverse(Unsigned code, unsigned len)
+{
+ BOOST_STATIC_ASSERT(std::is_unsigned<Unsigned>::value);
+ BOOST_ASSERT(len <= 8 * sizeof(unsigned));
+ Unsigned res = 0;
+ do
+ {
+ res |= code & 1;
+ code >>= 1;
+ res <<= 1;
+ }
+ while(--len > 0);
+ return res >> 1;
+}
+
+/* Generate the codes for a given tree and bit counts (which need not be optimal).
+ IN assertion: the array bl_count contains the bit length statistics for
+ the given tree and the field len is set for all tree elements.
+ OUT assertion: the field code is set for all tree elements of non
+ zero code length.
+*/
+template<class>
+void
+deflate_stream::
+gen_codes(ct_data *tree, int max_code, std::uint16_t *bl_count)
+{
+ std::uint16_t next_code[maxBits+1]; /* next code value for each bit length */
+ std::uint16_t code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ // The distribution counts are first used to
+ // generate the code values without bit reversal.
+ for(bits = 1; bits <= maxBits; bits++)
+ {
+ code = (code + bl_count[bits-1]) << 1;
+ next_code[bits] = code;
+ }
+ // Check that the bit counts in bl_count are consistent.
+ // The last code must be all ones.
+ BOOST_ASSERT(code + bl_count[maxBits]-1 == (1<<maxBits)-1);
+ for(n = 0; n <= max_code; n++)
+ {
+ int len = tree[n].dl;
+ if(len == 0)
+ continue;
+ tree[n].fc = bi_reverse(next_code[len]++, len);
+ }
+}
+
+template<class>
+auto
+deflate_stream::get_lut() ->
+ lut_type const&
+{
+ struct init
+ {
+ lut_type tables;
+
+ init()
+ {
+ // number of codes at each bit length for an optimal tree
+ //std::uint16_t bl_count[maxBits+1];
+
+ // Initialize the mapping length (0..255) -> length code (0..28)
+ std::uint8_t length = 0;
+ for(std::uint8_t code = 0; code < lengthCodes-1; ++code)
+ {
+ tables.base_length[code] = length;
+ auto const run = 1U << tables.extra_lbits[code];
+ for(unsigned n = 0; n < run; ++n)
+ tables.length_code[length++] = code;
+ }
+ BOOST_ASSERT(length == 0);
+ // Note that the length 255 (match length 258) can be represented
+ // in two different ways: code 284 + 5 bits or code 285, so we
+ // overwrite length_code[255] to use the best encoding:
+ tables.length_code[255] = lengthCodes-1;
+
+ // Initialize the mapping dist (0..32K) -> dist code (0..29)
+ {
+ std::uint8_t code;
+ std::uint16_t dist = 0;
+ for(code = 0; code < 16; code++)
+ {
+ tables.base_dist[code] = dist;
+ auto const run = 1U << tables.extra_dbits[code];
+ for(unsigned n = 0; n < run; ++n)
+ tables.dist_code[dist++] = code;
+ }
+ BOOST_ASSERT(dist == 256);
+ // from now on, all distances are divided by 128
+ dist >>= 7;
+ for(; code < dCodes; ++code)
+ {
+ tables.base_dist[code] = dist << 7;
+ auto const run = 1U << (tables.extra_dbits[code]-7);
+ for(std::size_t n = 0; n < run; ++n)
+ tables.dist_code[256 + dist++] = code;
+ }
+ BOOST_ASSERT(dist == 256);
+ }
+
+ // Construct the codes of the static literal tree
+ std::uint16_t bl_count[maxBits+1];
+ std::memset(bl_count, 0, sizeof(bl_count));
+ unsigned n = 0;
+ while (n <= 143)
+ tables.ltree[n++].dl = 8;
+ bl_count[8] += 144;
+ while (n <= 255)
+ tables.ltree[n++].dl = 9;
+ bl_count[9] += 112;
+ while (n <= 279)
+ tables.ltree[n++].dl = 7;
+ bl_count[7] += 24;
+ while (n <= 287)
+ tables.ltree[n++].dl = 8;
+ bl_count[8] += 8;
+ // Codes 286 and 287 do not exist, but we must include them in the tree
+ // construction to get a canonical Huffman tree (longest code all ones)
+ gen_codes(tables.ltree, lCodes+1, bl_count);
+
+ for(n = 0; n < dCodes; ++n)
+ {
+ tables.dtree[n].dl = 5;
+ tables.dtree[n].fc =
+ static_cast<std::uint16_t>(bi_reverse(n, 5));
+ }
+ }
+ };
+ static init const data;
+ return data.tables;
+}
+
+template<class>
+void
+deflate_stream::
+doReset(
+ int level,
+ int windowBits,
+ int memLevel,
+ Strategy strategy)
+{
+ if(level == default_size)
+ level = 6;
+
+ // VFALCO What do we do about this?
+ // until 256-byte window bug fixed
+ if(windowBits == 8)
+ windowBits = 9;
+
+ if(level < 0 || level > 9)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid level"});
+
+ if(windowBits < 8 || windowBits > 15)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid windowBits"});
+
+ if(memLevel < 1 || memLevel > max_mem_level)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "invalid memLevel"});
+
+ w_bits_ = windowBits;
+
+ hash_bits_ = memLevel + 7;
+
+ // 16K elements by default
+ lit_bufsize_ = 1 << (memLevel + 6);
+
+ level_ = level;
+ strategy_ = strategy;
+ inited_ = false;
+}
+
+template<class>
+void
+deflate_stream::
+doReset()
+{
+ inited_ = false;
+}
+
+template<class>
+void
+deflate_stream::
+doClear()
+{
+ inited_ = false;
+ buf_.reset();
+}
+
+template<class>
+std::size_t
+deflate_stream::
+doUpperBound(std::size_t sourceLen) const
+{
+ std::size_t complen;
+ std::size_t wraplen;
+
+ /* conservative upper bound for compressed data */
+ complen = sourceLen +
+ ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
+
+ /* compute wrapper length */
+ wraplen = 0;
+
+ /* if not default parameters, return conservative bound */
+ if(w_bits_ != 15 || hash_bits_ != 8 + 7)
+ return complen + wraplen;
+
+ /* default settings: return tight bound for that case */
+ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
+ (sourceLen >> 25) + 13 - 6 + wraplen;
+}
+
+template<class>
+void
+deflate_stream::
+doTune(
+ int good_length,
+ int max_lazy,
+ int nice_length,
+ int max_chain)
+{
+ good_match_ = good_length;
+ nice_match_ = nice_length;
+ max_lazy_match_ = max_lazy;
+ max_chain_length_ = max_chain;
+}
+
+template<class>
+void
+deflate_stream::
+doParams(z_params& zs, int level, Strategy strategy, error_code& ec)
+{
+ compress_func func;
+
+ if(level == default_size)
+ level = 6;
+ if(level < 0 || level > 9)
+ {
+ ec = error::stream_error;
+ return;
+ }
+ func = get_config(level_).func;
+
+ if((strategy != strategy_ || func != get_config(level).func) &&
+ zs.total_in != 0)
+ {
+ // Flush the last buffer:
+ doWrite(zs, Flush::block, ec);
+ if(ec == error::need_buffers && pending_ == 0)
+ ec.assign(0, ec.category());
+ }
+ if(level_ != level)
+ {
+ level_ = level;
+ max_lazy_match_ = get_config(level).max_lazy;
+ good_match_ = get_config(level).good_length;
+ nice_match_ = get_config(level).nice_length;
+ max_chain_length_ = get_config(level).max_chain;
+ }
+ strategy_ = strategy;
+}
+
+// VFALCO boost::optional param is a workaround for
+// gcc "maybe uninitialized" warning
+// https://github.com/boostorg/beast/issues/532
+//
+template<class>
+void
+deflate_stream::
+doWrite(z_params& zs, boost::optional<Flush> flush, error_code& ec)
+{
+ maybe_init();
+
+ if(zs.next_out == 0 || (zs.next_in == 0 && zs.avail_in != 0) ||
+ (status_ == FINISH_STATE && flush != Flush::finish))
+ {
+ ec = error::stream_error;
+ return;
+ }
+ if(zs.avail_out == 0)
+ {
+ ec = error::need_buffers;
+ return;
+ }
+
+ // value of flush param for previous deflate call
+ auto old_flush = boost::make_optional<Flush>(
+ last_flush_.is_initialized(),
+ last_flush_ ? *last_flush_ : Flush::none);
+
+ last_flush_ = flush;
+
+ // Flush as much pending output as possible
+ if(pending_ != 0)
+ {
+ flush_pending(zs);
+ if(zs.avail_out == 0)
+ {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ last_flush_ = boost::none;
+ return;
+ }
+ }
+ else if(zs.avail_in == 0 && (
+ old_flush && flush <= *old_flush
+ ) && flush != Flush::finish)
+ {
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Flush::finish, we keep
+ * returning Z_STREAM_END instead of Z_BUF_ERROR.
+ */
+ ec = error::need_buffers;
+ return;
+ }
+
+ // User must not provide more input after the first FINISH:
+ if(status_ == FINISH_STATE && zs.avail_in != 0)
+ {
+ ec = error::need_buffers;
+ return;
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if(zs.avail_in != 0 || lookahead_ != 0 ||
+ (flush != Flush::none && status_ != FINISH_STATE))
+ {
+ block_state bstate;
+
+ switch(strategy_)
+ {
+ case Strategy::huffman:
+ bstate = deflate_huff(zs, flush.get());
+ break;
+ case Strategy::rle:
+ bstate = deflate_rle(zs, flush.get());
+ break;
+ default:
+ {
+ bstate = (this->*(get_config(level_).func))(zs, flush.get());
+ break;
+ }
+ }
+
+ if(bstate == finish_started || bstate == finish_done)
+ {
+ status_ = FINISH_STATE;
+ }
+ if(bstate == need_more || bstate == finish_started)
+ {
+ if(zs.avail_out == 0)
+ {
+ last_flush_ = boost::none; /* avoid BUF_ERROR next call, see above */
+ }
+ return;
+ /* If flush != Flush::none && avail_out == 0, the next call
+ of deflate should use the same flush parameter to make sure
+ that the flush is complete. So we don't have to output an
+ empty block here, this will be done at next call. This also
+ ensures that for a very small output buffer, we emit at most
+ one empty block.
+ */
+ }
+ if(bstate == block_done)
+ {
+ if(flush == Flush::partial)
+ {
+ tr_align();
+ }
+ else if(flush != Flush::block)
+ {
+ /* FULL_FLUSH or SYNC_FLUSH */
+ tr_stored_block((char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if(flush == Flush::full)
+ {
+ clear_hash(); // forget history
+ if(lookahead_ == 0)
+ {
+ strstart_ = 0;
+ block_start_ = 0L;
+ insert_ = 0;
+ }
+ }
+ }
+ flush_pending(zs);
+ if(zs.avail_out == 0)
+ {
+ last_flush_ = boost::none; /* avoid BUF_ERROR at next call, see above */
+ return;
+ }
+ }
+ }
+
+ if(flush == Flush::finish)
+ {
+ ec = error::end_of_stream;
+ return;
+ }
+}
+
+// VFALCO Warning: untested
+template<class>
+void
+deflate_stream::
+doDictionary(Byte const* dict, uInt dictLength, error_code& ec)
+{
+ if(lookahead_)
+ {
+ ec = error::stream_error;
+ return;
+ }
+
+ maybe_init();
+
+ /* if dict would fill window, just replace the history */
+ if(dictLength >= w_size_)
+ {
+ clear_hash();
+ strstart_ = 0;
+ block_start_ = 0L;
+ insert_ = 0;
+ dict += dictLength - w_size_; /* use the tail */
+ dictLength = w_size_;
+ }
+
+ /* insert dict into window and hash */
+ z_params zs;
+ zs.avail_in = dictLength;
+ zs.next_in = (const Byte *)dict;
+ zs.avail_out = 0;
+ zs.next_out = 0;
+ fill_window(zs);
+ while(lookahead_ >= minMatch)
+ {
+ uInt str = strstart_;
+ uInt n = lookahead_ - (minMatch-1);
+ do
+ {
+ update_hash(ins_h_, window_[str + minMatch-1]);
+ prev_[str & w_mask_] = head_[ins_h_];
+ head_[ins_h_] = (std::uint16_t)str;
+ str++;
+ }
+ while(--n);
+ strstart_ = str;
+ lookahead_ = minMatch-1;
+ fill_window(zs);
+ }
+ strstart_ += lookahead_;
+ block_start_ = (long)strstart_;
+ insert_ = lookahead_;
+ lookahead_ = 0;
+ match_length_ = prev_length_ = minMatch-1;
+ match_available_ = 0;
+}
+
+template<class>
+void
+deflate_stream::
+doPrime(int bits, int value, error_code& ec)
+{
+ maybe_init();
+
+ if((Byte *)(d_buf_) < pending_out_ + ((Buf_size + 7) >> 3))
+ {
+ ec = error::need_buffers;
+ return;
+ }
+
+ do
+ {
+ int put = Buf_size - bi_valid_;
+ if(put > bits)
+ put = bits;
+ bi_buf_ |= (std::uint16_t)((value & ((1 << put) - 1)) << bi_valid_);
+ bi_valid_ += put;
+ tr_flush_bits();
+ value >>= put;
+ bits -= put;
+ }
+ while(bits);
+}
+
+template<class>
+void
+deflate_stream::
+doPending(unsigned* value, int* bits)
+{
+ if(value != 0)
+ *value = pending_;
+ if(bits != 0)
+ *bits = bi_valid_;
+}
+
+//--------------------------------------------------------------------------
+
+// Do lazy initialization
+template<class>
+void
+deflate_stream::
+init()
+{
+ // Caller must set these:
+ // w_bits_
+ // hash_bits_
+ // lit_bufsize_
+ // level_
+ // strategy_
+
+ w_size_ = 1 << w_bits_;
+ w_mask_ = w_size_ - 1;
+
+ hash_size_ = 1 << hash_bits_;
+ hash_mask_ = hash_size_ - 1;
+ hash_shift_ = ((hash_bits_+minMatch-1)/minMatch);
+
+ auto const nwindow = w_size_ * 2*sizeof(Byte);
+ auto const nprev = w_size_ * sizeof(std::uint16_t);
+ auto const nhead = hash_size_ * sizeof(std::uint16_t);
+ auto const noverlay = lit_bufsize_ * (sizeof(std::uint16_t)+2);
+ auto const needed = nwindow + nprev + nhead + noverlay;
+
+ if(! buf_ || buf_size_ != needed)
+ {
+ buf_ = boost::make_unique_noinit<
+ std::uint8_t[]>(needed);
+ buf_size_ = needed;
+ }
+
+ window_ = reinterpret_cast<Byte*>(buf_.get());
+ prev_ = reinterpret_cast<std::uint16_t*>(buf_.get() + nwindow);
+ head_ = reinterpret_cast<std::uint16_t*>(buf_.get() + nwindow + nprev);
+
+ /* We overlay pending_buf_ and d_buf_ + l_buf_. This works
+ since the average output size for(length, distance)
+ codes is <= 24 bits.
+ */
+ auto overlay = reinterpret_cast<std::uint16_t*>(
+ buf_.get() + nwindow + nprev + nhead);
+
+ // nothing written to window_ yet
+ high_water_ = 0;
+
+ pending_buf_ =
+ reinterpret_cast<std::uint8_t*>(overlay);
+ pending_buf_size_ =
+ static_cast<std::uint32_t>(lit_bufsize_) *
+ (sizeof(std::uint16_t) + 2L);
+
+ d_buf_ = overlay + lit_bufsize_ / sizeof(std::uint16_t);
+ l_buf_ = pending_buf_ + (1 + sizeof(std::uint16_t)) * lit_bufsize_;
+
+ pending_ = 0;
+ pending_out_ = pending_buf_;
+
+ status_ = BUSY_STATE;
+ last_flush_ = Flush::none;
+
+ tr_init();
+ lm_init();
+
+ inited_ = true;
+}
+
+/* Initialize the "longest match" routines for a new zlib stream
+*/
+template<class>
+void
+deflate_stream::
+lm_init()
+{
+ window_size_ = (std::uint32_t)2L*w_size_;
+
+ clear_hash();
+
+ /* Set the default configuration parameters:
+ */
+ // VFALCO TODO just copy the config struct
+ max_lazy_match_ = get_config(level_).max_lazy;
+ good_match_ = get_config(level_).good_length;
+ nice_match_ = get_config(level_).nice_length;
+ max_chain_length_ = get_config(level_).max_chain;
+
+ strstart_ = 0;
+ block_start_ = 0L;
+ lookahead_ = 0;
+ insert_ = 0;
+ match_length_ = prev_length_ = minMatch-1;
+ match_available_ = 0;
+ ins_h_ = 0;
+}
+
+// Initialize a new block.
+//
+template<class>
+void
+deflate_stream::
+init_block()
+{
+ for(int n = 0; n < lCodes; n++)
+ dyn_ltree_[n].fc = 0;
+ for(int n = 0; n < dCodes; n++)
+ dyn_dtree_[n].fc = 0;
+ for(int n = 0; n < blCodes; n++)
+ bl_tree_[n].fc = 0;
+ dyn_ltree_[END_BLOCK].fc = 1;
+ opt_len_ = 0L;
+ static_len_ = 0L;
+ last_lit_ = 0;
+ matches_ = 0;
+}
+
+/* Restore the heap property by moving down the tree starting at node k,
+ exchanging a node with the smallest of its two sons if necessary,
+ stopping when the heap property is re-established (each father smaller
+ than its two sons).
+*/
+template<class>
+void
+deflate_stream::
+pqdownheap(
+ ct_data const* tree, // the tree to restore
+ int k) // node to move down
+{
+ int v = heap_[k];
+ int j = k << 1; // left son of k
+ while(j <= heap_len_)
+ {
+ // Set j to the smallest of the two sons:
+ if(j < heap_len_ &&
+ smaller(tree, heap_[j+1], heap_[j]))
+ j++;
+ // Exit if v is smaller than both sons
+ if(smaller(tree, v, heap_[j]))
+ break;
+
+ // Exchange v with the smallest son
+ heap_[k] = heap_[j];
+ k = j;
+
+ // And continue down the tree,
+ // setting j to the left son of k
+ j <<= 1;
+ }
+ heap_[k] = v;
+}
+
+/* Remove the smallest element from the heap and recreate the heap
+ with one less element. Updates heap and heap_len.
+*/
+template<class>
+inline
+void
+deflate_stream::
+pqremove(ct_data const* tree, int& top)
+{
+ top = heap_[kSmallest];
+ heap_[kSmallest] = heap_[heap_len_--];
+ pqdownheap(tree, kSmallest);
+}
+
+/* Compute the optimal bit lengths for a tree and update the total bit length
+ for the current block.
+ IN assertion: the fields freq and dad are set, heap[heap_max] and
+ above are the tree nodes sorted by increasing frequency.
+ OUT assertions: the field len is set to the optimal bit length, the
+ array bl_count contains the frequencies for each bit length.
+ The length opt_len is updated; static_len is also updated if stree is
+ not null.
+*/
+template<class>
+void
+deflate_stream::
+gen_bitlen(tree_desc *desc)
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ ct_data const* stree = desc->stat_desc->static_tree;
+ std::uint8_t const *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; // heap index
+ int n, m; // iterate over the tree elements
+ int bits; // bit length
+ int xbits; // extra bits
+ std::uint16_t f; // frequency
+ int overflow = 0; // number of elements with bit length too large
+
+ std::fill(&bl_count_[0], &bl_count_[maxBits+1], std::uint16_t{0});
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[heap_[heap_max_]].dl = 0; // root of the heap
+
+ for(h = heap_max_+1; h < HEAP_SIZE; h++) {
+ n = heap_[h];
+ bits = tree[tree[n].dl].dl + 1;
+ if(bits > max_length) bits = max_length, overflow++;
+ // We overwrite tree[n].dl which is no longer needed
+ tree[n].dl = (std::uint16_t)bits;
+
+ if(n > max_code)
+ continue; // not a leaf node
+
+ bl_count_[bits]++;
+ xbits = 0;
+ if(n >= base)
+ xbits = extra[n-base];
+ f = tree[n].fc;
+ opt_len_ += (std::uint32_t)f * (bits + xbits);
+ if(stree)
+ static_len_ += (std::uint32_t)f * (stree[n].dl + xbits);
+ }
+ if(overflow == 0)
+ return;
+
+ // Find the first bit length which could increase:
+ do
+ {
+ bits = max_length-1;
+ while(bl_count_[bits] == 0)
+ bits--;
+ bl_count_[bits]--; // move one leaf down the tree
+ bl_count_[bits+1] += 2; // move one overflow item as its brother
+ bl_count_[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ }
+ while(overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for(bits = max_length; bits != 0; bits--)
+ {
+ n = bl_count_[bits];
+ while(n != 0)
+ {
+ m = heap_[--h];
+ if(m > max_code)
+ continue;
+ if((unsigned) tree[m].dl != (unsigned) bits)
+ {
+ opt_len_ += ((long)bits - (long)tree[m].dl) *(long)tree[m].fc;
+ tree[m].dl = (std::uint16_t)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* Construct one Huffman tree and assigns the code bit strings and lengths.
+ Update the total bit length for the current block.
+ IN assertion: the field freq is set for all tree elements.
+ OUT assertions: the fields len and code are set to the optimal bit length
+ and corresponding code. The length opt_len is updated; static_len is
+ also updated if stree is not null. The field max_code is set.
+*/
+template<class>
+void
+deflate_stream::
+build_tree(tree_desc *desc)
+{
+ ct_data *tree = desc->dyn_tree;
+ ct_data const* stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; // iterate over heap elements
+ int max_code = -1; // largest code with non zero frequency
+ int node; // new node being created
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[kSmallest]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ heap_len_ = 0;
+ heap_max_ = HEAP_SIZE;
+
+ for(n = 0; n < elems; n++)
+ {
+ if(tree[n].fc != 0)
+ {
+ heap_[++(heap_len_)] = max_code = n;
+ depth_[n] = 0;
+ }
+ else
+ {
+ tree[n].dl = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while(heap_len_ < 2)
+ {
+ node = heap_[++(heap_len_)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].fc = 1;
+ depth_[node] = 0;
+ opt_len_--;
+ if(stree)
+ static_len_ -= stree[node].dl;
+ // node is 0 or 1 so it does not have extra bits
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for(n = heap_len_/2; n >= 1; n--)
+ pqdownheap(tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do
+ {
+ pqremove(tree, n); /* n = node of least frequency */
+ m = heap_[kSmallest]; /* m = node of next least frequency */
+
+ heap_[--(heap_max_)] = n; /* keep the nodes sorted by frequency */
+ heap_[--(heap_max_)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].fc = tree[n].fc + tree[m].fc;
+ depth_[node] = (std::uint8_t)((depth_[n] >= depth_[m] ?
+ depth_[n] : depth_[m]) + 1);
+ tree[n].dl = tree[m].dl = (std::uint16_t)node;
+ /* and insert the new node in the heap */
+ heap_[kSmallest] = node++;
+ pqdownheap(tree, kSmallest);
+
+ }
+ while(heap_len_ >= 2);
+
+ heap_[--(heap_max_)] = heap_[kSmallest];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen((tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes(tree, max_code, bl_count_);
+}
+
+/* Scan a literal or distance tree to determine the frequencies
+ of the codes in the bit length tree.
+*/
+template<class>
+void
+deflate_stream::
+scan_tree(
+ ct_data *tree, // the tree to be scanned
+ int max_code) // and its largest code of non zero frequency
+{
+ int n; // iterates over all tree elements
+ int prevlen = -1; // last emitted length
+ int curlen; // length of current code
+ int nextlen = tree[0].dl; // length of next code
+ std::uint16_t count = 0; // repeat count of the current code
+ int max_count = 7; // max repeat count
+ int min_count = 4; // min repeat count
+
+ if(nextlen == 0)
+ {
+ max_count = 138;
+ min_count = 3;
+ }
+ tree[max_code+1].dl = (std::uint16_t)0xffff; // guard
+
+ for(n = 0; n <= max_code; n++)
+ {
+ curlen = nextlen; nextlen = tree[n+1].dl;
+ if(++count < max_count && curlen == nextlen)
+ {
+ continue;
+ }
+ else if(count < min_count)
+ {
+ bl_tree_[curlen].fc += count;
+ }
+ else if(curlen != 0)
+ {
+ if(curlen != prevlen) bl_tree_[curlen].fc++;
+ bl_tree_[REP_3_6].fc++;
+ }
+ else if(count <= 10)
+ {
+ bl_tree_[REPZ_3_10].fc++;
+ }
+ else
+ {
+ bl_tree_[REPZ_11_138].fc++;
+ }
+ count = 0;
+ prevlen = curlen;
+ if(nextlen == 0)
+ {
+ max_count = 138;
+ min_count = 3;
+ }
+ else if(curlen == nextlen)
+ {
+ max_count = 6;
+ min_count = 3;
+ }
+ else
+ {
+ max_count = 7;
+ min_count = 4;
+ }
+ }
+}
+
+/* Send a literal or distance tree in compressed form,
+ using the codes in bl_tree.
+*/
+template<class>
+void
+deflate_stream::
+send_tree(
+ ct_data *tree, // the tree to be scanned
+ int max_code) // and its largest code of non zero frequency
+{
+ int n; // iterates over all tree elements
+ int prevlen = -1; // last emitted length
+ int curlen; // length of current code
+ int nextlen = tree[0].dl; // length of next code
+ int count = 0; // repeat count of the current code
+ int max_count = 7; // max repeat count
+ int min_count = 4; // min repeat count
+
+ // tree[max_code+1].dl = -1; // guard already set
+ if(nextlen == 0)
+ {
+ max_count = 138;
+ min_count = 3;
+ }
+
+ for(n = 0; n <= max_code; n++)
+ {
+ curlen = nextlen;
+ nextlen = tree[n+1].dl;
+ if(++count < max_count && curlen == nextlen)
+ {
+ continue;
+ }
+ else if(count < min_count)
+ {
+ do
+ {
+ send_code(curlen, bl_tree_);
+ }
+ while (--count != 0);
+ }
+ else if(curlen != 0)
+ {
+ if(curlen != prevlen)
+ {
+ send_code(curlen, bl_tree_);
+ count--;
+ }
+ BOOST_ASSERT(count >= 3 && count <= 6);
+ send_code(REP_3_6, bl_tree_);
+ send_bits(count-3, 2);
+ }
+ else if(count <= 10)
+ {
+ send_code(REPZ_3_10, bl_tree_);
+ send_bits(count-3, 3);
+ }
+ else
+ {
+ send_code(REPZ_11_138, bl_tree_);
+ send_bits(count-11, 7);
+ }
+ count = 0;
+ prevlen = curlen;
+ if(nextlen == 0)
+ {
+ max_count = 138;
+ min_count = 3;
+ }
+ else if(curlen == nextlen)
+ {
+ max_count = 6;
+ min_count = 3;
+ }
+ else
+ {
+ max_count = 7;
+ min_count = 4;
+ }
+ }
+}
+
+/* Construct the Huffman tree for the bit lengths and return
+ the index in bl_order of the last bit length code to send.
+*/
+template<class>
+int
+deflate_stream::
+build_bl_tree()
+{
+ int max_blindex; // index of last bit length code of non zero freq
+
+ // Determine the bit length frequencies for literal and distance trees
+ scan_tree((ct_data *)dyn_ltree_, l_desc_.max_code);
+ scan_tree((ct_data *)dyn_dtree_, d_desc_.max_code);
+
+ // Build the bit length tree:
+ build_tree((tree_desc *)(&(bl_desc_)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for(max_blindex = blCodes-1; max_blindex >= 3; max_blindex--)
+ {
+ if(bl_tree_[lut_.bl_order[max_blindex]].dl != 0)
+ break;
+ }
+ // Update opt_len to include the bit length tree and counts
+ opt_len_ += 3*(max_blindex+1) + 5+5+4;
+ return max_blindex;
+}
+
+/* Send the header for a block using dynamic Huffman trees: the counts,
+ the lengths of the bit length codes, the literal tree and the distance
+ tree.
+ IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+*/
+template<class>
+void
+deflate_stream::
+send_all_trees(
+ int lcodes,
+ int dcodes,
+ int blcodes) // number of codes for each tree
+{
+ int rank; // index in bl_order
+
+ BOOST_ASSERT(lcodes >= 257 && dcodes >= 1 && blcodes >= 4);
+ BOOST_ASSERT(lcodes <= lCodes && dcodes <= dCodes && blcodes <= blCodes);
+ send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt
+ send_bits(dcodes-1, 5);
+ send_bits(blcodes-4, 4); // not -3 as stated in appnote.txt
+ for(rank = 0; rank < blcodes; rank++)
+ send_bits(bl_tree_[lut_.bl_order[rank]].dl, 3);
+ send_tree((ct_data *)dyn_ltree_, lcodes-1); // literal tree
+ send_tree((ct_data *)dyn_dtree_, dcodes-1); // distance tree
+}
+
+/* Send the block data compressed using the given Huffman trees
+*/
+template<class>
+void
+deflate_stream::
+compress_block(
+ ct_data const* ltree, // literal tree
+ ct_data const* dtree) // distance tree
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if(last_lit_ != 0)
+ {
+ do
+ {
+ dist = d_buf_[lx];
+ lc = l_buf_[lx++];
+ if(dist == 0)
+ {
+ send_code(lc, ltree); /* send a literal byte */
+ }
+ else
+ {
+ /* Here, lc is the match length - minMatch */
+ code = lut_.length_code[lc];
+ send_code(code+literals+1, ltree); /* send the length code */
+ extra = lut_.extra_lbits[code];
+ if(extra != 0)
+ {
+ lc -= lut_.base_length[code];
+ send_bits(lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ BOOST_ASSERT(code < dCodes);
+
+ send_code(code, dtree); /* send the distance code */
+ extra = lut_.extra_dbits[code];
+ if(extra != 0)
+ {
+ dist -= lut_.base_dist[code];
+ send_bits(dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ BOOST_ASSERT((uInt)(pending_) < lit_bufsize_ + 2*lx);
+ }
+ while(lx < last_lit_);
+ }
+
+ send_code(END_BLOCK, ltree);
+}
+
+/* Check if the data type is TEXT or BINARY, using the following algorithm:
+ - TEXT if the two conditions below are satisfied:
+ a) There are no non-portable control characters belonging to the
+ "black list" (0..6, 14..25, 28..31).
+ b) There is at least one printable character belonging to the
+ "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
+ - BINARY otherwise.
+ - The following partially-portable control characters form a
+ "gray list" that is ignored in this detection algorithm:
+ (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
+ IN assertion: the fields fc of dyn_ltree are set.
+*/
+template<class>
+int
+deflate_stream::
+detect_data_type()
+{
+ /* black_mask is the bit mask of black-listed bytes
+ * set bits 0..6, 14..25, and 28..31
+ * 0xf3ffc07f = binary 11110011111111111100000001111111
+ */
+ unsigned long black_mask = 0xf3ffc07fUL;
+ int n;
+
+ // Check for non-textual ("black-listed") bytes.
+ for(n = 0; n <= 31; n++, black_mask >>= 1)
+ if((black_mask & 1) && (dyn_ltree_[n].fc != 0))
+ return binary;
+
+ // Check for textual ("white-listed") bytes. */
+ if(dyn_ltree_[9].fc != 0 || dyn_ltree_[10].fc != 0
+ || dyn_ltree_[13].fc != 0)
+ return text;
+ for(n = 32; n < literals; n++)
+ if(dyn_ltree_[n].fc != 0)
+ return text;
+
+ /* There are no "black-listed" or "white-listed" bytes:
+ * this stream either is empty or has tolerated ("gray-listed") bytes only.
+ */
+ return binary;
+}
+
+/* Flush the bit buffer and align the output on a byte boundary
+*/
+template<class>
+void
+deflate_stream::
+bi_windup()
+{
+ if(bi_valid_ > 8)
+ put_short(bi_buf_);
+ else if(bi_valid_ > 0)
+ put_byte((Byte)bi_buf_);
+ bi_buf_ = 0;
+ bi_valid_ = 0;
+}
+
+/* Flush the bit buffer, keeping at most 7 bits in it.
+*/
+template<class>
+void
+deflate_stream::
+bi_flush()
+{
+ if(bi_valid_ == 16)
+ {
+ put_short(bi_buf_);
+ bi_buf_ = 0;
+ bi_valid_ = 0;
+ }
+ else if(bi_valid_ >= 8)
+ {
+ put_byte((Byte)bi_buf_);
+ bi_buf_ >>= 8;
+ bi_valid_ -= 8;
+ }
+}
+
+/* Copy a stored block, storing first the length and its
+ one's complement if requested.
+*/
+template<class>
+void
+deflate_stream::
+copy_block(
+ char *buf, // the input data
+ unsigned len, // its length
+ int header) // true if block header must be written
+{
+ bi_windup(); // align on byte boundary
+
+ if(header)
+ {
+ put_short((std::uint16_t)len);
+ put_short((std::uint16_t)~len);
+ }
+ // VFALCO Use memcpy?
+ while (len--)
+ put_byte(*buf++);
+}
+
+//------------------------------------------------------------------------------
+
+/* Initialize the tree data structures for a new zlib stream.
+*/
+template<class>
+void
+deflate_stream::
+tr_init()
+{
+ l_desc_.dyn_tree = dyn_ltree_;
+ l_desc_.stat_desc = &lut_.l_desc;
+
+ d_desc_.dyn_tree = dyn_dtree_;
+ d_desc_.stat_desc = &lut_.d_desc;
+
+ bl_desc_.dyn_tree = bl_tree_;
+ bl_desc_.stat_desc = &lut_.bl_desc;
+
+ bi_buf_ = 0;
+ bi_valid_ = 0;
+
+ // Initialize the first block of the first file:
+ init_block();
+}
+
+/* Send one empty static block to give enough lookahead for inflate.
+ This takes 10 bits, of which 7 may remain in the bit buffer.
+*/
+template<class>
+void
+deflate_stream::
+tr_align()
+{
+ send_bits(STATIC_TREES<<1, 3);
+ send_code(END_BLOCK, lut_.ltree);
+ bi_flush();
+}
+
+/* Flush the bits in the bit buffer to pending output (leaves at most 7 bits)
+*/
+template<class>
+void
+deflate_stream::
+tr_flush_bits()
+{
+ bi_flush();
+}
+
+/* Send a stored block
+*/
+template<class>
+void
+deflate_stream::
+tr_stored_block(
+ char *buf, // input block
+ std::uint32_t stored_len, // length of input block
+ int last) // one if this is the last block for a file
+{
+ send_bits((STORED_BLOCK<<1)+last, 3); // send block type
+ copy_block(buf, (unsigned)stored_len, 1); // with header
+}
+
+template<class>
+inline
+void
+deflate_stream::
+tr_tally_dist(std::uint16_t dist, std::uint8_t len, bool& flush)
+{
+ d_buf_[last_lit_] = dist;
+ l_buf_[last_lit_++] = len;
+ dist--;
+ dyn_ltree_[lut_.length_code[len]+literals+1].fc++;
+ dyn_dtree_[d_code(dist)].fc++;
+ flush = (last_lit_ == lit_bufsize_-1);
+}
+
+template<class>
+inline
+void
+deflate_stream::
+tr_tally_lit(std::uint8_t c, bool& flush)
+{
+ d_buf_[last_lit_] = 0;
+ l_buf_[last_lit_++] = c;
+ dyn_ltree_[c].fc++;
+ flush = (last_lit_ == lit_bufsize_-1);
+}
+
+//------------------------------------------------------------------------------
+
+/* Determine the best encoding for the current block: dynamic trees,
+ static trees or store, and output the encoded block to the zip file.
+*/
+template<class>
+void
+deflate_stream::
+tr_flush_block(
+ z_params& zs,
+ char *buf, // input block, or NULL if too old
+ std::uint32_t stored_len, // length of input block
+ int last) // one if this is the last block for a file
+{
+ std::uint32_t opt_lenb;
+ std::uint32_t static_lenb; // opt_len and static_len in bytes
+ int max_blindex = 0; // index of last bit length code of non zero freq
+
+ // Build the Huffman trees unless a stored block is forced
+ if(level_ > 0)
+ {
+ // Check if the file is binary or text
+ if(zs.data_type == unknown)
+ zs.data_type = detect_data_type();
+
+ // Construct the literal and distance trees
+ build_tree((tree_desc *)(&(l_desc_)));
+
+ build_tree((tree_desc *)(&(d_desc_)));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree();
+
+ /* Determine the best encoding. Compute the block lengths in bytes. */
+ opt_lenb = (opt_len_+3+7)>>3;
+ static_lenb = (static_len_+3+7)>>3;
+
+ if(static_lenb <= opt_lenb)
+ opt_lenb = static_lenb;
+ }
+ else
+ {
+ // VFALCO This assertion fails even in the original ZLib,
+ // happens with strategy == Z_HUFFMAN_ONLY, see:
+ // https://github.com/madler/zlib/issues/172
+
+ #if 0
+ BOOST_ASSERT(buf);
+ #endif
+ opt_lenb = static_lenb = stored_len + 5; // force a stored block
+ }
+
+#ifdef FORCE_STORED
+ if(buf != (char*)0) { /* force stored block */
+#else
+ if(stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ tr_stored_block(buf, stored_len, last);
+
+#ifdef FORCE_STATIC
+ }
+ else if(static_lenb >= 0)
+ {
+ // force static trees
+#else
+ }
+ else if(strategy_ == Strategy::fixed || static_lenb == opt_lenb)
+ {
+#endif
+ send_bits((STATIC_TREES<<1)+last, 3);
+ compress_block(lut_.ltree, lut_.dtree);
+ }
+ else
+ {
+ send_bits((DYN_TREES<<1)+last, 3);
+ send_all_trees(l_desc_.max_code+1, d_desc_.max_code+1,
+ max_blindex+1);
+ compress_block((const ct_data *)dyn_ltree_,
+ (const ct_data *)dyn_dtree_);
+ }
+ /* The above check is made mod 2^32, for files larger than 512 MB
+ * and std::size_t implemented on 32 bits.
+ */
+ init_block();
+
+ if(last)
+ bi_windup();
+}
+
+template<class>
+void
+deflate_stream::
+fill_window(z_params& zs)
+{
+ unsigned n, m;
+ unsigned more; // Amount of free space at the end of the window.
+ std::uint16_t *p;
+ uInt wsize = w_size_;
+
+ do
+ {
+ more = (unsigned)(window_size_ -
+ (std::uint32_t)lookahead_ -(std::uint32_t)strstart_);
+
+ // VFALCO We don't support systems below 32-bit
+ #if 0
+ // Deal with !@#$% 64K limit:
+ if(sizeof(int) <= 2)
+ {
+ if(more == 0 && strstart_ == 0 && lookahead_ == 0)
+ {
+ more = wsize;
+ }
+ else if(more == (unsigned)(-1))
+ {
+ /* Very unlikely, but possible on 16 bit machine if
+ * strstart == 0 && lookahead == 1 (input done a byte at time)
+ */
+ more--;
+ }
+ }
+ #endif
+
+ /* If the window is almost full and there is insufficient lookahead,
+ move the upper half to the lower one to make room in the upper half.
+ */
+ if(strstart_ >= wsize+max_dist())
+ {
+ std::memcpy(window_, window_+wsize, (unsigned)wsize);
+ match_start_ -= wsize;
+ strstart_ -= wsize; // we now have strstart >= max_dist
+ block_start_ -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ n = hash_size_;
+ p = &head_[n];
+ do
+ {
+ m = *--p;
+ *p = (std::uint16_t)(m >= wsize ? m-wsize : 0);
+ }
+ while(--n);
+
+ n = wsize;
+ p = &prev_[n];
+ do
+ {
+ m = *--p;
+ *p = (std::uint16_t)(m >= wsize ? m-wsize : 0);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ its value will never be used.
+ */
+ }
+ while(--n);
+ more += wsize;
+ }
+ if(zs.avail_in == 0)
+ break;
+
+ /* If there was no sliding:
+ strstart <= WSIZE+max_dist-1 && lookahead <= kMinLookahead - 1 &&
+ more == window_size - lookahead - strstart
+ => more >= window_size - (kMinLookahead-1 + WSIZE + max_dist-1)
+ => more >= window_size - 2*WSIZE + 2
+ In the BIG_MEM or MMAP case (not yet supported),
+ window_size == input_size + kMinLookahead &&
+ strstart + lookahead_ <= input_size => more >= kMinLookahead.
+ Otherwise, window_size == 2*WSIZE so more >= 2.
+ If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ n = read_buf(zs, window_ + strstart_ + lookahead_, more);
+ lookahead_ += n;
+
+ // Initialize the hash value now that we have some input:
+ if(lookahead_ + insert_ >= minMatch)
+ {
+ uInt str = strstart_ - insert_;
+ ins_h_ = window_[str];
+ update_hash(ins_h_, window_[str + 1]);
+ while(insert_)
+ {
+ update_hash(ins_h_, window_[str + minMatch-1]);
+ prev_[str & w_mask_] = head_[ins_h_];
+ head_[ins_h_] = (std::uint16_t)str;
+ str++;
+ insert_--;
+ if(lookahead_ + insert_ < minMatch)
+ break;
+ }
+ }
+ /* If the whole input has less than minMatch bytes, ins_h is garbage,
+ but this is not important since only literal bytes will be emitted.
+ */
+ }
+ while(lookahead_ < kMinLookahead && zs.avail_in != 0);
+
+ /* If the kWinInit bytes after the end of the current data have never been
+ written, then zero those bytes in order to avoid memory check reports of
+ the use of uninitialized (or uninitialised as Julian writes) bytes by
+ the longest match routines. Update the high water mark for the next
+ time through here. kWinInit is set to maxMatch since the longest match
+ routines allow scanning to strstart + maxMatch, ignoring lookahead.
+ */
+ if(high_water_ < window_size_)
+ {
+ std::uint32_t curr = strstart_ + (std::uint32_t)(lookahead_);
+ std::uint32_t winit;
+
+ if(high_water_ < curr)
+ {
+ /* Previous high water mark below current data -- zero kWinInit
+ bytes or up to end of window, whichever is less.
+ */
+ winit = window_size_ - curr;
+ if(winit > kWinInit)
+ winit = kWinInit;
+ std::memset(window_ + curr, 0, (unsigned)winit);
+ high_water_ = curr + winit;
+ }
+ else if(high_water_ < (std::uint32_t)curr + kWinInit)
+ {
+ /* High water mark at or above current data, but below current data
+ plus kWinInit -- zero out to current data plus kWinInit, or up
+ to end of window, whichever is less.
+ */
+ winit = (std::uint32_t)curr + kWinInit - high_water_;
+ if(winit > window_size_ - high_water_)
+ winit = window_size_ - high_water_;
+ std::memset(window_ + high_water_, 0, (unsigned)winit);
+ high_water_ += winit;
+ }
+ }
+}
+
+/* Flush as much pending output as possible. All write() output goes
+ through this function so some applications may wish to modify it
+ to avoid allocating a large strm->next_out buffer and copying into it.
+ (See also read_buf()).
+*/
+template<class>
+void
+deflate_stream::
+flush_pending(z_params& zs)
+{
+ tr_flush_bits();
+ auto len = clamp(pending_, zs.avail_out);
+ if(len == 0)
+ return;
+
+ std::memcpy(zs.next_out, pending_out_, len);
+ zs.next_out =
+ static_cast<std::uint8_t*>(zs.next_out) + len;
+ pending_out_ += len;
+ zs.total_out += len;
+ zs.avail_out -= len;
+ pending_ -= len;
+ if(pending_ == 0)
+ pending_out_ = pending_buf_;
+}
+
+/* Flush the current block, with given end-of-file flag.
+ IN assertion: strstart is set to the end of the current match.
+*/
+template<class>
+inline
+void
+deflate_stream::
+flush_block(z_params& zs, bool last)
+{
+ tr_flush_block(zs,
+ (block_start_ >= 0L ?
+ (char *)&window_[(unsigned)block_start_] :
+ (char *)0),
+ (std::uint32_t)((long)strstart_ - block_start_),
+ last);
+ block_start_ = strstart_;
+ flush_pending(zs);
+}
+
+/* Read a new buffer from the current input stream, update the adler32
+ and total number of bytes read. All write() input goes through
+ this function so some applications may wish to modify it to avoid
+ allocating a large strm->next_in buffer and copying from it.
+ (See also flush_pending()).
+*/
+template<class>
+int
+deflate_stream::
+read_buf(z_params& zs, Byte *buf, unsigned size)
+{
+ auto len = clamp(zs.avail_in, size);
+ if(len == 0)
+ return 0;
+
+ zs.avail_in -= len;
+
+ std::memcpy(buf, zs.next_in, len);
+ zs.next_in = static_cast<
+ std::uint8_t const*>(zs.next_in) + len;
+ zs.total_in += len;
+ return (int)len;
+}
+
+/* Set match_start to the longest match starting at the given string and
+ return its length. Matches shorter or equal to prev_length are discarded,
+ in which case the result is equal to prev_length and match_start is
+ garbage.
+ IN assertions: cur_match is the head of the hash chain for the current
+ string (strstart) and its distance is <= max_dist, and prev_length >= 1
+ OUT assertion: the match length is not greater than s->lookahead_.
+
+ For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ match.S. The code will be functionally equivalent.
+*/
+template<class>
+uInt
+deflate_stream::
+longest_match(IPos cur_match)
+{
+ unsigned chain_length = max_chain_length_;/* max hash chain length */
+ Byte *scan = window_ + strstart_; /* current string */
+ Byte *match; /* matched string */
+ int len; /* length of current match */
+ int best_len = prev_length_; /* best match length so far */
+ int nice_match = nice_match_; /* stop if match long enough */
+ IPos limit = strstart_ > (IPos)max_dist() ?
+ strstart_ - (IPos)max_dist() : 0;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ std::uint16_t *prev = prev_;
+ uInt wmask = w_mask_;
+
+ Byte *strend = window_ + strstart_ + maxMatch;
+ Byte scan_end1 = scan[best_len-1];
+ Byte scan_end = scan[best_len];
+
+ /* The code is optimized for HASH_BITS >= 8 and maxMatch-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ BOOST_ASSERT(hash_bits_ >= 8 && maxMatch == 258);
+
+ /* Do not waste too much time if we already have a good match: */
+ if(prev_length_ >= good_match_) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if((uInt)nice_match > lookahead_)
+ nice_match = lookahead_;
+
+ BOOST_ASSERT((std::uint32_t)strstart_ <= window_size_-kMinLookahead);
+
+ do {
+ BOOST_ASSERT(cur_match < strstart_);
+ match = window_ + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2. Note that the checks below
+ * for insufficient lookahead only occur occasionally for performance
+ * reasons. Therefore uninitialized memory will be accessed, and
+ * conditional jumps will be made that depend on those values.
+ * However the length of the match is limited to the lookahead, so
+ * the output of deflate is not affected by the uninitialized values.
+ */
+ if( match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1])
+ continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ BOOST_ASSERT(*scan == *match);
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do
+ {
+ }
+ while( *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ BOOST_ASSERT(scan <= window_+(unsigned)(window_size_-1));
+
+ len = maxMatch - (int)(strend - scan);
+ scan = strend - maxMatch;
+
+ if(len > best_len) {
+ match_start_ = cur_match;
+ best_len = len;
+ if(len >= nice_match) break;
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+ }
+ }
+ while((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if((uInt)best_len <= lookahead_)
+ return (uInt)best_len;
+ return lookahead_;
+}
+
+//------------------------------------------------------------------------------
+
+/* Copy without compression as much as possible from the input stream, return
+ the current block state.
+ This function does not insert new strings in the dictionary since
+ uncompressible data is probably not useful. This function is used
+ only for the level=0 compression option.
+ NOTE: this function should be optimized to avoid extra copying from
+ window to pending_buf.
+*/
+template<class>
+inline
+auto
+deflate_stream::
+f_stored(z_params& zs, Flush flush) ->
+ block_state
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ std::uint32_t max_block_size = 0xffff;
+ std::uint32_t max_start;
+
+ if(max_block_size > pending_buf_size_ - 5) {
+ max_block_size = pending_buf_size_ - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for(;;) {
+ /* Fill the window as much as possible: */
+ if(lookahead_ <= 1) {
+
+ BOOST_ASSERT(strstart_ < w_size_+max_dist() ||
+ block_start_ >= (long)w_size_);
+
+ fill_window(zs);
+ if(lookahead_ == 0 && flush == Flush::none)
+ return need_more;
+
+ if(lookahead_ == 0) break; /* flush the current block */
+ }
+ BOOST_ASSERT(block_start_ >= 0L);
+
+ strstart_ += lookahead_;
+ lookahead_ = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = block_start_ + max_block_size;
+ if(strstart_ == 0 || (std::uint32_t)strstart_ >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ lookahead_ = (uInt)(strstart_ - max_start);
+ strstart_ = (uInt)max_start;
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if(strstart_ - (uInt)block_start_ >= max_dist()) {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ }
+ insert_ = 0;
+ if(flush == Flush::finish)
+ {
+ flush_block(zs, true);
+ if(zs.avail_out == 0)
+ return finish_started;
+ return finish_done;
+ }
+ if((long)strstart_ > block_start_)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ return block_done;
+}
+
+/* Compress as much as possible from the input stream, return the current
+ block state.
+ This function does not perform lazy evaluation of matches and inserts
+ new strings in the dictionary only for unmatched strings or for short
+ matches. It is used only for the fast compression options.
+*/
+template<class>
+inline
+auto
+deflate_stream::
+f_fast(z_params& zs, Flush flush) ->
+ block_state
+{
+ IPos hash_head; /* head of the hash chain */
+ bool bflush; /* set if current block must be flushed */
+
+ for(;;)
+ {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need maxMatch bytes
+ * for the next match, plus minMatch bytes to insert the
+ * string following the next match.
+ */
+ if(lookahead_ < kMinLookahead)
+ {
+ fill_window(zs);
+ if(lookahead_ < kMinLookahead && flush == Flush::none)
+ return need_more;
+ if(lookahead_ == 0)
+ break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ hash_head = 0;
+ if(lookahead_ >= minMatch) {
+ insert_string(hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < minMatch
+ */
+ if(hash_head != 0 && strstart_ - hash_head <= max_dist()) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ match_length_ = longest_match (hash_head);
+ /* longest_match() sets match_start */
+ }
+ if(match_length_ >= minMatch)
+ {
+ tr_tally_dist(static_cast<std::uint16_t>(strstart_ - match_start_),
+ static_cast<std::uint8_t>(match_length_ - minMatch), bflush);
+
+ lookahead_ -= match_length_;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+ if(match_length_ <= max_lazy_match_ &&
+ lookahead_ >= minMatch) {
+ match_length_--; /* string at strstart already in table */
+ do
+ {
+ strstart_++;
+ insert_string(hash_head);
+ /* strstart never exceeds WSIZE-maxMatch, so there are
+ * always minMatch bytes ahead.
+ */
+ }
+ while(--match_length_ != 0);
+ strstart_++;
+ }
+ else
+ {
+ strstart_ += match_length_;
+ match_length_ = 0;
+ ins_h_ = window_[strstart_];
+ update_hash(ins_h_, window_[strstart_+1]);
+ /* If lookahead < minMatch, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ }
+ else
+ {
+ /* No match, output a literal byte */
+ tr_tally_lit(window_[strstart_], bflush);
+ lookahead_--;
+ strstart_++;
+ }
+ if(bflush)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ }
+ insert_ = strstart_ < minMatch-1 ? strstart_ : minMatch-1;
+ if(flush == Flush::finish)
+ {
+ flush_block(zs, true);
+ if(zs.avail_out == 0)
+ return finish_started;
+ return finish_done;
+ }
+ if(last_lit_)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ return block_done;
+}
+
+/* Same as above, but achieves better compression. We use a lazy
+ evaluation for matches: a match is finally adopted only if there is
+ no better match at the next window position.
+*/
+template<class>
+inline
+auto
+deflate_stream::
+f_slow(z_params& zs, Flush flush) ->
+ block_state
+{
+ IPos hash_head; /* head of hash chain */
+ bool bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for(;;)
+ {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need maxMatch bytes
+ * for the next match, plus minMatch bytes to insert the
+ * string following the next match.
+ */
+ if(lookahead_ < kMinLookahead)
+ {
+ fill_window(zs);
+ if(lookahead_ < kMinLookahead && flush == Flush::none)
+ return need_more;
+ if(lookahead_ == 0)
+ break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ hash_head = 0;
+ if(lookahead_ >= minMatch)
+ insert_string(hash_head);
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ prev_length_ = match_length_, prev_match_ = match_start_;
+ match_length_ = minMatch-1;
+
+ if(hash_head != 0 && prev_length_ < max_lazy_match_ &&
+ strstart_ - hash_head <= max_dist())
+ {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ match_length_ = longest_match(hash_head);
+ /* longest_match() sets match_start */
+
+ if(match_length_ <= 5 && (strategy_ == Strategy::filtered
+ || (match_length_ == minMatch &&
+ strstart_ - match_start_ > kTooFar)
+ ))
+ {
+ /* If prev_match is also minMatch, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ match_length_ = minMatch-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if(prev_length_ >= minMatch && match_length_ <= prev_length_)
+ {
+ /* Do not insert strings in hash table beyond this. */
+ uInt max_insert = strstart_ + lookahead_ - minMatch;
+
+ tr_tally_dist(
+ static_cast<std::uint16_t>(strstart_ -1 - prev_match_),
+ static_cast<std::uint8_t>(prev_length_ - minMatch), bflush);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ lookahead_ -= prev_length_-1;
+ prev_length_ -= 2;
+ do {
+ if(++strstart_ <= max_insert)
+ insert_string(hash_head);
+ }
+ while(--prev_length_ != 0);
+ match_available_ = 0;
+ match_length_ = minMatch-1;
+ strstart_++;
+
+ if(bflush)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+
+ }
+ else if(match_available_)
+ {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ tr_tally_lit(window_[strstart_-1], bflush);
+ if(bflush)
+ flush_block(zs, false);
+ strstart_++;
+ lookahead_--;
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ else
+ {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ match_available_ = 1;
+ strstart_++;
+ lookahead_--;
+ }
+ }
+ BOOST_ASSERT(flush != Flush::none);
+ if(match_available_)
+ {
+ tr_tally_lit(window_[strstart_-1], bflush);
+ match_available_ = 0;
+ }
+ insert_ = strstart_ < minMatch-1 ? strstart_ : minMatch-1;
+ if(flush == Flush::finish)
+ {
+ flush_block(zs, true);
+ if(zs.avail_out == 0)
+ return finish_started;
+ return finish_done;
+ }
+ if(last_lit_)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ return block_done;
+}
+
+/* For Strategy::rle, simply look for runs of bytes, generate matches only of distance
+ one. Do not maintain a hash table. (It will be regenerated if this run of
+ deflate switches away from Strategy::rle.)
+*/
+template<class>
+inline
+auto
+deflate_stream::
+f_rle(z_params& zs, Flush flush) ->
+ block_state
+{
+ bool bflush; // set if current block must be flushed
+ uInt prev; // byte at distance one to match
+ Byte *scan, *strend; // scan goes up to strend for length of run
+
+ for(;;)
+ {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need maxMatch bytes
+ * for the longest run, plus one for the unrolled loop.
+ */
+ if(lookahead_ <= maxMatch) {
+ fill_window(zs);
+ if(lookahead_ <= maxMatch && flush == Flush::none) {
+ return need_more;
+ }
+ if(lookahead_ == 0) break; /* flush the current block */
+ }
+
+ /* See how many times the previous byte repeats */
+ match_length_ = 0;
+ if(lookahead_ >= minMatch && strstart_ > 0) {
+ scan = window_ + strstart_ - 1;
+ prev = *scan;
+ if(prev == *++scan && prev == *++scan && prev == *++scan) {
+ strend = window_ + strstart_ + maxMatch;
+ do {
+ } while(prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ prev == *++scan && prev == *++scan &&
+ scan < strend);
+ match_length_ = maxMatch - (int)(strend - scan);
+ if(match_length_ > lookahead_)
+ match_length_ = lookahead_;
+ }
+ BOOST_ASSERT(scan <= window_+(uInt)(window_size_-1));
+ }
+
+ /* Emit match if have run of minMatch or longer, else emit literal */
+ if(match_length_ >= minMatch) {
+ tr_tally_dist(std::uint16_t{1},
+ static_cast<std::uint8_t>(match_length_ - minMatch),
+ bflush);
+
+ lookahead_ -= match_length_;
+ strstart_ += match_length_;
+ match_length_ = 0;
+ } else {
+ /* No match, output a literal byte */
+ tr_tally_lit(window_[strstart_], bflush);
+ lookahead_--;
+ strstart_++;
+ }
+ if(bflush)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ }
+ insert_ = 0;
+ if(flush == Flush::finish)
+ {
+ flush_block(zs, true);
+ if(zs.avail_out == 0)
+ return finish_started;
+ return finish_done;
+ }
+ if(last_lit_)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ return block_done;
+}
+
+/* ===========================================================================
+ * For Strategy::huffman, do not look for matches. Do not maintain a hash table.
+ * (It will be regenerated if this run of deflate switches away from Huffman.)
+ */
+template<class>
+inline
+auto
+deflate_stream::
+f_huff(z_params& zs, Flush flush) ->
+ block_state
+{
+ bool bflush; // set if current block must be flushed
+
+ for(;;)
+ {
+ // Make sure that we have a literal to write.
+ if(lookahead_ == 0)
+ {
+ fill_window(zs);
+ if(lookahead_ == 0)
+ {
+ if(flush == Flush::none)
+ return need_more;
+ break; // flush the current block
+ }
+ }
+
+ // Output a literal byte
+ match_length_ = 0;
+ tr_tally_lit(window_[strstart_], bflush);
+ lookahead_--;
+ strstart_++;
+ if(bflush)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ }
+ insert_ = 0;
+ if(flush == Flush::finish)
+ {
+ flush_block(zs, true);
+ if(zs.avail_out == 0)
+ return finish_started;
+ return finish_done;
+ }
+ if(last_lit_)
+ {
+ flush_block(zs, false);
+ if(zs.avail_out == 0)
+ return need_more;
+ }
+ return block_done;
+}
+
+} // detail
+} // zlib
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/zlib/detail/inflate_stream.hpp b/boost/beast/zlib/detail/inflate_stream.hpp
new file mode 100644
index 0000000000..57be172387
--- /dev/null
+++ b/boost/beast/zlib/detail/inflate_stream.hpp
@@ -0,0 +1,1310 @@
+//
+// 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
+//
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef BOOST_BEAST_ZLIB_DETAIL_INFLATE_STREAM_HPP
+#define BOOST_BEAST_ZLIB_DETAIL_INFLATE_STREAM_HPP
+
+#include <boost/beast/zlib/error.hpp>
+#include <boost/beast/zlib/zlib.hpp>
+#include <boost/beast/zlib/detail/bitstream.hpp>
+#include <boost/beast/zlib/detail/ranges.hpp>
+#include <boost/beast/zlib/detail/window.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/throw_exception.hpp>
+#include <algorithm>
+#include <array>
+#include <cstdint>
+#include <cstring>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace zlib {
+namespace detail {
+
+class inflate_stream
+{
+protected:
+ inflate_stream()
+ {
+ w_.reset(15);
+ }
+
+ template<class = void> void doClear();
+ template<class = void> void doReset(int windowBits);
+ template<class = void> void doWrite(z_params& zs, Flush flush, error_code& ec);
+
+ void
+ doReset()
+ {
+ doReset(w_.bits());
+ }
+
+private:
+ enum Mode
+ {
+ HEAD, // i: waiting for magic header
+ FLAGS, // i: waiting for method and flags (gzip)
+ TIME, // i: waiting for modification time (gzip)
+ OS, // i: waiting for extra flags and operating system (gzip)
+ EXLEN, // i: waiting for extra length (gzip)
+ EXTRA, // i: waiting for extra bytes (gzip)
+ NAME, // i: waiting for end of file name (gzip)
+ COMMENT, // i: waiting for end of comment (gzip)
+ HCRC, // i: waiting for header crc (gzip)
+ TYPE, // i: waiting for type bits, including last-flag bit
+ TYPEDO, // i: same, but skip check to exit inflate on new block
+ STORED, // i: waiting for stored size (length and complement)
+ COPY_, // i/o: same as COPY below, but only first time in
+ COPY, // i/o: waiting for input or output to copy stored block
+ TABLE, // i: waiting for dynamic block table lengths
+ LENLENS, // i: waiting for code length code lengths
+ CODELENS, // i: waiting for length/lit and distance code lengths
+ LEN_, // i: same as LEN below, but only first time in
+ LEN, // i: waiting for length/lit/eob code
+ LENEXT, // i: waiting for length extra bits
+ DIST, // i: waiting for distance code
+ DISTEXT,// i: waiting for distance extra bits
+ MATCH, // o: waiting for output space to copy string
+ LIT, // o: waiting for output space to write literal
+ CHECK, // i: waiting for 32-bit check value
+ LENGTH, // i: waiting for 32-bit length (gzip)
+ DONE, // finished check, done -- remain here until reset
+ BAD, // got a data error -- remain here until reset
+ SYNC // looking for synchronization bytes to restart inflate()
+ };
+
+ /* Structure for decoding tables. Each entry provides either the
+ information needed to do the operation requested by the code that
+ indexed that table entry, or it provides a pointer to another
+ table that indexes more bits of the code. op indicates whether
+ the entry is a pointer to another table, a literal, a length or
+ distance, an end-of-block, or an invalid code. For a table
+ pointer, the low four bits of op is the number of index bits of
+ that table. For a length or distance, the low four bits of op
+ is the number of extra bits to get after the code. bits is
+ the number of bits in this code or part of the code to drop off
+ of the bit buffer. val is the actual byte to output in the case
+ of a literal, the base length or distance, or the offset from
+ the current table to the next table. Each entry is four bytes.
+
+ op values as set by inflate_table():
+
+ 00000000 - literal
+ 0000tttt - table link, tttt != 0 is the number of table index bits
+ 0001eeee - length or distance, eeee is the number of extra bits
+ 01100000 - end of block
+ 01000000 - invalid code
+ */
+ struct code
+ {
+ std::uint8_t op; // operation, extra bits, table bits
+ std::uint8_t bits; // bits in this part of the code
+ std::uint16_t val; // offset in table or code value
+ };
+
+ /* Maximum size of the dynamic table. The maximum number of code
+ structures is 1444, which is the sum of 852 for literal/length codes
+ and 592 for distance codes. These values were found by exhaustive
+ searches using the program examples/enough.c found in the zlib
+ distribtution. The arguments to that program are the number of
+ symbols, the initial root table size, and the maximum bit length
+ of a code. "enough 286 9 15" for literal/length codes returns
+ returns 852, and "enough 30 6 15" for distance codes returns 592.
+ The initial root table size (9 or 6) is found in the fifth argument
+ of the inflate_table() calls in inflate.c and infback.c. If the
+ root table size is changed, then these maximum sizes would be need
+ to be recalculated and updated.
+ */
+ static std::uint16_t constexpr kEnoughLens = 852;
+ static std::uint16_t constexpr kEnoughDists = 592;
+ static std::uint16_t constexpr kEnough = kEnoughLens + kEnoughDists;
+
+ struct codes
+ {
+ code const* lencode;
+ code const* distcode;
+ unsigned lenbits; // VFALCO use std::uint8_t
+ unsigned distbits;
+ };
+
+ // Type of code to build for inflate_table()
+ enum class build
+ {
+ codes,
+ lens,
+ dists
+ };
+
+ template<class = void>
+ static
+ void
+ inflate_table(
+ build type,
+ std::uint16_t* lens,
+ std::size_t codes,
+ code** table,
+ unsigned *bits,
+ std::uint16_t* work,
+ error_code& ec);
+
+ template<class = void>
+ static
+ codes const&
+ get_fixed_tables();
+
+ template<class = void>
+ void
+ fixedTables();
+
+ template<class = void>
+ void
+ inflate_fast(ranges& r, error_code& ec);
+
+ bitstream bi_;
+
+ Mode mode_ = HEAD; // current inflate mode
+ int last_ = 0; // true if processing last block
+ unsigned dmax_ = 32768U; // zlib header max distance (INFLATE_STRICT)
+
+ // sliding window
+ window w_;
+
+ // for string and stored block copying
+ unsigned length_; // literal or length of data to copy
+ unsigned offset_; // distance back to copy string from
+
+ // for table and code decoding
+ unsigned extra_; // extra bits needed
+
+ // dynamic table building
+ unsigned ncode_; // number of code length code lengths
+ unsigned nlen_; // number of length code lengths
+ unsigned ndist_; // number of distance code lengths
+ unsigned have_; // number of code lengths in lens[]
+ unsigned short lens_[320]; // temporary storage for code lengths
+ unsigned short work_[288]; // work area for code table building
+ code codes_[kEnough]; // space for code tables
+ code *next_ = codes_; // next available space in codes[]
+ int back_ = -1; // bits back of last unprocessed length/lit
+ unsigned was_; // initial length of match
+
+ // fixed and dynamic code tables
+ code const* lencode_ = codes_ ; // starting table for length/literal codes
+ code const* distcode_ = codes_; // starting table for distance codes
+ unsigned lenbits_; // index bits for lencode
+ unsigned distbits_; // index bits for distcode
+};
+
+//------------------------------------------------------------------------------
+
+template<class>
+void
+inflate_stream::
+doReset(int windowBits)
+{
+ if(windowBits < 8 || windowBits > 15)
+ BOOST_THROW_EXCEPTION(std::domain_error{
+ "windowBits out of range"});
+ w_.reset(windowBits);
+
+ bi_.flush();
+ mode_ = HEAD;
+ last_ = 0;
+ dmax_ = 32768U;
+ lencode_ = codes_;
+ distcode_ = codes_;
+ next_ = codes_;
+ back_ = -1;
+}
+
+template<class>
+void
+inflate_stream::
+doClear()
+{
+}
+
+template<class>
+void
+inflate_stream::
+doWrite(z_params& zs, Flush flush, error_code& ec)
+{
+ ranges r;
+ r.in.first = reinterpret_cast<
+ std::uint8_t const*>(zs.next_in);
+ r.in.last = r.in.first + zs.avail_in;
+ r.in.next = r.in.first;
+ r.out.first = reinterpret_cast<
+ std::uint8_t*>(zs.next_out);
+ r.out.last = r.out.first + zs.avail_out;
+ r.out.next = r.out.first;
+
+ auto const done =
+ [&]
+ {
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call updatewindow() to create and/or update the window state.
+ Note: a memory error from inflate() is non-recoverable.
+ */
+
+
+ // VFALCO TODO Don't allocate update the window unless necessary
+ if(/*wsize_ ||*/ (r.out.used() && mode_ < BAD &&
+ (mode_ < CHECK || flush != Flush::finish)))
+ w_.write(r.out.first, r.out.used());
+
+ zs.next_in = r.in.next;
+ zs.avail_in = r.in.avail();
+ zs.next_out = r.out.next;
+ zs.avail_out = r.out.avail();
+ zs.total_in += r.in.used();
+ zs.total_out += r.out.used();
+ zs.data_type = bi_.size() + (last_ ? 64 : 0) +
+ (mode_ == TYPE ? 128 : 0) +
+ (mode_ == LEN_ || mode_ == COPY_ ? 256 : 0);
+
+ if(((! r.in.used() && ! r.out.used()) ||
+ flush == Flush::finish) && ! ec)
+ ec = error::need_buffers;
+ };
+ auto const err =
+ [&](error e)
+ {
+ ec = e;
+ mode_ = BAD;
+ };
+
+ if(mode_ == TYPE)
+ mode_ = TYPEDO;
+
+ for(;;)
+ {
+ switch(mode_)
+ {
+ case HEAD:
+ mode_ = TYPEDO;
+ break;
+
+ case TYPE:
+ if(flush == Flush::block || flush == Flush::trees)
+ return done();
+ // fall through
+
+ case TYPEDO:
+ {
+ if(last_)
+ {
+ bi_.flush_byte();
+ mode_ = CHECK;
+ break;
+ }
+ if(! bi_.fill(3, r.in.next, r.in.last))
+ return done();
+ std::uint8_t v;
+ bi_.read(v, 1);
+ last_ = v != 0;
+ bi_.read(v, 2);
+ switch(v)
+ {
+ case 0:
+ // uncompressed block
+ mode_ = STORED;
+ break;
+ case 1:
+ // fixed Huffman table
+ fixedTables();
+ mode_ = LEN_; /* decode codes */
+ if(flush == Flush::trees)
+ return done();
+ break;
+ case 2:
+ // dynamic Huffman table
+ mode_ = TABLE;
+ break;
+
+ default:
+ return err(error::invalid_block_type);
+ }
+ break;
+ }
+
+ case STORED:
+ {
+ bi_.flush_byte();
+ std::uint32_t v;
+ if(! bi_.fill(32, r.in.next, r.in.last))
+ return done();
+ bi_.peek(v, 32);
+ length_ = v & 0xffff;
+ if(length_ != ((v >> 16) ^ 0xffff))
+ return err(error::invalid_stored_length);
+ // flush instead of read, otherwise
+ // undefined right shift behavior.
+ bi_.flush();
+ mode_ = COPY_;
+ if(flush == Flush::trees)
+ return done();
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case COPY_:
+ mode_ = COPY;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case COPY:
+ {
+ auto copy = length_;
+ if(copy == 0)
+ {
+ mode_ = TYPE;
+ break;
+ }
+ copy = clamp(copy, r.in.avail());
+ copy = clamp(copy, r.out.avail());
+ if(copy == 0)
+ return done();
+ std::memcpy(r.out.next, r.in.next, copy);
+ r.in.next += copy;
+ r.out.next += copy;
+ length_ -= copy;
+ break;
+ }
+
+ case TABLE:
+ if(! bi_.fill(5 + 5 + 4, r.in.next, r.in.last))
+ return done();
+ bi_.read(nlen_, 5);
+ nlen_ += 257;
+ bi_.read(ndist_, 5);
+ ndist_ += 1;
+ bi_.read(ncode_, 4);
+ ncode_ += 4;
+ if(nlen_ > 286 || ndist_ > 30)
+ return err(error::too_many_symbols);
+ have_ = 0;
+ mode_ = LENLENS;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case LENLENS:
+ {
+ static std::array<std::uint8_t, 19> constexpr order = {{
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}};
+ while(have_ < ncode_)
+ {
+ if(! bi_.fill(3, r.in.next, r.in.last))
+ return done();
+ bi_.read(lens_[order[have_]], 3);
+ ++have_;
+ }
+ while(have_ < order.size())
+ lens_[order[have_++]] = 0;
+
+ next_ = &codes_[0];
+ lencode_ = next_;
+ lenbits_ = 7;
+ inflate_table(build::codes, &lens_[0],
+ order.size(), &next_, &lenbits_, work_, ec);
+ if(ec)
+ {
+ mode_ = BAD;
+ break;
+ }
+ have_ = 0;
+ mode_ = CODELENS;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case CODELENS:
+ {
+ while(have_ < nlen_ + ndist_)
+ {
+ std::uint16_t v;
+ if(! bi_.fill(lenbits_, r.in.next, r.in.last))
+ return done();
+ bi_.peek(v, lenbits_);
+ auto cp = &lencode_[v];
+ if(cp->val < 16)
+ {
+ bi_.drop(cp->bits);
+ lens_[have_++] = cp->val;
+ }
+ else
+ {
+ std::uint16_t len;
+ std::uint16_t copy;
+ if(cp->val == 16)
+ {
+ if(! bi_.fill(cp->bits + 2, r.in.next, r.in.last))
+ return done();
+ bi_.drop(cp->bits);
+ if(have_ == 0)
+ return err(error::invalid_bit_length_repeat);
+ bi_.read(copy, 2);
+ len = lens_[have_ - 1];
+ copy += 3;
+
+ }
+ else if(cp->val == 17)
+ {
+ if(! bi_.fill(cp->bits + 3, r.in.next, r.in.last))
+ return done();
+ bi_.drop(cp->bits);
+ bi_.read(copy, 3);
+ len = 0;
+ copy += 3;
+ }
+ else
+ {
+ if(! bi_.fill(cp->bits + 7, r.in.next, r.in.last))
+ return done();
+ bi_.drop(cp->bits);
+ bi_.read(copy, 7);
+ len = 0;
+ copy += 11;
+ }
+ if(have_ + copy > nlen_ + ndist_)
+ return err(error::invalid_bit_length_repeat);
+ std::fill(&lens_[have_], &lens_[have_ + copy], len);
+ have_ += copy;
+ copy = 0;
+ }
+ }
+ // handle error breaks in while
+ if(mode_ == BAD)
+ break;
+ // check for end-of-block code (better have one)
+ if(lens_[256] == 0)
+ return err(error::missing_eob);
+ /* build code tables -- note: do not change the lenbits or distbits
+ values here (9 and 6) without reading the comments in inftrees.hpp
+ concerning the kEnough constants, which depend on those values */
+ next_ = &codes_[0];
+ lencode_ = next_;
+ lenbits_ = 9;
+ inflate_table(build::lens, &lens_[0],
+ nlen_, &next_, &lenbits_, work_, ec);
+ if(ec)
+ {
+ mode_ = BAD;
+ return;
+ }
+ distcode_ = next_;
+ distbits_ = 6;
+ inflate_table(build::dists, lens_ + nlen_,
+ ndist_, &next_, &distbits_, work_, ec);
+ if(ec)
+ {
+ mode_ = BAD;
+ return;
+ }
+ mode_ = LEN_;
+ if(flush == Flush::trees)
+ return done();
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case LEN_:
+ mode_ = LEN;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case LEN:
+ {
+ if(r.in.avail() >= 6 && r.out.avail() >= 258)
+ {
+ inflate_fast(r, ec);
+ if(ec)
+ {
+ mode_ = BAD;
+ return;
+ }
+ if(mode_ == TYPE)
+ back_ = -1;
+ break;
+ }
+ if(! bi_.fill(lenbits_, r.in.next, r.in.last))
+ return done();
+ std::uint16_t v;
+ back_ = 0;
+ bi_.peek(v, lenbits_);
+ auto cp = &lencode_[v];
+ if(cp->op && (cp->op & 0xf0) == 0)
+ {
+ auto prev = cp;
+ if(! bi_.fill(prev->bits + prev->op, r.in.next, r.in.last))
+ return done();
+ bi_.peek(v, prev->bits + prev->op);
+ cp = &lencode_[prev->val + (v >> prev->bits)];
+ bi_.drop(prev->bits + cp->bits);
+ back_ += prev->bits + cp->bits;
+ }
+ else
+ {
+ bi_.drop(cp->bits);
+ back_ += cp->bits;
+ }
+ length_ = cp->val;
+ if(cp->op == 0)
+ {
+ mode_ = LIT;
+ break;
+ }
+ if(cp->op & 32)
+ {
+ back_ = -1;
+ mode_ = TYPE;
+ break;
+ }
+ if(cp->op & 64)
+ return err(error::invalid_literal_length);
+ extra_ = cp->op & 15;
+ mode_ = LENEXT;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case LENEXT:
+ if(extra_)
+ {
+ if(! bi_.fill(extra_, r.in.next, r.in.last))
+ return done();
+ std::uint16_t v;
+ bi_.read(v, extra_);
+ length_ += v;
+ back_ += extra_;
+ }
+ was_ = length_;
+ mode_ = DIST;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case DIST:
+ {
+ if(! bi_.fill(distbits_, r.in.next, r.in.last))
+ return done();
+ std::uint16_t v;
+ bi_.peek(v, distbits_);
+ auto cp = &distcode_[v];
+ if((cp->op & 0xf0) == 0)
+ {
+ auto prev = cp;
+ if(! bi_.fill(prev->bits + prev->op, r.in.next, r.in.last))
+ return done();
+ bi_.peek(v, prev->bits + prev->op);
+ cp = &distcode_[prev->val + (v >> prev->bits)];
+ bi_.drop(prev->bits + cp->bits);
+ back_ += prev->bits + cp->bits;
+ }
+ else
+ {
+ bi_.drop(cp->bits);
+ back_ += cp->bits;
+ }
+ if(cp->op & 64)
+ return err(error::invalid_distance_code);
+ offset_ = cp->val;
+ extra_ = cp->op & 15;
+ mode_ = DISTEXT;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case DISTEXT:
+ if(extra_)
+ {
+ std::uint16_t v;
+ if(! bi_.fill(extra_, r.in.next, r.in.last))
+ return done();
+ bi_.read(v, extra_);
+ offset_ += v;
+ back_ += extra_;
+ }
+#ifdef INFLATE_STRICT
+ if(offset_ > dmax_)
+ return err(error::invalid_distance);
+#endif
+ mode_ = MATCH;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case MATCH:
+ {
+ if(! r.out.avail())
+ return done();
+ if(offset_ > r.out.used())
+ {
+ // copy from window
+ auto offset = static_cast<std::uint16_t>(
+ offset_ - r.out.used());
+ if(offset > w_.size())
+ return err(error::invalid_distance);
+ auto const n = clamp(clamp(
+ length_, offset), r.out.avail());
+ w_.read(r.out.next, offset, n);
+ r.out.next += n;
+ length_ -= n;
+ }
+ else
+ {
+ // copy from output
+ auto in = r.out.next - offset_;
+ auto n = clamp(length_, r.out.avail());
+ length_ -= n;
+ while(n--)
+ *r.out.next++ = *in++;
+ }
+ if(length_ == 0)
+ mode_ = LEN;
+ break;
+ }
+
+ case LIT:
+ {
+ if(! r.out.avail())
+ return done();
+ auto const v = static_cast<std::uint8_t>(length_);
+ *r.out.next++ = v;
+ mode_ = LEN;
+ break;
+ }
+
+ case CHECK:
+ mode_ = DONE;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case DONE:
+ ec = error::end_of_stream;
+ return done();
+
+ case BAD:
+ return done();
+
+ case SYNC:
+ default:
+ BOOST_THROW_EXCEPTION(std::logic_error{
+ "stream error"});
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+/* Build a set of tables to decode the provided canonical Huffman code.
+ The code lengths are lens[0..codes-1]. The result starts at *table,
+ whose indices are 0..2^bits-1. work is a writable array of at least
+ lens shorts, which is used as a work area. type is the type of code
+ to be generated, build::codes, build::lens, or build::dists. On
+ return, zero is success, -1 is an invalid code, and +1 means that
+ kEnough isn't enough. table on return points to the next available
+ entry's address. bits is the requested root table index bits, and
+ on return it is the actual root table index bits. It will differ if
+ the request is greater than the longest code or if it is less than
+ the shortest code.
+*/
+template<class>
+void
+inflate_stream::
+inflate_table(
+ build type,
+ std::uint16_t* lens,
+ std::size_t codes,
+ code** table,
+ unsigned *bits,
+ std::uint16_t* work,
+ error_code& ec)
+{
+ unsigned len; // a code's length in bits
+ unsigned sym; // index of code symbols
+ unsigned min, max; // minimum and maximum code lengths
+ unsigned root; // number of index bits for root table
+ unsigned curr; // number of index bits for current table
+ unsigned drop; // code bits to drop for sub-table
+ int left; // number of prefix codes available
+ unsigned used; // code entries in table used
+ unsigned huff; // Huffman code
+ unsigned incr; // for incrementing code, index
+ unsigned fill; // index for replicating entries
+ unsigned low; // low bits for current root entry
+ unsigned mask; // mask for low root bits
+ code here; // table entry for duplication
+ code *next; // next available space in table
+ std::uint16_t const* base; // base value table to use
+ std::uint16_t const* extra; // extra bits table to use
+ int end; // use base and extra for symbol > end
+ std::uint16_t count[15+1]; // number of codes of each length
+ std::uint16_t offs[15+1]; // offsets in table for each length
+
+ // Length codes 257..285 base
+ static std::uint16_t constexpr lbase[31] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+
+ // Length codes 257..285 extra
+ static std::uint16_t constexpr lext[31] = {
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78};
+
+ // Distance codes 0..29 base
+ static std::uint16_t constexpr dbase[32] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0};
+
+ // Distance codes 0..29 extra
+ static std::uint16_t constexpr dext[32] = {
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64};
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..15. The caller must assure this.
+ 1..15 is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..15) */
+ for (len = 0; len <= 15; len++)
+ count[len] = 0;
+ for (sym = 0; sym < codes; sym++)
+ count[lens[sym]]++;
+
+ /* bound code lengths, force root to be within code lengths */
+ root = *bits;
+ for (max = 15; max >= 1; max--)
+ if (count[max] != 0)
+ break;
+ if (root > max)
+ root = max;
+ if (max == 0)
+ { /* no symbols to code at all */
+ here.op = (std::uint8_t)64; /* invalid code marker */
+ here.bits = (std::uint8_t)1;
+ here.val = (std::uint16_t)0;
+ *(*table)++ = here; /* make a table to force an error */
+ *(*table)++ = here;
+ *bits = 1;
+ return; /* no symbols, but wait for decoding to report error */
+ }
+ for (min = 1; min < max; min++)
+ if (count[min] != 0)
+ break;
+ if (root < min)
+ root = min;
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= 15; len++)
+ {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0)
+ {
+ ec = error::over_subscribed_length;
+ return;
+ }
+ }
+ if (left > 0 && (type == build::codes || max != 1))
+ {
+ ec = error::incomplete_length_set;
+ return;
+ }
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < 15; len++)
+ offs[len + 1] = offs[len] + count[len];
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++)
+ if (lens[sym] != 0)
+ work[offs[lens[sym]]++] = (std::uint16_t)sym;
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked for build::lens and DIST tables against
+ the constants kEnoughLens and kEnoughDists to guard against changes in
+ the initial root table size constants. See the comments in inftrees.hpp
+ for more information.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ switch (type)
+ {
+ case build::codes:
+ base = extra = work; /* dummy value--not used */
+ end = 19;
+ break;
+ case build::lens:
+ base = lbase;
+ base -= 257;
+ extra = lext;
+ extra -= 257;
+ end = 256;
+ break;
+ default: /* build::dists */
+ base = dbase;
+ extra = dext;
+ end = -1;
+ }
+
+ /* initialize state for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = *table; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = (unsigned)(-1); /* trigger new sub-table when len > root */
+ used = 1U << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ auto const not_enough = []
+ {
+ BOOST_THROW_EXCEPTION(std::logic_error{
+ "insufficient output size when inflating tables"});
+ };
+
+ // check available table space
+ if ((type == build::lens && used > kEnoughLens) ||
+ (type == build::dists && used > kEnoughDists))
+ return not_enough();
+
+ /* process all codes and make table entries */
+ for (;;)
+ {
+ /* create table entry */
+ here.bits = (std::uint8_t)(len - drop);
+ if ((int)(work[sym]) < end)
+ {
+ here.op = (std::uint8_t)0;
+ here.val = work[sym];
+ }
+ else if ((int)(work[sym]) > end)
+ {
+ here.op = (std::uint8_t)(extra[work[sym]]);
+ here.val = base[work[sym]];
+ }
+ else
+ {
+ here.op = (std::uint8_t)(32 + 64); /* end of block */
+ here.val = 0;
+ }
+
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1U << (len - drop);
+ fill = 1U << curr;
+ min = fill; /* save offset to next table */
+ do
+ {
+ fill -= incr;
+ next[(huff >> drop) + fill] = here;
+ } while (fill != 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0)
+ {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--(count[len]) == 0)
+ {
+ if (len == max) break;
+ len = lens[work[sym]];
+ }
+
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) != low)
+ {
+ /* if first time, transition to sub-tables */
+ if (drop == 0)
+ drop = root;
+
+ /* increment past last table */
+ next += min; /* here min is 1 << curr */
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = (int)(1 << curr);
+ while (curr + drop < max)
+ {
+ left -= count[curr + drop];
+ if (left <= 0) break;
+ curr++;
+ left <<= 1;
+ }
+
+ /* check for enough space */
+ used += 1U << curr;
+ if ((type == build::lens && used > kEnoughLens) ||
+ (type == build::dists && used > kEnoughDists))
+ return not_enough();
+
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ (*table)[low].op = (std::uint8_t)curr;
+ (*table)[low].bits = (std::uint8_t)root;
+ (*table)[low].val = (std::uint16_t)(next - *table);
+ }
+ }
+
+ /* fill in remaining table entry if code is incomplete (guaranteed to have
+ at most one remaining entry, since if the code is incomplete, the
+ maximum code length that was allowed to get this far is one bit) */
+ if (huff != 0)
+ {
+ here.op = 64; // invalid code marker
+ here.bits = (std::uint8_t)(len - drop);
+ here.val = 0;
+ next[huff] = here;
+ }
+
+ *table += used;
+ *bits = root;
+}
+
+template<class>
+auto
+inflate_stream::
+get_fixed_tables() ->
+ codes const&
+{
+ struct fixed_codes : codes
+ {
+ code len_[512];
+ code dist_[32];
+
+ fixed_codes()
+ {
+ lencode = len_;
+ lenbits = 9;
+ distcode = dist_;
+ distbits = 5;
+
+ std::uint16_t lens[320];
+ std::uint16_t work[288];
+
+ std::fill(&lens[ 0], &lens[144], std::uint16_t{8});
+ std::fill(&lens[144], &lens[256], std::uint16_t{9});
+ std::fill(&lens[256], &lens[280], std::uint16_t{7});
+ std::fill(&lens[280], &lens[288], std::uint16_t{8});
+
+ {
+ error_code ec;
+ auto next = &len_[0];
+ inflate_table(build::lens,
+ lens, 288, &next, &lenbits, work, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(std::logic_error{ec.message()});
+ }
+
+ // VFALCO These fixups are from ZLib
+ len_[ 99].op = 64;
+ len_[227].op = 64;
+ len_[355].op = 64;
+ len_[483].op = 64;
+
+ {
+ error_code ec;
+ auto next = &dist_[0];
+ std::fill(&lens[0], &lens[32], std::uint16_t{5});
+ inflate_table(build::dists,
+ lens, 32, &next, &distbits, work, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(std::logic_error{ec.message()});
+ }
+ }
+ };
+
+ static fixed_codes const fc;
+ return fc;
+}
+
+template<class>
+void
+inflate_stream::
+fixedTables()
+{
+ auto const fc = get_fixed_tables();
+ lencode_ = fc.lencode;
+ lenbits_ = fc.lenbits;
+ distcode_ = fc.distcode;
+ distbits_ = fc.distbits;
+}
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state->mode_ == LEN
+ zs.avail_in >= 6
+ zs.avail_out >= 258
+ start >= zs.avail_out
+ state->bits_ < 8
+
+ On return, state->mode_ is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if zs.avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires zs.avail_out >= 258 for each loop to avoid checking for
+ output space.
+
+ inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+ - Using bit fields for code structure
+ - Different op definition to avoid & for extra bits (do & for table bits)
+ - Three separate decoding do-loops for direct, window, and wnext == 0
+ - Special case for distance > 1 copies to do overlapped load and store copy
+ - Explicit branch predictions (based on measured branch probabilities)
+ - Deferring match copy and interspersed it with decoding subsequent codes
+ - Swapping literal/length else
+ - Swapping window/direct else
+ - Larger unrolled copy loops (three is about right)
+ - Moving len -= 3 statement into middle of loop
+ */
+template<class>
+void
+inflate_stream::
+inflate_fast(ranges& r, error_code& ec)
+{
+ unsigned char const* last; // have enough input while in < last
+ unsigned char *end; // while out < end, enough space available
+ std::size_t op; // code bits, operation, extra bits, or window position, window bytes to copy
+ unsigned len; // match length, unused bytes
+ unsigned dist; // match distance
+ unsigned const lmask =
+ (1U << lenbits_) - 1; // mask for first level of length codes
+ unsigned const dmask =
+ (1U << distbits_) - 1; // mask for first level of distance codes
+
+ last = r.in.next + (r.in.avail() - 5);
+ end = r.out.next + (r.out.avail() - 257);
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
+ do
+ {
+ if(bi_.size() < 15)
+ bi_.fill_16(r.in.next);
+ auto cp = &lencode_[bi_.peek_fast() & lmask];
+ dolen:
+ bi_.drop(cp->bits);
+ op = (unsigned)(cp->op);
+ if(op == 0)
+ {
+ // literal
+ *r.out.next++ = (unsigned char)(cp->val);
+ }
+ else if(op & 16)
+ {
+ // length base
+ len = (unsigned)(cp->val);
+ op &= 15; // number of extra bits
+ if(op)
+ {
+ if(bi_.size() < op)
+ bi_.fill_8(r.in.next);
+ len += (unsigned)bi_.peek_fast() & ((1U << op) - 1);
+ bi_.drop(op);
+ }
+ if(bi_.size() < 15)
+ bi_.fill_16(r.in.next);
+ cp = &distcode_[bi_.peek_fast() & dmask];
+ dodist:
+ bi_.drop(cp->bits);
+ op = (unsigned)(cp->op);
+ if(op & 16)
+ {
+ // distance base
+ dist = (unsigned)(cp->val);
+ op &= 15; // number of extra bits
+ if(bi_.size() < op)
+ {
+ bi_.fill_8(r.in.next);
+ if(bi_.size() < op)
+ bi_.fill_8(r.in.next);
+ }
+ dist += (unsigned)bi_.peek_fast() & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+ if(dist > dmax_)
+ {
+ ec = error::invalid_distance;
+ mode_ = BAD;
+ break;
+ }
+#endif
+ bi_.drop(op);
+
+ op = r.out.used();
+ if(dist > op)
+ {
+ // copy from window
+ op = dist - op; // distance back in window
+ if(op > w_.size())
+ {
+ ec = error::invalid_distance;
+ mode_ = BAD;
+ break;
+ }
+ auto const n = clamp(len, op);
+ w_.read(r.out.next, op, n);
+ r.out.next += n;
+ len -= n;
+ }
+ if(len > 0)
+ {
+ // copy from output
+ auto in = r.out.next - dist;
+ auto n = clamp(len, r.out.avail());
+ len -= n;
+ while(n--)
+ *r.out.next++ = *in++;
+ }
+ }
+ else if((op & 64) == 0)
+ {
+ // 2nd level distance code
+ cp = &distcode_[cp->val + (bi_.peek_fast() & ((1U << op) - 1))];
+ goto dodist;
+ }
+ else
+ {
+ ec = error::invalid_distance_code;
+ mode_ = BAD;
+ break;
+ }
+ }
+ else if((op & 64) == 0)
+ {
+ // 2nd level length code
+ cp = &lencode_[cp->val + (bi_.peek_fast() & ((1U << op) - 1))];
+ goto dolen;
+ }
+ else if(op & 32)
+ {
+ // end-of-block
+ mode_ = TYPE;
+ break;
+ }
+ else
+ {
+ ec = error::invalid_literal_length;
+ mode_ = BAD;
+ break;
+ }
+ }
+ while(r.in.next < last && r.out.next < end);
+
+ // return unused bytes (on entry, bits < 8, so in won't go too far back)
+ bi_.rewind(r.in.next);
+}
+
+} // detail
+} // zlib
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/zlib/detail/ranges.hpp b/boost/beast/zlib/detail/ranges.hpp
new file mode 100644
index 0000000000..aee141f357
--- /dev/null
+++ b/boost/beast/zlib/detail/ranges.hpp
@@ -0,0 +1,104 @@
+//
+// 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
+//
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef BOOST_BEAST_ZLIB_DETAIL_RANGES_HPP
+#define BOOST_BEAST_ZLIB_DETAIL_RANGES_HPP
+
+#include <cstdint>
+#include <type_traits>
+
+namespace boost {
+namespace beast {
+namespace zlib {
+namespace detail {
+
+struct ranges
+{
+ template<bool isConst>
+ struct range
+ {
+ using iter_t =
+ typename std::conditional<isConst,
+ std::uint8_t const*,
+ std::uint8_t*>::type;
+
+ iter_t first;
+ iter_t last;
+ iter_t next;
+
+ // total bytes in range
+ std::size_t
+ size() const
+ {
+ return last - first;
+ }
+
+ // bytes consumed
+ std::size_t
+ used() const
+ {
+ return next - first;
+ }
+
+ // bytes remaining
+ std::size_t
+ avail() const
+ {
+ return last - next;
+ }
+ };
+
+ range<true> in;
+ range<false> out;
+};
+
+// Clamp u to v where u and v are different types
+template<class U, class V>
+inline
+U
+clamp(U u, V v)
+{
+ if(u > v)
+ u = static_cast<U>(v);
+ return u;
+}
+
+} // detail
+} // zlib
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/zlib/detail/window.hpp b/boost/beast/zlib/detail/window.hpp
new file mode 100644
index 0000000000..f089649183
--- /dev/null
+++ b/boost/beast/zlib/detail/window.hpp
@@ -0,0 +1,167 @@
+//
+// 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
+//
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef BOOST_BEAST_ZLIB_DETAIL_WINDOW_HPP
+#define BOOST_BEAST_ZLIB_DETAIL_WINDOW_HPP
+
+#include <boost/assert.hpp>
+#include <boost/make_unique.hpp>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+
+namespace boost {
+namespace beast {
+namespace zlib {
+namespace detail {
+
+class window
+{
+ std::unique_ptr<std::uint8_t[]> p_;
+ std::uint16_t i_ = 0;
+ std::uint16_t size_ = 0;
+ std::uint16_t capacity_ = 0;
+ std::uint8_t bits_ = 0;
+
+public:
+ int
+ bits() const
+ {
+ return bits_;
+ }
+
+ unsigned
+ capacity() const
+ {
+ return capacity_;
+ }
+
+ unsigned
+ size() const
+ {
+ return size_;
+ }
+
+ void
+ reset(int bits);
+
+ void
+ read(std::uint8_t* out, std::size_t pos, std::size_t n);
+
+ template<class = void>
+ void
+ write(std::uint8_t const* in, std::size_t n);
+};
+
+inline
+void
+window::
+reset(int bits)
+{
+ if(bits_ != bits)
+ {
+ p_.reset();
+ bits_ = static_cast<std::uint8_t>(bits);
+ capacity_ = 1U << bits_;
+ }
+ i_ = 0;
+ size_ = 0;
+}
+
+inline
+void
+window::
+read(std::uint8_t* out, std::size_t pos, std::size_t n)
+{
+ if(i_ >= size_)
+ {
+ // window is contiguous
+ std::memcpy(out, &p_[i_ - pos], n);
+ return;
+ }
+ auto i = ((i_ - pos) + capacity_) % capacity_;
+ auto m = capacity_ - i;
+ if(n <= m)
+ {
+ std::memcpy(out, &p_[i], n);
+ return;
+ }
+ std::memcpy(out, &p_[i], m);
+ out += m;
+ std::memcpy(out, &p_[0], n - m);
+}
+
+template<class>
+void
+window::
+write(std::uint8_t const* in, std::size_t n)
+{
+ if(! p_)
+ p_ = boost::make_unique<
+ std::uint8_t[]>(capacity_);
+ if(n >= capacity_)
+ {
+ i_ = 0;
+ size_ = capacity_;
+ std::memcpy(&p_[0], in + (n - size_), size_);
+ return;
+ }
+ if(i_ + n <= capacity_)
+ {
+ std::memcpy(&p_[i_], in, n);
+ if(size_ >= capacity_ - n)
+ size_ = capacity_;
+ else
+ size_ = static_cast<std::uint16_t>(size_ + n);
+
+ i_ = static_cast<std::uint16_t>(
+ (i_ + n) % capacity_);
+ return;
+ }
+ auto m = capacity_ - i_;
+ std::memcpy(&p_[i_], in, m);
+ in += m;
+ i_ = static_cast<std::uint16_t>(n - m);
+ std::memcpy(&p_[0], in, i_);
+ size_ = capacity_;
+}
+
+} // detail
+} // zlib
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/zlib/error.hpp b/boost/beast/zlib/error.hpp
new file mode 100644
index 0000000000..797c511ba1
--- /dev/null
+++ b/boost/beast/zlib/error.hpp
@@ -0,0 +1,139 @@
+//
+// 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_ZLIB_ERROR_HPP
+#define BOOST_BEAST_ZLIB_ERROR_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/core/error.hpp>
+
+namespace boost {
+namespace beast {
+namespace zlib {
+
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+/** Error codes returned by the codec.
+*/
+enum class error
+{
+ /** Additional buffers are required.
+
+ This error indicates that one or both of the buffers
+ provided buffers do not have sufficient available bytes
+ to make forward progress.
+
+ This does not always indicate a failure condition.
+
+ @note This is the same as `Z_BUF_ERROR` returned by ZLib.
+ */
+ need_buffers = 1,
+
+ /** End of stream reached.
+
+ @note This is the same as `Z_STREAM_END` returned by ZLib.
+ */
+ end_of_stream,
+
+ /** Invalid stream or parameters.
+
+ This error is returned when invalid parameters are passed,
+ or the operation being performed is not consistent with the
+ state of the stream. For example, attempting to write data
+ when the end of stream is already reached.
+
+ @note This is the same as `Z_STREAM_ERROR` returned by ZLib.
+ */
+ stream_error,
+
+ //
+ // Errors generated by basic_deflate_stream
+ //
+
+ //
+ // Errors generated by basic_inflate_stream
+ //
+
+ /// Invalid block type
+ invalid_block_type,
+
+ /// Invalid stored block length
+ invalid_stored_length,
+
+ /// Too many length or distance symbols
+ too_many_symbols,
+
+ /// Invalid code lengths
+ invalid_code_lenths,
+
+ /// Invalid bit length repeat
+ invalid_bit_length_repeat,
+
+ /// Missing end of block code
+ missing_eob,
+
+ /// Invalid literal/length code
+ invalid_literal_length,
+
+ /// Invalid distance code
+ invalid_distance_code,
+
+ /// Invalid distance too far back
+ invalid_distance,
+
+ //
+ // Errors generated by inflate_table
+ //
+
+ /// Over-subscribed length code
+ over_subscribed_length,
+
+ /// Incomplete length set
+ incomplete_length_set,
+
+
+
+ /// general error
+ general
+};
+
+} // zlib
+} // beast
+} // boost
+
+#include <boost/beast/zlib/impl/error.ipp>
+
+#endif
+
diff --git a/boost/beast/zlib/impl/error.ipp b/boost/beast/zlib/impl/error.ipp
new file mode 100644
index 0000000000..6aa3d22d47
--- /dev/null
+++ b/boost/beast/zlib/impl/error.ipp
@@ -0,0 +1,140 @@
+//
+// 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
+//
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef BOOST_BEAST_ZLIB_IMPL_ERROR_IPP
+#define BOOST_BEAST_ZLIB_IMPL_ERROR_IPP
+
+#include <boost/beast/core/error.hpp>
+#include <type_traits>
+
+namespace boost {
+
+namespace system {
+template<>
+struct is_error_code_enum<beast::zlib::error>
+{
+ static bool const value = true;
+};
+} // system
+
+namespace beast {
+namespace zlib {
+namespace detail {
+
+class zlib_error_category : public error_category
+{
+public:
+ const char*
+ name() const noexcept override
+ {
+ return "beast.zlib";
+ }
+
+ std::string
+ message(int ev) const override
+ {
+ switch(static_cast<error>(ev))
+ {
+ case error::need_buffers: return "need buffers";
+ case error::end_of_stream: return "unexpected end of deflate stream";
+ case error::stream_error: return "stream error";
+
+ case error::invalid_block_type: return "invalid block type";
+ case error::invalid_stored_length: return "invalid stored block length";
+ case error::too_many_symbols: return "too many symbols";
+ case error::invalid_code_lenths: return "invalid code lengths";
+ case error::invalid_bit_length_repeat: return "invalid bit length repeat";
+ case error::missing_eob: return "missing end of block code";
+ case error::invalid_literal_length: return "invalid literal/length code";
+ case error::invalid_distance_code: return "invalid distance code";
+ case error::invalid_distance: return "invalid distance";
+
+ case error::over_subscribed_length: return "over-subscribed length";
+ case error::incomplete_length_set: return "incomplete length set";
+
+ case error::general:
+ default:
+ return "beast.zlib error";
+ }
+ }
+
+ error_condition
+ default_error_condition(int ev) const noexcept override
+ {
+ return error_condition{ev, *this};
+ }
+
+ bool
+ equivalent(int ev,
+ error_condition const& condition
+ ) const noexcept override
+ {
+ return condition.value() == ev &&
+ &condition.category() == this;
+ }
+
+ bool
+ equivalent(error_code const& error, int ev) const noexcept override
+ {
+ return error.value() == ev &&
+ &error.category() == this;
+ }
+};
+
+inline
+error_category const&
+get_error_category()
+{
+ static zlib_error_category const cat{};
+ return cat;
+}
+
+} // detail
+
+inline
+error_code
+make_error_code(error ev)
+{
+ return error_code{
+ static_cast<std::underlying_type<error>::type>(ev),
+ detail::get_error_category()};
+}
+
+} // zlib
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/zlib/inflate_stream.hpp b/boost/beast/zlib/inflate_stream.hpp
new file mode 100644
index 0000000000..8410cb8bc2
--- /dev/null
+++ b/boost/beast/zlib/inflate_stream.hpp
@@ -0,0 +1,220 @@
+//
+// 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_ZLIB_INFLATE_STREAM_HPP
+#define BOOST_BEAST_ZLIB_INFLATE_STREAM_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/zlib/detail/inflate_stream.hpp>
+
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+namespace boost {
+namespace beast {
+namespace zlib {
+
+/** Raw deflate stream decompressor.
+
+ This implements a raw deflate stream decompressor. The deflate
+ protocol is a compression protocol described in
+ "DEFLATE Compressed Data Format Specification version 1.3"
+ located here: https://tools.ietf.org/html/rfc1951
+
+ The implementation is a refactored port to C++ of ZLib's "inflate".
+ A more detailed description of ZLib is at http://zlib.net/.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is memory mapped), or can be done
+ by repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output (providing
+ more output space) before each call.
+*/
+class inflate_stream
+ : private detail::inflate_stream
+{
+public:
+ /** Construct a raw deflate decompression stream.
+
+ The window size is set to the default of 15 bits.
+ */
+ inflate_stream() = default;
+
+ /** Reset the stream.
+
+ This puts the stream in a newly constructed state with
+ the previously specified window size, but without de-allocating
+ any dynamically created structures.
+ */
+ void
+ reset()
+ {
+ doReset();
+ }
+
+ /** Reset the stream.
+
+ This puts the stream in a newly constructed state with the
+ specified window size, but without de-allocating any dynamically
+ created structures.
+ */
+ void
+ reset(int windowBits)
+ {
+ doReset(windowBits);
+ }
+
+ /** Put the stream in a newly constructed state.
+
+ All dynamically allocated memory is de-allocated.
+ */
+ void
+ clear()
+ {
+ doClear();
+ }
+
+ /** Decompress input and produce output.
+
+ This function decompresses as much data as possible, and stops when
+ the input buffer becomes empty or the output buffer becomes full. It
+ may introduce some output latency (reading input without producing any
+ output) except when forced to flush.
+
+ One or both of the following actions are performed:
+
+ @li Decompress more input starting at `zs.next_in` and update `zs.next_in`
+ and `zs.avail_in` accordingly. If not all input can be processed (because
+ there is not enough room in the output buffer), `zs.next_in` is updated
+ and processing will resume at this point for the next call.
+
+ @li Provide more output starting at `zs.next_out` and update `zs.next_out`
+ and `zs.avail_out` accordingly. `write` provides as much output as
+ possible, until there is no more input data or no more space in the output
+ buffer (see below about the flush parameter).
+
+ Before the call, the application should ensure that at least one of the
+ actions is possible, by providing more input and/or consuming more output,
+ and updating the values in `zs` accordingly. The application can consume
+ the uncompressed output when it wants, for example when the output buffer
+ is full (`zs.avail_out == 0`), or after each call. If `write` returns no
+ error and with zero `zs.avail_out`, it must be called again after making
+ room in the output buffer because there might be more output pending.
+
+ The flush parameter may be `Flush::none`, `Flush::sync`, `Flush::finish`,
+ `Flush::block`, or `Flush::trees`. `Flush::sync` requests to flush as much
+ output as possible to the output buffer. `Flush::block` requests to stop if
+ and when it gets to the next deflate block boundary. When decoding the
+ zlib or gzip format, this will cause `write` to return immediately after
+ the header and before the first block. When doing a raw inflate, `write` will
+ go ahead and process the first block, and will return when it gets to the
+ end of that block, or when it runs out of data.
+
+ The `Flush::block` option assists in appending to or combining deflate
+ streams. Also to assist in this, on return `write` will set `zs.data_type`
+ to the number of unused bits in the last byte taken from `zs.next_in`, plus
+ 64 if `write` is currently decoding the last block in the deflate stream,
+ plus 128 if `write` returned immediately after decoding an end-of-block code
+ or decoding the complete header up to just before the first byte of the
+ deflate stream. The end-of-block will not be indicated until all of the
+ uncompressed data from that block has been written to `zs.next_out`. The
+ number of unused bits may in general be greater than seven, except when
+ bit 7 of `zs.data_type` is set, in which case the number of unused bits
+ will be less than eight. `zs.data_type` is set as noted here every time
+ `write` returns for all flush options, and so can be used to determine the
+ amount of currently consumed input in bits.
+
+ The `Flush::trees` option behaves as `Flush::block` does, but it also returns
+ when the end of each deflate block header is reached, before any actual data
+ in that block is decoded. This allows the caller to determine the length of
+ the deflate block header for later use in random access within a deflate block.
+ 256 is added to the value of `zs.data_type` when `write` returns immediately
+ after reaching the end of the deflate block header.
+
+ `write` should normally be called until it returns `error::end_of_stream` or
+ another error. However if all decompression is to be performed in a single
+ step (a single call of `write`), the parameter flush should be set to
+ `Flush::finish`. In this case all pending input is processed and all pending
+ output is flushed; `zs.avail_out` must be large enough to hold all of the
+ uncompressed data for the operation to complete. (The size of the uncompressed
+ data may have been saved by the compressor for this purpose.) The use of
+ `Flush::finish` is not required to perform an inflation in one step. However
+ it may be used to inform inflate that a faster approach can be used for the
+ single call. `Flush::finish` also informs inflate to not maintain a sliding
+ window if the stream completes, which reduces inflate's memory footprint.
+ If the stream does not complete, either because not all of the stream is
+ provided or not enough output space is provided, then a sliding window will be
+ allocated and `write` can be called again to continue the operation as if
+ `Flush::none` had been used.
+
+ In this implementation, `write` always flushes as much output as possible to
+ the output buffer, and always uses the faster approach on the first call. So
+ the effects of the flush parameter in this implementation are on the return value
+ of `write` as noted below, when `write` returns early when `Flush::block` or
+ `Flush::trees` is used, and when `write` avoids the allocation of memory for a
+ sliding window when `Flush::finsih` is used.
+
+ If a preset dictionary is needed after this call,
+ `write` sets `zs.adler` to the Adler-32 checksum of the dictionary chosen by
+ the compressor and returns `error::need_dictionary`; otherwise it sets
+ `zs.adler` to the Adler-32 checksum of all output produced so far (that is,
+ `zs.total_out bytes`) and returns no error, `error::end_of_stream`, or an
+ error code as described below. At the end of the stream, `write` checks that
+ its computed adler32 checksum is equal to that saved by the compressor and
+ returns `error::end_of_stream` only if the checksum is correct.
+
+ This function returns no error if some progress has been made (more input
+ processed or more output produced), `error::end_of_stream` if the end of the
+ compressed data has been reached and all uncompressed output has been produced,
+ `error::need_dictionary` if a preset dictionary is needed at this point,
+ `error::invalid_data` if the input data was corrupted (input stream not
+ conforming to the zlib format or incorrect check value), `error::stream_error`
+ if the stream structure was inconsistent (for example if `zs.next_in` or
+ `zs.next_out` was null), `error::need_buffers` if no progress is possible or
+ if there was not enough room in the output buffer when `Flush::finish` is
+ used. Note that `error::need_buffers` is not fatal, and `write` can be called
+ again with more input and more output space to continue decompressing.
+ */
+ void
+ write(z_params& zs, Flush flush, error_code& ec)
+ {
+ doWrite(zs, flush, ec);
+ }
+};
+
+} // zlib
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/zlib/zlib.hpp b/boost/beast/zlib/zlib.hpp
new file mode 100644
index 0000000000..b8dddcbfe8
--- /dev/null
+++ b/boost/beast/zlib/zlib.hpp
@@ -0,0 +1,184 @@
+//
+// 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_ZLIB_ZLIB_HPP
+#define BOOST_BEAST_ZLIB_ZLIB_HPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <cstdint>
+#include <cstdlib>
+
+// This is a derivative work based on Zlib, copyright below:
+/*
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+namespace boost {
+namespace beast {
+namespace zlib {
+
+#if !defined(__MACTYPES__)
+using Byte = unsigned char; // 8 bits
+#endif
+using uInt = unsigned int; // 16 bits or more
+
+/* Possible values of the data_type field (though see inflate()) */
+enum kind
+{
+ binary = 0,
+ text = 1,
+ unknown = 2
+};
+
+/** Deflate codec parameters.
+
+ Objects of this type are filled in by callers and provided to the
+ deflate codec to define the input and output areas for the next
+ compress or decompress operation.
+
+ The application must update next_in and avail_in when avail_in has dropped
+ to zero. It must update next_out and avail_out when avail_out has dropped
+ to zero. The application must initialize zalloc, zfree and opaque before
+ calling the init function. All other fields are set by the compression
+ library and must not be updated by the application.
+
+ The fields total_in and total_out can be used for statistics or progress
+ reports. After compression, total_in holds the total size of the
+ uncompressed data and may be saved for use in the decompressor (particularly
+ if the decompressor wants to decompress everything in a single step).
+*/
+struct z_params
+{
+ /** A pointer to the next input byte.
+
+ If there is no more input, this may be set to `nullptr`.
+ */
+ void const* next_in;
+
+ /** The number of bytes of input available at `next_in`.
+
+ If there is no more input, this should be set to zero.
+ */
+ std::size_t avail_in;
+
+ /** The total number of input bytes read so far.
+ */
+ std::size_t total_in = 0;
+
+ /** A pointer to the next output byte.
+ */
+ void* next_out;
+
+ /** The remaining bytes of space at `next_out`.
+ */
+ std::size_t avail_out;
+
+ /** The total number of bytes output so far.
+ */
+ std::size_t total_out = 0;
+
+ int data_type = unknown; // best guess about the data type: binary or text
+};
+
+/** Flush option.
+*/
+enum class Flush
+{
+ // order matters
+
+ none,
+ block,
+ partial,
+ sync,
+ full,
+ finish,
+ trees
+};
+
+/* compression levels */
+enum compression
+{
+ none = 0,
+ best_speed = 1,
+ best_size = 9,
+ default_size = -1
+};
+
+/** Compression strategy.
+
+ These are used when compressing streams.
+*/
+enum class Strategy
+{
+ /** Default strategy.
+
+ This is suitable for general purpose compression, and works
+ well in the majority of cases.
+ */
+ normal,
+
+ /** Filtered strategy.
+
+ This strategy should be used when the data be compressed
+ is produced by a filter or predictor.
+ */
+ filtered,
+
+ /** Huffman-only strategy.
+
+ This strategy only performs Huffman encoding, without doing
+ any string matching.
+ */
+ huffman,
+
+ /** Run Length Encoding strategy.
+
+ This strategy limits match distances to one, making it
+ equivalent to run length encoding. This can give better
+ performance for things like PNG image data.
+ */
+ rle,
+
+ /** Fixed table strategy.
+
+ This strategy prevents the use of dynamic Huffman codes,
+ allowing for a simpler decoder for special applications.
+ */
+ fixed
+};
+
+} // zlib
+} // beast
+} // boost
+
+#endif
+