diff options
Diffstat (limited to 'boost/beast')
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 + |