mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Squashed 'src/beast/' changes from 1b9a714..6d5547a
6d5547a Set version to 1.0.0-b34 6fab138 Fix and tidy up CMake build scripts: ccefa54 Set version to 1.0.0-b33 32afe41 Set internal state correctly when writing frames: fe3e20b Add write_frames unit test 578dcd0 Add decorator unit test aaa3733 Use fwrite return value in file_body df66165 Require Visual Studio 2015 Update 3 or later b8e5a21 Set version to 1.0.0-b32 ffb1758 Update CMake scripts for finding packages: b893749 Remove http Writer suspend and resume feature (API Change): 27864fb Add io_service completion invariants tests eba05a7 Set version to 1.0.0-b31 484bcef Fix badge markdown in README.md 5663bea Add missing dynabuf_readstream member 0d7a551 Tidy up build settings 0fd4030 Move the handler, don't copy it git-subtree-dir: src/beast git-subtree-split: 6d5547a32c50ec95832c4779311502555ab0ee1f
This commit is contained in:
27
include/beast/config.hpp
Normal file
27
include/beast/config.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CONFIG_HPP
|
||||
#define BEAST_CONFIG_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(_MSC_FULL_VER)
|
||||
#if _MSC_FULL_VER < 190024210
|
||||
static_assert(false,
|
||||
"This library requires Visual Studio 2015 Update 3 or later");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
34
include/beast/core.hpp
Normal file
34
include/beast/core.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_HPP
|
||||
#define BEAST_CORE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/buffers_adapter.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <beast/core/prepare_buffers.hpp>
|
||||
#include <beast/core/static_streambuf.hpp>
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/core/dynabuf_readstream.hpp>
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/core/write_dynabuf.hpp>
|
||||
|
||||
#endif
|
||||
88
include/beast/core/async_completion.hpp
Normal file
88
include/beast/core/async_completion.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_ASYNC_COMPLETION_HPP
|
||||
#define BEAST_ASYNC_COMPLETION_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Helper for customizing the return type of asynchronous initiation functions.
|
||||
|
||||
This class template is used to transform caller-provided completion
|
||||
handlers in calls to asynchronous initiation functions. The transformation
|
||||
allows customization of the return type of the initiating function, and the
|
||||
function signature of the final handler.
|
||||
|
||||
@tparam CompletionHandler A completion handler, or a user defined type
|
||||
with specializations for customizing the return type (for example,
|
||||
`boost::asio::use_future` or `boost::asio::yield_context`).
|
||||
|
||||
@tparam Signature The callable signature of the final completion handler.
|
||||
|
||||
Example:
|
||||
@code
|
||||
...
|
||||
template<class CompletionHandler>
|
||||
typename async_completion<CompletionHandler,
|
||||
void(error_code)>::result_type
|
||||
async_initfn(..., CompletionHandler&& handler)
|
||||
{
|
||||
async_completion<CompletionHandler,
|
||||
void(error_code)> completion{handler};
|
||||
...
|
||||
return completion.result.get();
|
||||
}
|
||||
@endcode
|
||||
|
||||
@note See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
|
||||
Library Foundations For Asynchronous Operations</a>
|
||||
*/
|
||||
template<class CompletionHandler, class Signature>
|
||||
struct async_completion
|
||||
{
|
||||
/** The type of the final handler called by the asynchronous initiation function.
|
||||
|
||||
Objects of this type will be callable with the specified signature.
|
||||
*/
|
||||
using handler_type =
|
||||
typename boost::asio::handler_type<
|
||||
CompletionHandler, Signature>::type;
|
||||
|
||||
/// The type of the value returned by the asynchronous initiation function.
|
||||
using result_type = typename
|
||||
boost::asio::async_result<handler_type>::type;
|
||||
|
||||
/** Construct the helper.
|
||||
|
||||
@param token The completion handler. Copies will be made as
|
||||
required. If `CompletionHandler` is movable, it may also be moved.
|
||||
*/
|
||||
async_completion(typename std::remove_reference<CompletionHandler>::type& token)
|
||||
: handler(std::forward<CompletionHandler>(token))
|
||||
, result(handler)
|
||||
{
|
||||
static_assert(is_CompletionHandler<handler_type, Signature>::value,
|
||||
"Handler requirements not met");
|
||||
}
|
||||
|
||||
/// The final completion handler, callable with the specified signature.
|
||||
handler_type handler;
|
||||
|
||||
/// The return value of the asynchronous initiation function.
|
||||
boost::asio::async_result<handler_type> result;
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
71
include/beast/core/bind_handler.hpp
Normal file
71
include/beast/core/bind_handler.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BIND_HANDLER_HPP
|
||||
#define BEAST_BIND_HANDLER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/detail/bind_handler.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Bind parameters to a completion handler, creating a new handler.
|
||||
|
||||
This function creates a new handler which, when invoked with no
|
||||
parameters, calls the original handler with the list of bound
|
||||
arguments. The passed handler and arguments are forwarded into
|
||||
the returned handler, which provides the same `io_service`
|
||||
execution guarantees as the original handler.
|
||||
|
||||
Unlike `io_service::wrap`, the returned handler can be used in
|
||||
a subsequent call to `io_service::post` instead of
|
||||
`io_service::dispatch`, to ensure that the handler will not be
|
||||
invoked immediately by the calling function.
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
|
||||
template<class AsyncReadStream, class ReadHandler>
|
||||
void
|
||||
do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
|
||||
{
|
||||
stream.get_io_service().post(
|
||||
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 GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
detail::bound_handler<
|
||||
typename std::decay<Handler>::type, Args...>
|
||||
#endif
|
||||
bind_handler(Handler&& handler, Args&&... args)
|
||||
{
|
||||
static_assert(is_CompletionHandler<
|
||||
Handler, void(Args...)>::value,
|
||||
"Handler requirements not met");
|
||||
return detail::bound_handler<typename std::decay<
|
||||
Handler>::type, Args...>(std::forward<
|
||||
Handler>(handler), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
59
include/beast/core/buffer_cat.hpp
Normal file
59
include/beast/core/buffer_cat.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BUFFER_CAT_HPP
|
||||
#define BEAST_BUFFER_CAT_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/buffer_cat.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** 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, else the returned buffer sequence
|
||||
will be a @b ConstBufferSequence.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... BufferSequence>
|
||||
implementation_defined
|
||||
buffer_cat(BufferSequence const&... buffers)
|
||||
#else
|
||||
template<class B1, class B2, class... Bn>
|
||||
detail::buffer_cat_helper<B1, B2, Bn...>
|
||||
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
|
||||
#endif
|
||||
{
|
||||
static_assert(
|
||||
detail::is_all_ConstBufferSequence<B1, B2, Bn...>::value,
|
||||
"BufferSequence requirements not met");
|
||||
return detail::buffer_cat_helper<
|
||||
B1, B2, Bn...>{b1, b2, bn...};
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
62
include/beast/core/buffer_concepts.hpp
Normal file
62
include/beast/core/buffer_concepts.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BUFFER_CONCEPTS_HPP
|
||||
#define BEAST_BUFFER_CONCEPTS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `BufferSequence`.
|
||||
template<class T, class BufferType>
|
||||
#if GENERATING_DOCS
|
||||
struct is_BufferSequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_BufferSequence : detail::is_BufferSequence<T, BufferType>::type
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `ConstBufferSequence`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_ConstBufferSequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_ConstBufferSequence :
|
||||
is_BufferSequence<T, boost::asio::const_buffer>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `DynamicBuffer`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_DynamicBuffer : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_DynamicBuffer : detail::is_DynamicBuffer<T>::type
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `MutableBufferSequence`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_MutableBufferSequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_MutableBufferSequence :
|
||||
is_BufferSequence<T, boost::asio::mutable_buffer>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
151
include/beast/core/buffers_adapter.hpp
Normal file
151
include/beast/core/buffers_adapter.hpp
Normal file
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BUFFERS_ADAPTER_HPP
|
||||
#define BEAST_BUFFERS_ADAPTER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
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(is_MutableBufferSequence<MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
|
||||
using iter_type = typename MutableBufferSequence::const_iterator;
|
||||
|
||||
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 GENERATING_DOCS
|
||||
/// 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_;
|
||||
}
|
||||
|
||||
/** 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
|
||||
|
||||
#include <beast/core/impl/buffers_adapter.ipp>
|
||||
|
||||
#endif
|
||||
124
include/beast/core/consuming_buffers.hpp
Normal file
124
include/beast/core/consuming_buffers.hpp
Normal file
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CONSUMING_BUFFERS_HPP
|
||||
#define BEAST_CONSUMING_BUFFERS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
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.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
class consuming_buffers
|
||||
{
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
BufferSequence bs_;
|
||||
iter_type begin_;
|
||||
iter_type end_;
|
||||
std::size_t skip_ = 0;
|
||||
|
||||
template<class Deduced>
|
||||
consuming_buffers(Deduced&& other, std::size_t nbegin)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, begin_(std::next(bs_.begin(), nbegin))
|
||||
, 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 GENERATING_DOCS
|
||||
using value_type = ...;
|
||||
#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 GENERATING_DOCS
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_iterator;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
consuming_buffers(consuming_buffers&&);
|
||||
|
||||
/// Copy constructor.
|
||||
consuming_buffers(consuming_buffers const&);
|
||||
|
||||
/// Move assignment.
|
||||
consuming_buffers& operator=(consuming_buffers&&);
|
||||
|
||||
/// Copy assignment.
|
||||
consuming_buffers& operator=(consuming_buffers const&);
|
||||
|
||||
/** Construct to represent a buffer sequence.
|
||||
|
||||
A copy of the buffer sequence is made. Ownership of the
|
||||
underlying memory is not transferred or copied.
|
||||
*/
|
||||
explicit
|
||||
consuming_buffers(BufferSequence const& buffers);
|
||||
|
||||
/// 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 n 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 n);
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/consuming_buffers.ipp>
|
||||
|
||||
#endif
|
||||
178
include/beast/core/detail/base64.hpp
Normal file
178
include/beast/core/detail/base64.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_BASE64_HPP
|
||||
#define BEAST_DETAIL_BASE64_HPP
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
|
||||
Copyright notice:
|
||||
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 Ren<65> 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<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
*/
|
||||
|
||||
template<class = void>
|
||||
std::string const&
|
||||
base64_alphabet()
|
||||
{
|
||||
static std::string const alphabet =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
return alphabet;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
is_base64(unsigned char c)
|
||||
{
|
||||
return (std::isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
base64_encode (std::uint8_t const* data,
|
||||
std::size_t in_len)
|
||||
{
|
||||
unsigned char c3[3], c4[4];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
std::string ret;
|
||||
ret.reserve (3 + in_len * 8 / 6);
|
||||
|
||||
char const* alphabet (base64_alphabet().data());
|
||||
|
||||
while(in_len--)
|
||||
{
|
||||
c3[i++] = *(data++);
|
||||
if(i == 3)
|
||||
{
|
||||
c4[0] = (c3[0] & 0xfc) >> 2;
|
||||
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
|
||||
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
|
||||
c4[3] = c3[2] & 0x3f;
|
||||
for(i = 0; (i < 4); i++)
|
||||
ret += alphabet[c4[i]];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
c3[j] = '\0';
|
||||
|
||||
c4[0] = (c3[0] & 0xfc) >> 2;
|
||||
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
|
||||
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
|
||||
c4[3] = c3[2] & 0x3f;
|
||||
|
||||
for(j = 0; (j < i + 1); j++)
|
||||
ret += alphabet[c4[j]];
|
||||
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
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)
|
||||
{
|
||||
auto in_len = data.size();
|
||||
unsigned char c3[3], c4[4];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
|
||||
std::string ret;
|
||||
ret.reserve (in_len * 6 / 8); // ???
|
||||
|
||||
while(in_len-- && (data[in_] != '=') &&
|
||||
is_base64(data[in_]))
|
||||
{
|
||||
c4[i++] = data[in_]; in_++;
|
||||
if(i == 4) {
|
||||
for(i = 0; i < 4; i++)
|
||||
c4[i] = static_cast<unsigned char>(
|
||||
base64_alphabet().find(c4[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(i = 0; (i < 3); i++)
|
||||
ret += c3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
for(j = i; j < 4; j++)
|
||||
c4[j] = 0;
|
||||
|
||||
for(j = 0; j < 4; j++)
|
||||
c4[j] = static_cast<unsigned char>(
|
||||
base64_alphabet().find(c4[j]));
|
||||
|
||||
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++)
|
||||
ret += c3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
114
include/beast/core/detail/bind_handler.hpp
Normal file
114
include/beast/core/detail/bind_handler.hpp
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BIND_DETAIL_HANDLER_HPP
|
||||
#define BEAST_BIND_DETAIL_HANDLER_HPP
|
||||
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/detail/integer_sequence.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/* Nullary handler that calls Handler with bound arguments.
|
||||
|
||||
The bound handler provides the same io_service execution
|
||||
guarantees as the original handler.
|
||||
*/
|
||||
template<class Handler, class... Args>
|
||||
class bound_handler
|
||||
{
|
||||
private:
|
||||
using args_type = std::tuple<
|
||||
typename std::decay<Args>::type...>;
|
||||
|
||||
Handler h_;
|
||||
args_type args_;
|
||||
|
||||
template<class Tuple, std::size_t... S>
|
||||
static void invoke(Handler& h, Tuple& args,
|
||||
index_sequence<S...>)
|
||||
{
|
||||
h(std::get<S>(args)...);
|
||||
}
|
||||
|
||||
public:
|
||||
using result_type = void;
|
||||
|
||||
template<class DeducedHandler>
|
||||
explicit
|
||||
bound_handler(
|
||||
DeducedHandler&& handler, Args&&... args)
|
||||
: h_(std::forward<DeducedHandler>(handler))
|
||||
, args_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
invoke(h_, args_,
|
||||
index_sequence_for<Args...>());
|
||||
}
|
||||
|
||||
void
|
||||
operator()() const
|
||||
{
|
||||
invoke(h_, args_,
|
||||
index_sequence_for<Args...>());
|
||||
}
|
||||
|
||||
friend
|
||||
void*
|
||||
asio_handler_allocate(
|
||||
std::size_t size, bound_handler* h)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, h->h_);
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size, bound_handler* h)
|
||||
{
|
||||
beast_asio_helpers::
|
||||
deallocate(p, size, h->h_);
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
asio_handler_is_continuation(bound_handler* h)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
is_continuation (h->h_);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
friend
|
||||
void
|
||||
asio_handler_invoke(F&& f, bound_handler* h)
|
||||
{
|
||||
beast_asio_helpers::
|
||||
invoke(f, h->h_);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace std {
|
||||
template<class Handler, class... Args>
|
||||
void
|
||||
bind(beast::detail::bound_handler<
|
||||
Handler, Args...>, ...) = delete;
|
||||
} // std
|
||||
|
||||
#endif
|
||||
483
include/beast/core/detail/buffer_cat.hpp
Normal file
483
include/beast/core/detail/buffer_cat.hpp
Normal file
@@ -0,0 +1,483 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
|
||||
#define BEAST_DETAIL_BUFFER_CAT_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
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... Bn>
|
||||
class buffer_cat_helper
|
||||
{
|
||||
std::tuple<Bn...> bn_;
|
||||
|
||||
public:
|
||||
using value_type = typename
|
||||
common_buffers_type<Bn...>::type;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
buffer_cat_helper(buffer_cat_helper&&) = default;
|
||||
buffer_cat_helper(buffer_cat_helper const&) = default;
|
||||
buffer_cat_helper& operator=(buffer_cat_helper&&) = delete;
|
||||
buffer_cat_helper& operator=(buffer_cat_helper const&) = delete;
|
||||
|
||||
explicit
|
||||
buffer_cat_helper(Bn const&... bn)
|
||||
: bn_(bn...)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
template<class... Bn>
|
||||
class buffer_cat_helper<Bn...>::const_iterator
|
||||
{
|
||||
std::size_t n_;
|
||||
std::tuple<Bn...> const* bn_;
|
||||
std::array<std::uint8_t,
|
||||
max_sizeof<typename Bn::const_iterator...>()> buf_;
|
||||
|
||||
friend class buffer_cat_helper<Bn...>;
|
||||
|
||||
template<std::size_t I>
|
||||
using C = std::integral_constant<std::size_t, I>;
|
||||
|
||||
template<std::size_t I>
|
||||
using iter_t = typename std::tuple_element<
|
||||
I, std::tuple<Bn...>>::type::const_iterator;
|
||||
|
||||
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
|
||||
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)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--();
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
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(std::get<I>(*bn_).begin() !=
|
||||
std::get<I>(*bn_).end())
|
||||
{
|
||||
n_ = I;
|
||||
new(buf_.data()) iter_t<I>{
|
||||
std::get<I>(*bn_).begin()};
|
||||
return;
|
||||
}
|
||||
construct(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_.data()) 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_.data()) 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
|
||||
{
|
||||
throw detail::make_exception<std::logic_error>(
|
||||
"invalid iterator", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
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&)
|
||||
{
|
||||
throw detail::make_exception<std::logic_error>(
|
||||
"invalid iterator", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
increment(C<I> const&)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
if(++iter<I>() !=
|
||||
std::get<I>(*bn_).end())
|
||||
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)
|
||||
{
|
||||
--n_;
|
||||
new(buf_.data()) iter_t<I-1>{
|
||||
std::get<I-1>(*bn_).end()};
|
||||
}
|
||||
decrement(C<I-1>{});
|
||||
}
|
||||
|
||||
void
|
||||
decrement(C<0> const&)
|
||||
{
|
||||
auto constexpr I = 0;
|
||||
if(iter<I>() != std::get<I>(*bn_).begin())
|
||||
{
|
||||
--iter<I>();
|
||||
return;
|
||||
}
|
||||
throw detail::make_exception<std::logic_error>(
|
||||
"invalid iterator", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
decrement(C<I> const&)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
if(iter<I>() != std::get<I>(*bn_).begin())
|
||||
{
|
||||
--iter<I>();
|
||||
return;
|
||||
}
|
||||
--n_;
|
||||
using Iter = iter_t<I>;
|
||||
iter<I>().~Iter();
|
||||
new(buf_.data()) iter_t<I-1>{
|
||||
std::get<I-1>(*bn_).end()};
|
||||
}
|
||||
decrement(C<I-1>{});
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::~const_iterator()
|
||||
{
|
||||
destroy(C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::const_iterator()
|
||||
: n_(sizeof...(Bn))
|
||||
, bn_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<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>
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::const_iterator(const_iterator&& other)
|
||||
: n_(other.n_)
|
||||
, bn_(other.bn_)
|
||||
{
|
||||
move(std::move(other), C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::const_iterator(const_iterator const& other)
|
||||
: n_(other.n_)
|
||||
, bn_(other.bn_)
|
||||
{
|
||||
copy(other, C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::operator=(const_iterator&& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
destroy(C<0>{});
|
||||
n_ = other.n_;
|
||||
bn_ = other.bn_;
|
||||
move(std::move(other), C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::operator=(const_iterator const& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
destroy(C<0>{});
|
||||
n_ = other.n_;
|
||||
bn_ = other.bn_;
|
||||
copy(other, C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
bool
|
||||
buffer_cat_helper<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
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::operator*() const ->
|
||||
reference
|
||||
{
|
||||
return dereference(C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::operator++() ->
|
||||
const_iterator&
|
||||
{
|
||||
increment(C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
const_iterator::operator--() ->
|
||||
const_iterator&
|
||||
{
|
||||
decrement(C<sizeof...(Bn)>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
inline
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{bn_, false};
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
inline
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{bn_, true};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
180
include/beast/core/detail/buffer_concepts.hpp
Normal file
180
include/beast/core/detail/buffer_concepts.hpp
Normal file
@@ -0,0 +1,180 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_BUFFER_CONCEPTS_HPP
|
||||
#define BEAST_DETAIL_BUFFER_CONCEPTS_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// 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 T, class BufferType>
|
||||
class is_BufferSequence
|
||||
{
|
||||
template<class U, class R = std::is_convertible<
|
||||
typename U::value_type, BufferType> >
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_base_of<
|
||||
#if 0
|
||||
std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename U::const_iterator>::iterator_category>>
|
||||
#else
|
||||
// workaround:
|
||||
// boost::asio::detail::consuming_buffers::const_iterator
|
||||
// is not bidirectional
|
||||
std::forward_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename U::const_iterator>::iterator_category>>
|
||||
#endif
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
template<class U, class R = typename
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().begin()),
|
||||
typename U::const_iterator>::type>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
template<class U, class R = typename std::is_convertible<decltype(
|
||||
std::declval<U>().end()),
|
||||
typename U::const_iterator>::type>
|
||||
static R check4(int);
|
||||
template<class>
|
||||
static std::false_type check4(...);
|
||||
using type4 = decltype(check4<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
std::is_copy_constructible<T>::value &&
|
||||
std::is_destructible<T>::value &&
|
||||
type1::value && type2::value &&
|
||||
type3::value && type4::value>;
|
||||
};
|
||||
|
||||
template<class B1, class... Bn>
|
||||
struct is_all_ConstBufferSequence
|
||||
: std::integral_constant<bool,
|
||||
is_BufferSequence<B1, boost::asio::const_buffer>::type::value &&
|
||||
is_all_ConstBufferSequence<Bn...>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template<class B1>
|
||||
struct is_all_ConstBufferSequence<B1>
|
||||
: is_BufferSequence<B1, boost::asio::const_buffer>::type
|
||||
{
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_DynamicBuffer
|
||||
{
|
||||
// size()
|
||||
template<class U, class R = std::is_convertible<decltype(
|
||||
std::declval<U const>().size()), std::size_t>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
// max_size()
|
||||
template<class U, class R = std::is_convertible<decltype(
|
||||
std::declval<U const>().max_size()), std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
// capacity()
|
||||
template<class U, class R = std::is_convertible<decltype(
|
||||
std::declval<U const>().capacity()), std::size_t>>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
// data()
|
||||
template<class U, class R = std::integral_constant<
|
||||
bool, is_BufferSequence<decltype(
|
||||
std::declval<U const>().data()),
|
||||
boost::asio::const_buffer>::type::value>>
|
||||
static R check4(int);
|
||||
template<class>
|
||||
static std::false_type check4(...);
|
||||
using type4 = decltype(check4<T>(0));
|
||||
|
||||
// prepare()
|
||||
template<class U, class R = std::integral_constant<
|
||||
bool, is_BufferSequence<decltype(
|
||||
std::declval<U>().prepare(1)),
|
||||
boost::asio::mutable_buffer>::type::value>>
|
||||
static R check5(int);
|
||||
template<class>
|
||||
static std::false_type check5(...);
|
||||
using type5 = decltype(check5<T>(0));
|
||||
|
||||
// commit()
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().commit(1), std::true_type{})>
|
||||
static R check6(int);
|
||||
template<class>
|
||||
static std::false_type check6(...);
|
||||
using type6 = decltype(check6<T>(0));
|
||||
|
||||
// consume
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().consume(1), std::true_type{})>
|
||||
static R check7(int);
|
||||
template<class>
|
||||
static std::false_type check7(...);
|
||||
using type7 = decltype(check7<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value
|
||||
&& type2::value
|
||||
//&& type3::value // Networking TS
|
||||
&& type4::value
|
||||
&& type5::value
|
||||
&& type6::value
|
||||
&& type7::value
|
||||
>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
106
include/beast/core/detail/ci_char_traits.hpp
Normal file
106
include/beast/core/detail/ci_char_traits.hpp
Normal file
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
||||
#define BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
||||
|
||||
#include <boost/range/algorithm/equal.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
char
|
||||
tolower(char c)
|
||||
{
|
||||
static std::array<std::uint8_t, 256> constexpr tab = {{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
|
||||
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
|
||||
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
|
||||
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
|
||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
|
||||
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
|
||||
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
|
||||
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
|
||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
|
||||
}};
|
||||
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
inline
|
||||
boost::string_ref
|
||||
string_helper(const char (&s)[N])
|
||||
{
|
||||
return boost::string_ref{s, N-1};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline
|
||||
T const&
|
||||
string_helper(T const& t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
// Case-insensitive less
|
||||
struct ci_less
|
||||
{
|
||||
static bool const is_transparent = true;
|
||||
|
||||
template<class S1, class S2>
|
||||
bool
|
||||
operator()(S1 const& lhs, S2 const& rhs) const noexcept
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
auto const s1 = string_helper(lhs);
|
||||
auto const s2 = string_helper(rhs);
|
||||
return std::lexicographical_compare(
|
||||
begin(s1), end(s1), begin(s2), end(s2),
|
||||
[](char lhs, char rhs)
|
||||
{
|
||||
return tolower(lhs) < tolower(rhs);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Case-insensitive equal
|
||||
struct ci_equal_pred
|
||||
{
|
||||
bool
|
||||
operator()(char c1, char c2) const noexcept
|
||||
{
|
||||
return tolower(c1) == tolower(c2);
|
||||
}
|
||||
};
|
||||
|
||||
// Case-insensitive equal
|
||||
template<class S1, class S2>
|
||||
bool
|
||||
ci_equal(S1 const& lhs, S2 const& rhs)
|
||||
{
|
||||
return boost::range::equal(
|
||||
string_helper(lhs), string_helper(rhs),
|
||||
ci_equal_pred{});
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
40
include/beast/core/detail/clamp.hpp
Normal file
40
include/beast/core/detail/clamp.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_DETAIL_CLAMP_HPP
|
||||
#define BEAST_CORE_DETAIL_CLAMP_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <cstdlib>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
94
include/beast/core/detail/empty_base_optimization.hpp
Normal file
94
include/beast/core/detail/empty_base_optimization.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
|
||||
#define BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
struct empty_base_optimization_decide
|
||||
: std::integral_constant <bool,
|
||||
std::is_empty <T>::value
|
||||
#ifdef __clang__
|
||||
&& !__is_final(T)
|
||||
#endif
|
||||
>
|
||||
{
|
||||
};
|
||||
|
||||
template<
|
||||
class T,
|
||||
int UniqueID = 0,
|
||||
bool ShouldDeriveFrom =
|
||||
empty_base_optimization_decide<T>::value
|
||||
>
|
||||
class empty_base_optimization : private T
|
||||
{
|
||||
public:
|
||||
empty_base_optimization() = default;
|
||||
|
||||
empty_base_optimization(T const& t)
|
||||
: T (t)
|
||||
{}
|
||||
|
||||
empty_base_optimization(T&& t)
|
||||
: T (std::move (t))
|
||||
{}
|
||||
|
||||
T& member() noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
T const& member() const noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class T,
|
||||
int UniqueID
|
||||
>
|
||||
class empty_base_optimization <T, UniqueID, false>
|
||||
{
|
||||
public:
|
||||
empty_base_optimization() = default;
|
||||
|
||||
empty_base_optimization(T const& t)
|
||||
: m_t (t)
|
||||
{}
|
||||
|
||||
empty_base_optimization(T&& t)
|
||||
: m_t (std::move (t))
|
||||
{}
|
||||
|
||||
T& member() noexcept
|
||||
{
|
||||
return m_t;
|
||||
}
|
||||
|
||||
T const& member() const noexcept
|
||||
{
|
||||
return m_t;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_t;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
53
include/beast/core/detail/get_lowest_layer.hpp
Normal file
53
include/beast/core/detail/get_lowest_layer.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_GET_LOWEST_LAYER_HPP
|
||||
#define BEAST_DETAIL_GET_LOWEST_LAYER_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class has_lowest_layer
|
||||
{
|
||||
template<class U, class R =
|
||||
typename U::lowest_layer_type>
|
||||
static std::true_type check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
static bool constexpr value = type::value;
|
||||
};
|
||||
|
||||
template<class T, bool B>
|
||||
struct maybe_get_lowest_layer
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct maybe_get_lowest_layer<T, true>
|
||||
{
|
||||
using type = typename T::lowest_layer_type;
|
||||
};
|
||||
|
||||
// If T has a nested type lowest_layer_type,
|
||||
// returns that, else returns T.
|
||||
template<class T>
|
||||
struct get_lowest_layer
|
||||
{
|
||||
using type = typename maybe_get_lowest_layer<T,
|
||||
has_lowest_layer<T>::value>::type;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
145
include/beast/core/detail/integer_sequence.hpp
Normal file
145
include/beast/core/detail/integer_sequence.hpp
Normal file
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
|
||||
#define BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class T, T... Ints>
|
||||
struct integer_sequence
|
||||
{
|
||||
using value_type = T;
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"std::integer_sequence can only be instantiated with an integral type" );
|
||||
|
||||
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 _MSC_VER
|
||||
|
||||
// This implementation compiles on 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
|
||||
{
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"T must be an integral type");
|
||||
|
||||
static_assert (N >= 0,
|
||||
"N must be non-negative");
|
||||
|
||||
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...>>
|
||||
{
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"T must be an integral type");
|
||||
|
||||
static_assert (N >= 0,
|
||||
"N must be non-negative");
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
54
include/beast/core/detail/is_call_possible.hpp
Normal file
54
include/beast/core/detail/is_call_possible.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
|
||||
#define BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class R, class C, class ...A>
|
||||
auto
|
||||
is_call_possible_test(C&& c, int, A&& ...a)
|
||||
-> decltype(std::is_convertible<
|
||||
decltype(c(a...)), R>::value ||
|
||||
std::is_same<R, void>::value,
|
||||
std::true_type());
|
||||
|
||||
template<class R, class C, class ...A>
|
||||
std::false_type
|
||||
is_call_possible_test(C&& c, long, A&& ...a);
|
||||
|
||||
/** Metafunction returns `true` if F callable as R(A...)
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
is_call_possible<T, void(std::string)>
|
||||
@endcode
|
||||
*/
|
||||
/** @{ */
|
||||
template<class C, class F>
|
||||
struct is_call_possible
|
||||
: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class C, class R, class ...A>
|
||||
struct is_call_possible<C, R(A...)>
|
||||
: decltype(is_call_possible_test<R>(
|
||||
std::declval<C>(), 1, std::declval<A>()...))
|
||||
{
|
||||
};
|
||||
/** @} */
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
341
include/beast/core/detail/prepare_buffers.hpp
Normal file
341
include/beast/core/detail/prepare_buffers.hpp
Normal file
@@ -0,0 +1,341 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_PREPARED_BUFFERS_HPP
|
||||
#define BEAST_DETAIL_PREPARED_BUFFERS_HPP
|
||||
|
||||
#include <beast/core/prepare_buffer.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** 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 prepared_buffers
|
||||
{
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
BufferSequence bs_;
|
||||
iter_type back_;
|
||||
iter_type end_;
|
||||
std::size_t size_;
|
||||
|
||||
template<class Deduced>
|
||||
prepared_buffers(Deduced&& other,
|
||||
std::size_t nback, std::size_t nend)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, back_(std::next(bs_.begin(), nback))
|
||||
, end_(std::next(bs_.begin(), nend))
|
||||
, size_(other.size_)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
setup(std::size_t n);
|
||||
|
||||
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 GENERATING_DOCS
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_iterator;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
prepared_buffers(prepared_buffers&&);
|
||||
|
||||
/// Copy constructor.
|
||||
prepared_buffers(prepared_buffers const&);
|
||||
|
||||
/// Move assignment.
|
||||
prepared_buffers& operator=(prepared_buffers&&);
|
||||
|
||||
/// Copy assignment.
|
||||
prepared_buffers& operator=(prepared_buffers const&);
|
||||
|
||||
/** Construct a shortened buffer sequence.
|
||||
|
||||
@param n 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 The buffer sequence to adapt. A copy of
|
||||
the sequence will be made, but ownership of the underlying
|
||||
memory is not transferred.
|
||||
*/
|
||||
prepared_buffers(std::size_t n, BufferSequence const& buffers);
|
||||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
template<class BufferSequence>
|
||||
class prepared_buffers<BufferSequence>::const_iterator
|
||||
{
|
||||
friend class prepared_buffers<BufferSequence>;
|
||||
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
prepared_buffers const* b_ = nullptr;
|
||||
typename BufferSequence::const_iterator 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);
|
||||
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
|
||||
{
|
||||
return b_ == other.b_ && it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
if(it_ == b_->back_)
|
||||
return prepare_buffer(b_->size_, *it_);
|
||||
return *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(prepared_buffers const& b,
|
||||
bool at_end)
|
||||
: b_(&b)
|
||||
, it_(at_end ? b.end_ : b.bs_.begin())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class BufferSequence>
|
||||
void
|
||||
prepared_buffers<BufferSequence>::
|
||||
setup(std::size_t n)
|
||||
{
|
||||
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
|
||||
{
|
||||
auto const len =
|
||||
boost::asio::buffer_size(*end_);
|
||||
if(n <= len)
|
||||
{
|
||||
size_ = n;
|
||||
back_ = end_++;
|
||||
return;
|
||||
}
|
||||
n -= len;
|
||||
}
|
||||
size_ = 0;
|
||||
back_ = end_;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::const_iterator::
|
||||
const_iterator(const_iterator&& other)
|
||||
: b_(other.b_)
|
||||
, it_(std::move(other.it_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::const_iterator::
|
||||
const_iterator(const_iterator const& other)
|
||||
: b_(other.b_)
|
||||
, it_(other.it_)
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::const_iterator::
|
||||
operator=(const_iterator&& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
b_ = other.b_;
|
||||
it_ = std::move(other.it_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::const_iterator::
|
||||
operator=(const_iterator const& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
b_ = other.b_;
|
||||
it_ = other.it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(prepared_buffers&& other)
|
||||
: prepared_buffers(std::move(other),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.back_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(prepared_buffers const& other)
|
||||
: prepared_buffers(other,
|
||||
std::distance<iter_type>(other.bs_.begin(), other.back_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::
|
||||
operator=(prepared_buffers&& other) ->
|
||||
prepared_buffers&
|
||||
{
|
||||
auto const nback = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.back_);
|
||||
auto const nend = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_);
|
||||
bs_ = std::move(other.bs_);
|
||||
back_ = std::next(bs_.begin(), nback);
|
||||
end_ = std::next(bs_.begin(), nend);
|
||||
size_ = other.size_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::
|
||||
operator=(prepared_buffers const& other) ->
|
||||
prepared_buffers&
|
||||
{
|
||||
auto const nback = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.back_);
|
||||
auto const nend = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_);
|
||||
bs_ = other.bs_;
|
||||
back_ = std::next(bs_.begin(), nback);
|
||||
end_ = std::next(bs_.begin(), nend);
|
||||
size_ = other.size_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(std::size_t n, BufferSequence const& bs)
|
||||
: bs_(bs)
|
||||
{
|
||||
setup(n);
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
309
include/beast/core/detail/sha1.hpp
Normal file
309
include/beast/core/detail/sha1.hpp
Normal file
@@ -0,0 +1,309 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_SHA1_HPP
|
||||
#define 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 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
|
||||
|
||||
#endif
|
||||
134
include/beast/core/detail/stream_concepts.hpp
Normal file
134
include/beast/core/detail/stream_concepts.hpp
Normal file
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP
|
||||
#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// 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 T>
|
||||
class has_get_io_service
|
||||
{
|
||||
template<class U, class R = typename std::is_same<
|
||||
decltype(std::declval<U>().get_io_service()),
|
||||
boost::asio::io_service&>>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
public:
|
||||
using type = decltype(check<T>(0));
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_AsyncReadStream
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().async_read_some(
|
||||
std::declval<MutableBufferSequence>(),
|
||||
std::declval<ReadHandler>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type1 = decltype(check<T>(0));
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value &&
|
||||
has_get_io_service<T>::type::value>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_AsyncWriteStream
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().async_write_some(
|
||||
std::declval<ConstBufferSequence>(),
|
||||
std::declval<WriteHandler>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type1 = decltype(check<T>(0));
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value &&
|
||||
has_get_io_service<T>::type::value>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_SyncReadStream
|
||||
{
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().read_some(
|
||||
std::declval<MutableBufferSequence>())),
|
||||
std::size_t>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().read_some(
|
||||
std::declval<MutableBufferSequence>(),
|
||||
std::declval<error_code&>())), std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value && type2::value>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_SyncWriteStream
|
||||
{
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().write_some(
|
||||
std::declval<ConstBufferSequence>())),
|
||||
std::size_t>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().write_some(
|
||||
std::declval<ConstBufferSequence>(),
|
||||
std::declval<error_code&>())), std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value && type2::value>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
93
include/beast/core/detail/sync_ostream.hpp
Normal file
93
include/beast/core/detail/sync_ostream.hpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_SYNC_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_SYNC_OSTREAM_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** A SyncWriteStream which outputs to a `std::ostream`
|
||||
|
||||
Objects of this type meet the requirements of @b SyncWriteStream.
|
||||
*/
|
||||
class sync_ostream
|
||||
{
|
||||
std::ostream& os_;
|
||||
|
||||
public:
|
||||
/** Construct the stream.
|
||||
|
||||
@param os The associated `std::ostream`. All buffers
|
||||
written will be passed to the associated output stream.
|
||||
*/
|
||||
sync_ostream(std::ostream& os)
|
||||
: os_(os)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers);
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
};
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
sync_ostream::
|
||||
write_some(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(
|
||||
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
auto const n = write_some(buffers, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
sync_ostream::
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
std::size_t n = 0;
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for(auto const& buffer : buffers)
|
||||
{
|
||||
os_.write(buffer_cast<char const*>(buffer),
|
||||
buffer_size(buffer));
|
||||
if(os_.fail())
|
||||
{
|
||||
ec = errc::make_error_code(
|
||||
errc::no_stream_resources);
|
||||
break;
|
||||
}
|
||||
n += buffer_size(buffer);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
98
include/beast/core/detail/type_traits.hpp
Normal file
98
include/beast/core/detail/type_traits.hpp
Normal file
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||
#define BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class... Ts>
|
||||
struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template<class... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template<class... Ts>
|
||||
inline
|
||||
void
|
||||
ignore_unused(Ts const& ...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
inline
|
||||
void
|
||||
ignore_unused()
|
||||
{}
|
||||
|
||||
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<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 Exception>
|
||||
Exception
|
||||
make_exception(char const* reason, char const* file, int line)
|
||||
{
|
||||
char const* n = file;
|
||||
for(auto p = file; *p; ++p)
|
||||
if(*p == '\\' || *p == '/')
|
||||
n = p + 1;
|
||||
return Exception{std::string(reason) + " (" +
|
||||
n + ":" + std::to_string(line) + ")"};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
140
include/beast/core/detail/write_dynabuf.hpp
Normal file
140
include/beast/core/detail/write_dynabuf.hpp
Normal file
@@ -0,0 +1,140 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_WRITE_DYNABUF_HPP
|
||||
#define BEAST_DETAIL_WRITE_DYNABUF_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// detects string literals.
|
||||
template<class T>
|
||||
struct is_string_literal : std::integral_constant<bool,
|
||||
! std::is_same<T, typename std::remove_extent<T>::type>::value &&
|
||||
std::is_same<char, typename std::remove_extent<T>::type>::value>
|
||||
{
|
||||
};
|
||||
|
||||
// `true` if a call to boost::asio::buffer(T const&) is possible
|
||||
// note: we exclude string literals because boost::asio::buffer()
|
||||
// will include the null terminator, which we don't want.
|
||||
template<class T>
|
||||
class is_BufferConvertible
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
boost::asio::buffer(std::declval<U const&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
static bool const value = type::value &&
|
||||
! is_string_literal<T>::value;
|
||||
};
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
boost::asio::const_buffer const& buffer)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffer)),
|
||||
buffer));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
boost::asio::mutable_buffer const& buffer)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffer)),
|
||||
buffer));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T>
|
||||
typename std::enable_if<
|
||||
is_BufferConvertible<T>::value &&
|
||||
! std::is_convertible<T, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<T, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const buffers = boost::asio::buffer(t);
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class Buffers>
|
||||
typename std::enable_if<
|
||||
is_ConstBufferSequence<Buffers>::value &&
|
||||
! is_BufferConvertible<Buffers>::value &&
|
||||
! std::is_convertible<Buffers, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, Buffers const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, std::size_t N>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf, const char (&s)[N])
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(N - 1),
|
||||
boost::asio::buffer(s, N - 1)));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T>
|
||||
typename std::enable_if<
|
||||
! is_string_literal<T>::value &&
|
||||
! is_ConstBufferSequence<T>::value &&
|
||||
! is_BufferConvertible<T>::value &&
|
||||
! std::is_convertible<T, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<T, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const s = boost::lexical_cast<std::string>(t);
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(s.size()), buffer(s)));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T0, class T1, class... TN>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
T0 const& t0, T1 const& t1, TN const&... tn)
|
||||
{
|
||||
write_dynabuf(dynabuf, t0);
|
||||
write_dynabuf(dynabuf, t1, tn...);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
363
include/beast/core/dynabuf_readstream.hpp
Normal file
363
include/beast/core/dynabuf_readstream.hpp
Normal file
@@ -0,0 +1,363 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DYNABUF_READSTREAM_HPP
|
||||
#define BEAST_DYNABUF_READSTREAM_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/core/detail/get_lowest_layer.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
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(
|
||||
dynabuf_readstream<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 prepare_buffers() to limit the input
|
||||
// sequence to only the data up to and including
|
||||
// the trailing "\r\n\r\n".
|
||||
//
|
||||
auto header_buffers = prepare_buffers(
|
||||
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 dynabuf_readstream
|
||||
{
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
|
||||
template<class Buffers, class Handler>
|
||||
class read_some_op;
|
||||
|
||||
DynamicBuffer sb_;
|
||||
std::size_t capacity_ = 0;
|
||||
Stream next_layer_;
|
||||
|
||||
public:
|
||||
/// The type of the internal buffer
|
||||
using dynabuf_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 =
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined;
|
||||
#else
|
||||
typename detail::get_lowest_layer<
|
||||
next_layer_type>::type;
|
||||
#endif
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
@note The behavior of move assignment on or from streams
|
||||
with active or pending operations is undefined.
|
||||
*/
|
||||
dynabuf_readstream(dynabuf_readstream&&) = default;
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
@note The behavior of move assignment on or from streams
|
||||
with active or pending operations is undefined.
|
||||
*/
|
||||
dynabuf_readstream& operator=(dynabuf_readstream&&) = default;
|
||||
|
||||
/** Construct the wrapping stream.
|
||||
|
||||
@param args Parameters forwarded to the `Stream` constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
dynabuf_readstream(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 io_service associated with the object.
|
||||
boost::asio::io_service&
|
||||
get_io_service()
|
||||
{
|
||||
return next_layer_.get_io_service();
|
||||
}
|
||||
|
||||
/** 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 sb_;
|
||||
}
|
||||
|
||||
/// Access the internal buffer
|
||||
DynamicBuffer const&
|
||||
buffer() const
|
||||
{
|
||||
return sb_;
|
||||
}
|
||||
|
||||
/** 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_service::post`.
|
||||
*/
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
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_SyncWriteStream<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_SyncWriteStream<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_service::post`.
|
||||
*/
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<WriteHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler);
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/dynabuf_readstream.ipp>
|
||||
|
||||
#endif
|
||||
45
include/beast/core/error.hpp
Normal file
45
include/beast/core/error.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_ERROR_HPP
|
||||
#define BEAST_ERROR_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
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 system error category used by the library
|
||||
#if GENERATING_DOCS
|
||||
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 GENERATING_DOCS
|
||||
enum errc{};
|
||||
#else
|
||||
namespace errc = boost::system::errc;
|
||||
#endif
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
141
include/beast/core/handler_alloc.hpp
Normal file
141
include/beast/core/handler_alloc.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_ALLOC_HPP
|
||||
#define BEAST_HANDLER_ALLOC_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
// Guidance from
|
||||
// http://howardhinnant.github.io/allocator_boilerplate.html
|
||||
|
||||
/** An allocator that uses handler customizations.
|
||||
|
||||
This allocator uses the handler customizations `asio_handler_allocate`
|
||||
and `asio_handler_deallocate` to manage memory. It meets the requirements
|
||||
of @b Allocator and can be used anywhere a `std::allocator` is
|
||||
accepted.
|
||||
|
||||
@tparam T The type of objects allocated by the allocator.
|
||||
|
||||
@tparam Handler The type of handler.
|
||||
|
||||
@note Memory allocated by this allocator must be freed before
|
||||
the handler is invoked or undefined behavior results. This behavior
|
||||
is described as the "deallocate before invocation" Asio guarantee.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class T, class Handler>
|
||||
class handler_alloc;
|
||||
#else
|
||||
template<class T, class Handler>
|
||||
class handler_alloc
|
||||
{
|
||||
private:
|
||||
// We want a partial template specialization as a friend
|
||||
// but that isn't allowed so we friend all versions. This
|
||||
// should produce a compile error if Handler is not
|
||||
// constructible from H.
|
||||
//
|
||||
template<class U, class H>
|
||||
friend class handler_alloc;
|
||||
|
||||
Handler& h_;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using is_always_equal = std::true_type;
|
||||
|
||||
template<class U>
|
||||
struct rebind
|
||||
{
|
||||
using other = handler_alloc<U, Handler>;
|
||||
};
|
||||
|
||||
handler_alloc() = delete;
|
||||
handler_alloc(handler_alloc&&) = default;
|
||||
handler_alloc(handler_alloc const&) = default;
|
||||
handler_alloc& operator=(handler_alloc&&) = default;
|
||||
handler_alloc& operator=(handler_alloc const&) = default;
|
||||
|
||||
/** Construct the allocator.
|
||||
|
||||
A reference of the handler is stored. The handler must
|
||||
remain valid for at least the lifetime of the allocator.
|
||||
*/
|
||||
explicit
|
||||
handler_alloc(Handler& h)
|
||||
: h_(h)
|
||||
{
|
||||
}
|
||||
|
||||
/// Copy constructor
|
||||
template<class U>
|
||||
handler_alloc(
|
||||
handler_alloc<U, Handler> const& other)
|
||||
: h_(other.h_)
|
||||
{
|
||||
}
|
||||
|
||||
value_type*
|
||||
allocate(std::ptrdiff_t n)
|
||||
{
|
||||
auto const size = n * sizeof(T);
|
||||
return static_cast<value_type*>(
|
||||
beast_asio_helpers::allocate(
|
||||
size, h_));
|
||||
}
|
||||
|
||||
void
|
||||
deallocate(value_type* p, std::ptrdiff_t n)
|
||||
{
|
||||
auto const size = n * sizeof(T);
|
||||
beast_asio_helpers::deallocate(
|
||||
p, size, h_);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Work-around for MSVC not using allocator_traits
|
||||
// in the implementation of shared_ptr
|
||||
//
|
||||
void
|
||||
destroy(T* t)
|
||||
{
|
||||
t->~T();
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class U>
|
||||
friend
|
||||
bool
|
||||
operator==(handler_alloc const& lhs,
|
||||
handler_alloc<U, Handler> const& rhs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
friend
|
||||
bool
|
||||
operator!=(handler_alloc const& lhs,
|
||||
handler_alloc<U, Handler> const& rhs)
|
||||
{
|
||||
return ! (lhs == rhs);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
28
include/beast/core/handler_concepts.hpp
Normal file
28
include/beast/core/handler_concepts.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_CONCEPTS_HPP
|
||||
#define BEAST_HANDLER_CONCEPTS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/is_call_possible.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `CompletionHandler`.
|
||||
template<class T, class Signature>
|
||||
#if GENERATING_DOCS
|
||||
using is_CompletionHandler = std::integral_constant<bool, ...>;
|
||||
#else
|
||||
using is_CompletionHandler = std::integral_constant<bool,
|
||||
std::is_copy_constructible<typename std::decay<T>::type>::value &&
|
||||
detail::is_call_possible<T, Signature>::value>;
|
||||
#endif
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
105
include/beast/core/handler_helpers.hpp
Normal file
105
include/beast/core/handler_helpers.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_HELPERS_HPP
|
||||
#define BEAST_HANDLER_HELPERS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <memory>
|
||||
|
||||
/* Calls to:
|
||||
|
||||
* asio_handler_allocate
|
||||
* asio_handler_deallocate
|
||||
* asio_handler_invoke
|
||||
* asio_handler_is_continuation
|
||||
|
||||
must be made from a namespace that does not
|
||||
contain overloads of this function. The beast_asio_helpers
|
||||
namespace is defined here for that purpose.
|
||||
*/
|
||||
|
||||
namespace beast_asio_helpers {
|
||||
|
||||
/// Allocation function for handlers.
|
||||
template <class Handler>
|
||||
inline
|
||||
void*
|
||||
allocate(std::size_t s, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
return ::operator new(s);
|
||||
#else
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(s, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Deallocation function for handlers.
|
||||
template<class Handler>
|
||||
inline
|
||||
void
|
||||
deallocate(void* p, std::size_t s, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
::operator delete(p);
|
||||
#else
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(p, s, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Invoke function for handlers.
|
||||
template<class Function, class Handler>
|
||||
inline
|
||||
void
|
||||
invoke(Function& function, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
Function tmp(function);
|
||||
tmp();
|
||||
#else
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(function, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Invoke function for handlers.
|
||||
template<class Function, class Handler>
|
||||
inline
|
||||
void
|
||||
invoke(Function const& function, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
Function tmp(function);
|
||||
tmp();
|
||||
#else
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(function, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Returns true if handler represents a continuation of the asynchronous operation
|
||||
template<class Handler>
|
||||
inline
|
||||
bool
|
||||
is_continuation(Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
return false;
|
||||
#else
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // beast_asio_helpers
|
||||
|
||||
#endif
|
||||
204
include/beast/core/handler_ptr.hpp
Normal file
204
include/beast/core/handler_ptr.hpp
Normal file
@@ -0,0 +1,204 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_PTR_HPP
|
||||
#define BEAST_HANDLER_PTR_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
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/vinniefalco/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.
|
||||
*/
|
||||
template<class... Args>
|
||||
void
|
||||
invoke(Args&&... args);
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/handler_ptr.ipp>
|
||||
|
||||
#endif
|
||||
504
include/beast/core/impl/buffers_adapter.ipp
Normal file
504
include/beast/core/impl/buffers_adapter.ipp
Normal file
@@ -0,0 +1,504 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_BUFFERS_ADAPTER_IPP
|
||||
#define BEAST_IMPL_BUFFERS_ADAPTER_IPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
class buffers_adapter<MutableBufferSequence>::
|
||||
const_buffers_type
|
||||
{
|
||||
buffers_adapter const* ba_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_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
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return value_type{buffer_cast<void const*>(*it_),
|
||||
(ba_->out_ == ba_->bs_.end() ||
|
||||
it_ != ba_->out_) ? buffer_size(*it_) : 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
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return value_type{buffer_cast<void*>(*it_),
|
||||
it_ == std::prev(ba_->end_) ?
|
||||
ba_->out_end_ : buffer_size(*it_)} +
|
||||
(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>(other.bs_.begin(), other.begin_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.out_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
buffers_adapter<MutableBufferSequence>::buffers_adapter(
|
||||
buffers_adapter const& other)
|
||||
: buffers_adapter(other,
|
||||
std::distance<iter_type>(other.bs_.begin(), other.begin_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.out_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
auto
|
||||
buffers_adapter<MutableBufferSequence>::operator=(
|
||||
buffers_adapter&& other) -> buffers_adapter&
|
||||
{
|
||||
auto const nbegin = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_);
|
||||
auto const nout = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.out_);
|
||||
auto const nend = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_);
|
||||
bs_ = std::move(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_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
auto
|
||||
buffers_adapter<MutableBufferSequence>::operator=(
|
||||
buffers_adapter const& other) -> buffers_adapter&
|
||||
{
|
||||
auto const nbegin = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_);
|
||||
auto const nout = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.out_);
|
||||
auto const nend = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_);
|
||||
bs_ = 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_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
buffers_adapter<MutableBufferSequence>::buffers_adapter(
|
||||
MutableBufferSequence const& bs)
|
||||
: bs_(bs)
|
||||
, begin_(bs_.begin())
|
||||
, out_(bs_.begin())
|
||||
, end_(bs_.begin())
|
||||
, 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_ != bs_.end())
|
||||
{
|
||||
auto size = buffer_size(*end_) - out_pos_;
|
||||
if(n > size)
|
||||
{
|
||||
n -= size;
|
||||
while(++end_ != bs_.end())
|
||||
{
|
||||
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)
|
||||
throw detail::make_exception<std::length_error>(
|
||||
"no space", __FILE__, __LINE__);
|
||||
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
|
||||
|
||||
#endif
|
||||
212
include/beast/core/impl/consuming_buffers.ipp
Normal file
212
include/beast/core/impl/consuming_buffers.ipp
Normal file
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP
|
||||
#define BEAST_IMPL_CONSUMING_BUFFERS_IPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class BufferSequence>
|
||||
class consuming_buffers<BufferSequence>::const_iterator
|
||||
{
|
||||
friend class consuming_buffers<BufferSequence>;
|
||||
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
consuming_buffers 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(consuming_buffers const& b,
|
||||
iter_type it)
|
||||
: it_(it)
|
||||
, b_(&b)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class BufferSequence>
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers(consuming_buffers&& other)
|
||||
: consuming_buffers(std::move(other),
|
||||
std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers(consuming_buffers const& other)
|
||||
: consuming_buffers(other,
|
||||
std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
consuming_buffers<BufferSequence>::
|
||||
operator=(consuming_buffers&& other) ->
|
||||
consuming_buffers&
|
||||
{
|
||||
auto const nbegin = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_);
|
||||
bs_ = std::move(other.bs_);
|
||||
begin_ = std::next(bs_.begin(), nbegin);
|
||||
skip_ = other.skip_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
consuming_buffers<BufferSequence>::
|
||||
operator=(consuming_buffers const& other) ->
|
||||
consuming_buffers&
|
||||
{
|
||||
auto const nbegin = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_);
|
||||
bs_ = other.bs_;
|
||||
begin_ = std::next(bs_.begin(), nbegin);
|
||||
skip_ = other.skip_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers(BufferSequence const& bs)
|
||||
: bs_(bs)
|
||||
, begin_(bs_.begin())
|
||||
{
|
||||
static_assert(
|
||||
is_BufferSequence<BufferSequence, value_type>::value,
|
||||
"BufferSequence requirements not met");
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
auto
|
||||
consuming_buffers<BufferSequence>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, begin_};
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
auto
|
||||
consuming_buffers<BufferSequence>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, bs_.end()};
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
void
|
||||
consuming_buffers<BufferSequence>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
for(;n > 0 && begin_ != bs_.end(); ++begin_)
|
||||
{
|
||||
auto const len =
|
||||
buffer_size(*begin_) - skip_;
|
||||
if(n < len)
|
||||
{
|
||||
skip_ += n;
|
||||
break;
|
||||
}
|
||||
n -= len;
|
||||
skip_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
254
include/beast/core/impl/dynabuf_readstream.ipp
Normal file
254
include/beast/core/impl/dynabuf_readstream.ipp
Normal file
@@ -0,0 +1,254 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_DYNABUF_READSTREAM_HPP
|
||||
#define BEAST_IMPL_DYNABUF_READSTREAM_HPP
|
||||
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence, class Handler>
|
||||
class dynabuf_readstream<
|
||||
Stream, DynamicBuffer>::read_some_op
|
||||
{
|
||||
// VFALCO What about bool cont for is_continuation?
|
||||
struct data
|
||||
{
|
||||
dynabuf_readstream& srs;
|
||||
MutableBufferSequence bs;
|
||||
int state = 0;
|
||||
|
||||
data(Handler&, dynabuf_readstream& srs_,
|
||||
MutableBufferSequence const& bs_)
|
||||
: srs(srs_)
|
||||
, bs(bs_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
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,
|
||||
dynabuf_readstream& srs, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
srs, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, 0);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_some_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_some_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_some_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
is_continuation(op->d_.handler());
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence, class Handler>
|
||||
void
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
read_some_op<MutableBufferSequence, Handler>::operator()(
|
||||
error_code const& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
auto& d = *d_;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.srs.sb_.size() == 0)
|
||||
{
|
||||
d.state =
|
||||
d.srs.capacity_ > 0 ? 2 : 1;
|
||||
break;
|
||||
}
|
||||
d.state = 4;
|
||||
d.srs.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// read (unbuffered)
|
||||
d.state = 99;
|
||||
d.srs.next_layer_.async_read_some(
|
||||
d.bs, std::move(*this));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
// read
|
||||
d.state = 3;
|
||||
d.srs.next_layer_.async_read_some(
|
||||
d.srs.sb_.prepare(d.srs.capacity_),
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got data
|
||||
case 3:
|
||||
d.state = 4;
|
||||
d.srs.sb_.commit(bytes_transferred);
|
||||
break;
|
||||
|
||||
// copy
|
||||
case 4:
|
||||
bytes_transferred =
|
||||
boost::asio::buffer_copy(
|
||||
d.bs, d.srs.sb_.data());
|
||||
d.srs.sb_.consume(bytes_transferred);
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d_.invoke(ec, bytes_transferred);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class... Args>
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
dynabuf_readstream(Args&&... args)
|
||||
: next_layer_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
auto
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler) ->
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
{
|
||||
static_assert(is_AsyncWriteStream<next_layer_type>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(is_CompletionHandler<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
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
read_some(
|
||||
MutableBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_SyncReadStream<next_layer_type>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_MutableBufferSequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
auto n = read_some(buffers, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
read_some(MutableBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<next_layer_type>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_MutableBufferSequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
if(sb_.size() == 0)
|
||||
{
|
||||
if(capacity_ == 0)
|
||||
return next_layer_.read_some(buffers, ec);
|
||||
sb_.commit(next_layer_.read_some(
|
||||
sb_.prepare(capacity_), ec));
|
||||
if(ec)
|
||||
return 0;
|
||||
}
|
||||
auto bytes_transferred =
|
||||
buffer_copy(buffers, sb_.data());
|
||||
sb_.consume(bytes_transferred);
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
auto
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
async_read_some(
|
||||
MutableBufferSequence const& buffers,
|
||||
ReadHandler&& handler) ->
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
{
|
||||
static_assert(is_AsyncReadStream<next_layer_type>::value,
|
||||
"Stream requirements not met");
|
||||
static_assert(is_MutableBufferSequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
ReadHandler, void(error_code, std::size_t)
|
||||
> completion{handler};
|
||||
read_some_op<MutableBufferSequence,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, *this, buffers};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
129
include/beast/core/impl/handler_ptr.ipp
Normal file
129
include/beast/core/impl/handler_ptr.ipp
Normal file
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_HANDLER_PTR_HPP
|
||||
#define BEAST_IMPL_HANDLER_PTR_HPP
|
||||
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <memory>
|
||||
|
||||
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))
|
||||
{
|
||||
t = reinterpret_cast<T*>(
|
||||
beast_asio_helpers::
|
||||
allocate(sizeof(T), handler));
|
||||
try
|
||||
{
|
||||
t = new(t) T{handler,
|
||||
std::forward<Args>(args)...};
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
beast_asio_helpers::
|
||||
deallocate(t, sizeof(T), handler);
|
||||
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();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
}
|
||||
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)...})
|
||||
{
|
||||
static_assert(! std::is_array<T>::value,
|
||||
"T must not be an array type");
|
||||
}
|
||||
|
||||
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)...})
|
||||
{
|
||||
static_assert(! std::is_array<T>::value,
|
||||
"T must not be an array type");
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
auto
|
||||
handler_ptr<T, Handler>::
|
||||
release_handler() ->
|
||||
handler_type
|
||||
{
|
||||
BOOST_ASSERT(p_);
|
||||
BOOST_ASSERT(p_->t);
|
||||
p_->t->~T();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
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();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
p_->t = nullptr;
|
||||
p_->handler(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
307
include/beast/core/impl/static_streambuf.ipp
Normal file
307
include/beast/core/impl/static_streambuf.ipp
Normal file
@@ -0,0 +1,307 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STATIC_STREAMBUF_IPP
|
||||
#define BEAST_IMPL_STATIC_STREAMBUF_IPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
|
||||
class static_streambuf::const_buffers_type
|
||||
{
|
||||
std::size_t n_;
|
||||
std::uint8_t const* p_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_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 static_streambuf;
|
||||
|
||||
const_buffers_type(
|
||||
std::uint8_t const* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class static_streambuf::const_buffers_type::const_iterator
|
||||
{
|
||||
std::size_t n_ = 0;
|
||||
std::uint8_t const* p_ = 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 p_ == other.p_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return value_type{p_, n_};
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
p_ += n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
p_ -= n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class const_buffers_type;
|
||||
|
||||
const_iterator(
|
||||
std::uint8_t const* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::const_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_, n_};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::const_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_ + n_, n_};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class static_streambuf::mutable_buffers_type
|
||||
{
|
||||
std::size_t n_;
|
||||
std::uint8_t* p_;
|
||||
|
||||
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 static_streambuf;
|
||||
|
||||
mutable_buffers_type(
|
||||
std::uint8_t* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class static_streambuf::mutable_buffers_type::const_iterator
|
||||
{
|
||||
std::size_t n_ = 0;
|
||||
std::uint8_t* p_ = 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 p_ == other.p_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return value_type{p_, n_};
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
p_ += n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
p_ -= n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class mutable_buffers_type;
|
||||
|
||||
const_iterator(std::uint8_t* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::mutable_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_, n_};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::mutable_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_ + n_, n_};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type{in_,
|
||||
static_cast<std::size_t>(out_ - in_)};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(n > static_cast<std::size_t>(end_ - out_))
|
||||
throw detail::make_exception<std::length_error>(
|
||||
"no space in streambuf", __FILE__, __LINE__);
|
||||
last_ = out_ + n;
|
||||
return mutable_buffers_type{out_, n};
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
884
include/beast/core/impl/streambuf.ipp
Normal file
884
include/beast/core/impl/streambuf.ipp
Normal file
@@ -0,0 +1,884 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STREAMBUF_IPP
|
||||
#define BEAST_IMPL_STREAMBUF_IPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <beast/core/detail/write_dynabuf.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
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_streambuf<Allocator>::element
|
||||
: public boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
{
|
||||
using size_type = typename std::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_streambuf<Allocator>::const_buffers_type
|
||||
{
|
||||
basic_streambuf const* sb_;
|
||||
|
||||
friend class basic_streambuf;
|
||||
|
||||
explicit
|
||||
const_buffers_type(basic_streambuf const& sb);
|
||||
|
||||
public:
|
||||
// Why?
|
||||
using value_type = boost::asio::const_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;
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::mutable_buffers_type
|
||||
{
|
||||
basic_streambuf const* sb_;
|
||||
|
||||
friend class basic_streambuf;
|
||||
|
||||
explicit
|
||||
mutable_buffers_type(basic_streambuf const& sb);
|
||||
|
||||
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_streambuf<Allocator>::const_buffers_type::const_iterator
|
||||
{
|
||||
basic_streambuf const* sb_ = 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_streambuf const& sb,
|
||||
typename list_type::const_iterator const& it)
|
||||
: sb_(&sb)
|
||||
, it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return sb_ == other.sb_ && 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(),
|
||||
(sb_->out_ == sb_->list_.end() ||
|
||||
&e != &*sb_->out_) ? e.size() : sb_->out_pos_} +
|
||||
(&e == &*sb_->list_.begin() ? sb_->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_streambuf<Allocator>::const_buffers_type::const_buffers_type(
|
||||
basic_streambuf const& sb)
|
||||
: sb_(&sb)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::const_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->list_.begin()};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::const_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->out_ ==
|
||||
sb_->list_.end() ? sb_->list_.end() :
|
||||
std::next(sb_->out_)};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::mutable_buffers_type::const_iterator
|
||||
{
|
||||
basic_streambuf const* sb_ = 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_streambuf const& sb,
|
||||
typename list_type::const_iterator const& it)
|
||||
: sb_(&sb)
|
||||
, it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return sb_ == other.sb_ && 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(sb_->list_.end()) ?
|
||||
sb_->out_end_ : e.size()} +
|
||||
(&e == &*sb_->out_ ? sb_->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_streambuf<Allocator>::mutable_buffers_type::mutable_buffers_type(
|
||||
basic_streambuf const& sb)
|
||||
: sb_(&sb)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::mutable_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->out_};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::mutable_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->list_.end()};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::~basic_streambuf()
|
||||
{
|
||||
delete_list();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf&& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
std::move(other.member()))
|
||||
, alloc_size_(other.alloc_size_)
|
||||
, 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_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf&& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
if(this->member() != other.member())
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
else
|
||||
move_assign(other, std::true_type{});
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf&& other) -> basic_streambuf&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
// VFALCO If any memory allocated we could use it first?
|
||||
clear();
|
||||
alloc_size_ = other.alloc_size_;
|
||||
move_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_move_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf const& other)
|
||||
: basic_streambuf(other.alloc_size_,
|
||||
alloc_traits::select_on_container_copy_construction(other.member()))
|
||||
{
|
||||
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf const& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf const& other) ->
|
||||
basic_streambuf&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
using boost::asio::buffer_copy;
|
||||
clear();
|
||||
copy_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_copy_assignment::value>{});
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
basic_streambuf<OtherAlloc> const& other)
|
||||
: basic_streambuf(other.alloc_size_)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
basic_streambuf<OtherAlloc> const& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf<OtherAlloc> const& other) ->
|
||||
basic_streambuf&
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
clear();
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
std::size_t alloc_size, Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, out_(list_.end())
|
||||
, alloc_size_(alloc_size)
|
||||
{
|
||||
if(alloc_size <= 0)
|
||||
throw detail::make_exception<std::invalid_argument>(
|
||||
"invalid alloc_size", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_streambuf<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_streambuf<Allocator>::
|
||||
data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type(*this);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::prepare(size_type n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
list_type reuse;
|
||||
if(out_ != list_.end())
|
||||
{
|
||||
if(out_ != list_.iterator_to(list_.back()))
|
||||
{
|
||||
out_end_ = out_->size();
|
||||
reuse.splice(reuse.end(), list_,
|
||||
std::next(out_), list_.end());
|
||||
debug_check();
|
||||
}
|
||||
auto const avail = out_->size() - out_pos_;
|
||||
if(n > avail)
|
||||
{
|
||||
out_end_ = out_->size();
|
||||
n -= avail;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_end_ = out_pos_ + n;
|
||||
n = 0;
|
||||
}
|
||||
debug_check();
|
||||
}
|
||||
while(n > 0 && ! reuse.empty())
|
||||
{
|
||||
auto& e = reuse.front();
|
||||
reuse.erase(reuse.iterator_to(e));
|
||||
list_.push_back(e);
|
||||
if(n > e.size())
|
||||
{
|
||||
out_end_ = e.size();
|
||||
n -= e.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
out_end_ = n;
|
||||
n = 0;
|
||||
}
|
||||
debug_check();
|
||||
}
|
||||
while(n > 0)
|
||||
{
|
||||
auto const size = std::max(alloc_size_, 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);
|
||||
if(n >= e.size())
|
||||
{
|
||||
out_end_ = e.size();
|
||||
n -= e.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
out_end_ = n;
|
||||
n = 0;
|
||||
}
|
||||
debug_check();
|
||||
}
|
||||
for(auto it = reuse.begin(); it != reuse.end();)
|
||||
{
|
||||
auto& e = *it++;
|
||||
reuse.erase(list_.iterator_to(e));
|
||||
auto const len = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<char*>(&e), len);
|
||||
}
|
||||
return mutable_buffers_type(*this);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<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;
|
||||
debug_check();
|
||||
return;
|
||||
}
|
||||
++out_;
|
||||
n -= avail;
|
||||
out_pos_ = 0;
|
||||
in_size_ += avail;
|
||||
debug_check();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
debug_check();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<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;
|
||||
debug_check();
|
||||
break;
|
||||
}
|
||||
n -= avail;
|
||||
in_size_ -= avail;
|
||||
in_pos_ = 0;
|
||||
auto& e = list_.front();
|
||||
list_.erase(list_.iterator_to(e));
|
||||
auto const len = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<char*>(&e), len);
|
||||
debug_check();
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
debug_check();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
clear()
|
||||
{
|
||||
delete_list();
|
||||
list_.clear();
|
||||
out_ = list_.begin();
|
||||
in_size_ = 0;
|
||||
in_pos_ = 0;
|
||||
out_pos_ = 0;
|
||||
out_end_ = 0;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
move_assign(basic_streambuf& other, std::false_type)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
other.clear();
|
||||
}
|
||||
else
|
||||
move_assign(other, std::true_type{});
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
move_assign(basic_streambuf& 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>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
copy_assign(basic_streambuf const& other, std::false_type)
|
||||
{
|
||||
beast::detail::ignore_unused(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
copy_assign(basic_streambuf const& other, std::true_type)
|
||||
{
|
||||
this->member() = other.member();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::delete_list()
|
||||
{
|
||||
for(auto iter = list_.begin(); iter != list_.end();)
|
||||
{
|
||||
auto& e = *iter++;
|
||||
auto const n = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<char*>(&e), n);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<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
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
read_size_helper(basic_streambuf<
|
||||
Allocator> const& streambuf, std::size_t max_size)
|
||||
{
|
||||
BOOST_ASSERT(max_size >= 1);
|
||||
// If we already have an allocated
|
||||
// buffer, try to fill that up first
|
||||
auto const avail = streambuf.capacity() - streambuf.size();
|
||||
if (avail > 0)
|
||||
return (std::min)(avail, max_size);
|
||||
// Try to have just one new block allocated
|
||||
constexpr std::size_t low = 512;
|
||||
if (streambuf.alloc_size_ > low)
|
||||
return (std::min)(max_size, streambuf.alloc_size_);
|
||||
// ...but enforce a 512 byte minimum.
|
||||
return (std::min)(max_size, low);
|
||||
}
|
||||
|
||||
template<class Alloc, class T>
|
||||
basic_streambuf<Alloc>&
|
||||
operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
|
||||
{
|
||||
detail::write_dynabuf(streambuf, t);
|
||||
return streambuf;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
30
include/beast/core/placeholders.hpp
Normal file
30
include/beast/core/placeholders.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PLACEHOLDERS_HPP
|
||||
#define BEAST_PLACEHOLDERS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <functional>
|
||||
|
||||
namespace beast {
|
||||
namespace asio {
|
||||
|
||||
namespace placeholders {
|
||||
// asio placeholders that work with std::bind
|
||||
namespace {
|
||||
static auto const error (std::placeholders::_1);
|
||||
static auto const bytes_transferred (std::placeholders::_2);
|
||||
static auto const iterator (std::placeholders::_2);
|
||||
static auto const signal_number (std::placeholders::_2);
|
||||
}
|
||||
} // placeholders
|
||||
|
||||
} // asio
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
69
include/beast/core/prepare_buffer.hpp
Normal file
69
include/beast/core/prepare_buffer.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PREPARE_BUFFER_HPP
|
||||
#define BEAST_PREPARE_BUFFER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Return a shortened 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 n The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. Ownership of the
|
||||
underlying memory is not transferred.
|
||||
|
||||
@return A new buffer that points to the first `n` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::const_buffer
|
||||
prepare_buffer(std::size_t n,
|
||||
boost::asio::const_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void const*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
/** Return a shortened 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 n The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. Ownership of the
|
||||
underlying memory is not transferred.
|
||||
|
||||
@return A new buffer that points to the first `n` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::mutable_buffer
|
||||
prepare_buffer(std::size_t n,
|
||||
boost::asio::mutable_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
53
include/beast/core/prepare_buffers.hpp
Normal file
53
include/beast/core/prepare_buffers.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PREPARE_BUFFERS_HPP
|
||||
#define BEAST_PREPARE_BUFFERS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/prepare_buffers.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Return a shortened buffer sequence.
|
||||
|
||||
This function returns a new buffer sequence which adapts the
|
||||
passed buffer sequence and efficiently presents a shorter subset
|
||||
of the original list of buffers starting with the first byte of
|
||||
the original sequence.
|
||||
|
||||
@param n 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 The buffer sequence to adapt. A copy of
|
||||
the sequence will be made, but ownership of the underlying
|
||||
memory is not transferred.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
inline
|
||||
detail::prepared_buffers<BufferSequence>
|
||||
#endif
|
||||
prepare_buffers(std::size_t n, BufferSequence const& buffers)
|
||||
{
|
||||
return detail::prepared_buffers<BufferSequence>(n, buffers);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
199
include/beast/core/static_streambuf.hpp
Normal file
199
include/beast/core/static_streambuf.hpp
Normal file
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STATIC_STREAMBUF_HPP
|
||||
#define BEAST_STATIC_STREAMBUF_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A @b `DynamicBuffer` with a fixed size internal buffer.
|
||||
|
||||
Ownership of the underlying storage belongs to the derived class.
|
||||
|
||||
@note Variables are usually declared using the template class
|
||||
@ref static_streambuf_n; 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_streambuf.
|
||||
*/
|
||||
class static_streambuf
|
||||
{
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#else
|
||||
protected:
|
||||
#endif
|
||||
std::uint8_t* begin_;
|
||||
std::uint8_t* in_;
|
||||
std::uint8_t* out_;
|
||||
std::uint8_t* last_;
|
||||
std::uint8_t* end_;
|
||||
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
/// 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;
|
||||
|
||||
static_streambuf(
|
||||
static_streambuf const& other) noexcept = delete;
|
||||
|
||||
static_streambuf& operator=(
|
||||
static_streambuf const&) noexcept = delete;
|
||||
|
||||
#endif
|
||||
|
||||
/// 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 end_ - begin_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return end_ - in_;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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)
|
||||
{
|
||||
in_ += std::min<std::size_t>(n, out_ - in_);
|
||||
}
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#else
|
||||
protected:
|
||||
#endif
|
||||
static_streambuf(std::uint8_t* p, std::size_t n)
|
||||
{
|
||||
reset(p, n);
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::uint8_t* p, std::size_t n)
|
||||
{
|
||||
begin_ = p;
|
||||
in_ = p;
|
||||
out_ = p;
|
||||
last_ = p;
|
||||
end_ = p + n;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A `DynamicBuffer` with a fixed size internal buffer.
|
||||
|
||||
@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 `static_streambuf` instead.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
class static_streambuf_n
|
||||
: public static_streambuf
|
||||
#if ! GENERATING_DOCS
|
||||
, private boost::base_from_member<
|
||||
std::array<std::uint8_t, N>>
|
||||
#endif
|
||||
{
|
||||
using member_type = boost::base_from_member<
|
||||
std::array<std::uint8_t, N>>;
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
static_streambuf_n(
|
||||
static_streambuf_n const&) = delete;
|
||||
static_streambuf_n& operator=(
|
||||
static_streambuf_n const&) = delete;
|
||||
#if GENERATING_DOCS
|
||||
public:
|
||||
#endif
|
||||
|
||||
/// Construct a static stream buffer.
|
||||
static_streambuf_n()
|
||||
: static_streambuf(
|
||||
member_type::member.data(),
|
||||
member_type::member.size())
|
||||
{
|
||||
}
|
||||
|
||||
/** Reset the stream buffer.
|
||||
|
||||
Postconditions:
|
||||
The input sequence and output sequence are empty,
|
||||
`max_size()` returns `N`.
|
||||
*/
|
||||
void
|
||||
reset()
|
||||
{
|
||||
static_streambuf::reset(
|
||||
member_type::member.data(),
|
||||
member_type::member.size());
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/static_streambuf.ipp>
|
||||
|
||||
#endif
|
||||
684
include/beast/core/static_string.hpp
Normal file
684
include/beast/core/static_string.hpp
Normal file
@@ -0,0 +1,684 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP
|
||||
#define BEAST_WEBSOCKET_STATIC_STRING_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A 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.
|
||||
*/
|
||||
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;
|
||||
|
||||
std::size_t n_;
|
||||
std::array<CharT, N+1> s_;
|
||||
|
||||
public:
|
||||
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>;
|
||||
|
||||
/** Default constructor.
|
||||
|
||||
The string is initially empty, and null terminated.
|
||||
*/
|
||||
static_string();
|
||||
|
||||
/// Copy constructor.
|
||||
static_string(static_string const& s);
|
||||
|
||||
/// Copy constructor.
|
||||
template<std::size_t M>
|
||||
static_string(static_string<M, CharT, Traits> const& s);
|
||||
|
||||
/// Copy assignment.
|
||||
static_string&
|
||||
operator=(static_string const& s);
|
||||
|
||||
/// Copy assignment.
|
||||
template<std::size_t M>
|
||||
static_string&
|
||||
operator=(static_string<M, CharT, Traits> const& s);
|
||||
|
||||
/// Construct from string literal.
|
||||
template<std::size_t M>
|
||||
static_string(const CharT (&s)[M]);
|
||||
|
||||
/// Assign from string literal.
|
||||
template<std::size_t M>
|
||||
static_string& operator=(const CharT (&s)[M]);
|
||||
|
||||
/// 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 &s_[0];
|
||||
}
|
||||
|
||||
/// 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()};
|
||||
}
|
||||
|
||||
/// 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 maximum number of characters that can be stored, excluding the null terminator.
|
||||
size_type constexpr
|
||||
max_size() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
/// Returns the number of characters that can be held in currently allocated storage.
|
||||
size_type
|
||||
capacity() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
/// Clears the contents.
|
||||
void
|
||||
clear()
|
||||
{
|
||||
resize(0);
|
||||
}
|
||||
|
||||
/** Changes the number of characters stored.
|
||||
|
||||
@note No value-initialization is performed.
|
||||
*/
|
||||
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);
|
||||
|
||||
/// Compare two character sequences.
|
||||
template<std::size_t M>
|
||||
int
|
||||
compare(static_string<M, CharT, Traits> const& rhs) const;
|
||||
|
||||
/// Return the characters as a `basic_string`.
|
||||
std::basic_string<CharT, Traits>
|
||||
to_string() const
|
||||
{
|
||||
return std::basic_string<
|
||||
CharT, Traits>{&s_[0], n_};
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
assign(CharT const* s);
|
||||
};
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string()
|
||||
: n_(0)
|
||||
{
|
||||
s_[0] = 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string const& s)
|
||||
: n_(s.n_)
|
||||
{
|
||||
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if(s.size() > N)
|
||||
throw detail::make_exception<std::length_error>(
|
||||
"static_string overflow", __FILE__, __LINE__);
|
||||
n_ = s.size();
|
||||
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
operator=(static_string const& s) ->
|
||||
static_string&
|
||||
{
|
||||
n_ = s.n_;
|
||||
Traits::copy(&s_[0], &s.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>::
|
||||
operator=(static_string<M, CharT, Traits> const& s) ->
|
||||
static_string&
|
||||
{
|
||||
if(s.size() > N)
|
||||
throw detail::make_exception<std::length_error>(
|
||||
"static_string overflow", __FILE__, __LINE__);
|
||||
n_ = s.size();
|
||||
Traits::copy(&s_[0], &s.s_[0], n_ + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(const CharT (&s)[M])
|
||||
: n_(M-1)
|
||||
{
|
||||
static_assert(M-1 <= N,
|
||||
"static_string overflow");
|
||||
Traits::copy(&s_[0], &s[0], M);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
operator=(const CharT (&s)[M]) ->
|
||||
static_string&
|
||||
{
|
||||
static_assert(M-1 <= N,
|
||||
"static_string overflow");
|
||||
n_ = M-1;
|
||||
Traits::copy(&s_[0], &s[0], M);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
at(size_type pos) ->
|
||||
reference
|
||||
{
|
||||
if(pos >= n_)
|
||||
throw detail::make_exception<std::out_of_range>(
|
||||
"invalid pos", __FILE__, __LINE__);
|
||||
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 >= n_)
|
||||
throw detail::make_exception<std::out_of_range>(
|
||||
"static_string::at", __FILE__, __LINE__);
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
resize(std::size_t n)
|
||||
{
|
||||
if(n > N)
|
||||
throw detail::make_exception<std::length_error>(
|
||||
"static_string overflow", __FILE__, __LINE__);
|
||||
n_ = n;
|
||||
s_[n_] = 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
resize(std::size_t n, CharT c)
|
||||
{
|
||||
if(n > N)
|
||||
throw detail::make_exception<std::length_error>(
|
||||
"static_string overflow", __FILE__, __LINE__);
|
||||
if(n > n_)
|
||||
Traits::assign(&s_[n_], n - n_, c);
|
||||
n_ = n;
|
||||
s_[n_] = 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
int
|
||||
static_string<N, CharT, Traits>::
|
||||
compare(static_string<M, CharT, Traits> const& rhs) const
|
||||
{
|
||||
if(size() < rhs.size())
|
||||
{
|
||||
auto const v = Traits::compare(
|
||||
data(), rhs.data(), size());
|
||||
if(v == 0)
|
||||
return -1;
|
||||
return v;
|
||||
}
|
||||
else if(size() > rhs.size())
|
||||
{
|
||||
auto const v = Traits::compare(
|
||||
data(), rhs.data(), rhs.size());
|
||||
if(v == 0)
|
||||
return 1;
|
||||
return v;
|
||||
}
|
||||
return Traits::compare(data(), rhs.data(), size());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(CharT const* s)
|
||||
{
|
||||
auto const n = Traits::length(s);
|
||||
if(n > N)
|
||||
throw detail::make_exception<std::out_of_range>(
|
||||
"too large", __FILE__, __LINE__);
|
||||
n_ = n;
|
||||
Traits::copy(&s_[0], s, n_ + 1);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<std::size_t N, std::size_t M, class CharT, class Traits>
|
||||
int
|
||||
compare(
|
||||
static_string<N, CharT, Traits> const& lhs,
|
||||
const CharT (&s)[M])
|
||||
{
|
||||
if(lhs.size() < M-1)
|
||||
{
|
||||
auto const v = Traits::compare(
|
||||
lhs.data(), &s[0], lhs.size());
|
||||
if(v == 0)
|
||||
return -1;
|
||||
return v;
|
||||
}
|
||||
else if(lhs.size() > M-1)
|
||||
{
|
||||
auto const v = Traits::compare(
|
||||
lhs.data(), &s[0], M-1);
|
||||
if(v == 0)
|
||||
return 1;
|
||||
return v;
|
||||
}
|
||||
return Traits::compare(lhs.data(), &s[0], lhs.size());
|
||||
}
|
||||
|
||||
template<std::size_t N, std::size_t M, class CharT, class Traits>
|
||||
inline
|
||||
int
|
||||
compare(
|
||||
const CharT (&s)[M],
|
||||
static_string<N, CharT, Traits> const& rhs)
|
||||
{
|
||||
return -compare(rhs, s);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
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, std::size_t M, class CharT, class Traits>
|
||||
bool
|
||||
operator==(
|
||||
const CharT (&s)[N],
|
||||
static_string<M, CharT, Traits> const& rhs)
|
||||
{
|
||||
return detail::compare(s, rhs) == 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits, std::size_t M>
|
||||
bool
|
||||
operator==(
|
||||
static_string<N, CharT, Traits> const& lhs,
|
||||
const CharT (&s)[M])
|
||||
{
|
||||
return detail::compare(lhs, s) == 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, std::size_t M, class CharT, class Traits>
|
||||
bool
|
||||
operator!=(
|
||||
const CharT (&s)[N],
|
||||
static_string<M, CharT, Traits> const& rhs)
|
||||
{
|
||||
return detail::compare(s, rhs) != 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits, std::size_t M>
|
||||
bool
|
||||
operator!=(
|
||||
static_string<N, CharT, Traits> const& lhs,
|
||||
const CharT (&s)[M])
|
||||
{
|
||||
return detail::compare(lhs, s) != 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, std::size_t M, class CharT, class Traits>
|
||||
bool
|
||||
operator<(
|
||||
const CharT (&s)[N],
|
||||
static_string<M, CharT, Traits> const& rhs)
|
||||
{
|
||||
return detail::compare(s, rhs) < 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits, std::size_t M>
|
||||
bool
|
||||
operator<(
|
||||
static_string<N, CharT, Traits> const& lhs,
|
||||
const CharT (&s)[M])
|
||||
{
|
||||
return detail::compare(lhs, s) < 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, std::size_t M, class CharT, class Traits>
|
||||
bool
|
||||
operator<=(
|
||||
const CharT (&s)[N],
|
||||
static_string<M, CharT, Traits> const& rhs)
|
||||
{
|
||||
return detail::compare(s, rhs) <= 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits, std::size_t M>
|
||||
bool
|
||||
operator<=(
|
||||
static_string<N, CharT, Traits> const& lhs,
|
||||
const CharT (&s)[M])
|
||||
{
|
||||
return detail::compare(lhs, s) <= 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, std::size_t M, class CharT, class Traits>
|
||||
bool
|
||||
operator>(
|
||||
const CharT (&s)[N],
|
||||
static_string<M, CharT, Traits> const& rhs)
|
||||
{
|
||||
return detail::compare(s, rhs) > 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits, std::size_t M>
|
||||
bool
|
||||
operator>(
|
||||
static_string<N, CharT, Traits> const& lhs,
|
||||
const CharT (&s)[M])
|
||||
{
|
||||
return detail::compare(lhs, s) > 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, std::size_t M, class CharT, class Traits>
|
||||
bool
|
||||
operator>=(
|
||||
const CharT (&s)[N],
|
||||
static_string<M, CharT, Traits> const& rhs)
|
||||
{
|
||||
return detail::compare(s, rhs) >= 0;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits, std::size_t M>
|
||||
bool
|
||||
operator>=(
|
||||
static_string<N, CharT, Traits> const& lhs,
|
||||
const CharT (&s)[M])
|
||||
{
|
||||
return detail::compare(lhs, s) >= 0;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
77
include/beast/core/stream_concepts.hpp
Normal file
77
include/beast/core/stream_concepts.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STREAM_CONCEPTS_HPP
|
||||
#define BEAST_STREAM_CONCEPTS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/stream_concepts.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/// Determine if `T` has the `get_io_service` member.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct has_get_io_service : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using has_get_io_service = typename detail::has_get_io_service<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `AsyncReadStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_AsyncReadStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_AsyncReadStream = typename detail::is_AsyncReadStream<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `AsyncWriteStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_AsyncWriteStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_AsyncWriteStream = typename detail::is_AsyncWriteStream<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `SyncReadStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_SyncReadStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_SyncReadStream = typename detail::is_SyncReadStream<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `SyncWriterStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_SyncWriteStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_SyncWriteStream = typename detail::is_SyncWriteStream<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `AsyncStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_AsyncStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_AsyncStream = std::integral_constant<bool,
|
||||
is_AsyncReadStream<T>::value && is_AsyncWriteStream<T>::value>;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `SyncStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_SyncStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_SyncStream = std::integral_constant<bool,
|
||||
is_SyncReadStream<T>::value && is_SyncWriteStream<T>::value>;
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
345
include/beast/core/streambuf.hpp
Normal file
345
include/beast/core/streambuf.hpp
Normal file
@@ -0,0 +1,345 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STREAMBUF_HPP
|
||||
#define BEAST_STREAMBUF_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <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 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_streambuf
|
||||
#if ! GENERATING_DOCS
|
||||
: private detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
#else
|
||||
using allocator_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// 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 = std::allocator_traits<allocator_type>;
|
||||
using list_type = typename boost::intrusive::make_list<element,
|
||||
boost::intrusive::constant_time_size<true>>::type;
|
||||
using iterator = typename list_type::iterator;
|
||||
using const_iterator = typename list_type::const_iterator;
|
||||
|
||||
using size_type = typename std::allocator_traits<Allocator>::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<iterator>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<const_iterator>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
list_type list_; // list of allocated buffers
|
||||
iterator out_; // element that contains out_pos_
|
||||
size_type alloc_size_; // min amount to allocate
|
||||
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:
|
||||
#if GENERATING_DOCS
|
||||
/// 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_streambuf();
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The new object will have the input sequence of
|
||||
the other stream buffer, and an empty output sequence.
|
||||
|
||||
@note After the move, the moved-from object will have
|
||||
an empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf&&);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The new object will have the input sequence of
|
||||
the other stream buffer, and an empty output sequence.
|
||||
|
||||
@note After the move, the moved-from object will have
|
||||
an empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf&&,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
This object will have the input sequence of
|
||||
the other stream buffer, and an empty output sequence.
|
||||
|
||||
@note After the move, the moved-from object will have
|
||||
an empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
*/
|
||||
basic_streambuf&
|
||||
operator=(basic_streambuf&&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf const&,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Copy assignment.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
*/
|
||||
basic_streambuf& operator=(basic_streambuf const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf(basic_streambuf<OtherAlloc> const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf(basic_streambuf<OtherAlloc> const&,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Copy assignment.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const&);
|
||||
|
||||
/** Construct a stream buffer.
|
||||
|
||||
@param alloc_size The size of buffer to allocate. This is a
|
||||
soft limit, calls to prepare for buffers exceeding this size
|
||||
will allocate the larger size. The default allocation size
|
||||
is 1KB (1024 bytes).
|
||||
|
||||
@param alloc The allocator to use. If this parameter is
|
||||
unspecified, a default constructed allocator will be used.
|
||||
*/
|
||||
explicit
|
||||
basic_streambuf(std::size_t alloc_size = 1024,
|
||||
Allocator const& alloc = allocator_type{});
|
||||
|
||||
/// Returns a copy of the associated allocator.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return this->member();
|
||||
}
|
||||
|
||||
/** Returns the default allocation size.
|
||||
|
||||
This is the smallest size that the stream buffer will allocate.
|
||||
The size of the allocation can influence capacity, which will
|
||||
affect algorithms that use capacity to efficiently read from
|
||||
streams.
|
||||
*/
|
||||
std::size_t
|
||||
alloc_size() const
|
||||
{
|
||||
return alloc_size_;
|
||||
}
|
||||
|
||||
/** Set the default allocation size.
|
||||
|
||||
This is the smallest size that the stream buffer will allocate.
|
||||
The size of the allocation can influence capacity, which will
|
||||
affect algorithms that use capacity to efficiently read from
|
||||
streams.
|
||||
|
||||
@note This will not affect any already-existing allocations.
|
||||
|
||||
@param n The number of bytes.
|
||||
*/
|
||||
void
|
||||
alloc_size(std::size_t n)
|
||||
{
|
||||
alloc_size_ = n;
|
||||
}
|
||||
|
||||
/// 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 (std::numeric_limits<std::size_t>::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);
|
||||
|
||||
// Helper for boost::asio::read_until
|
||||
template<class OtherAllocator>
|
||||
friend
|
||||
std::size_t
|
||||
read_size_helper(basic_streambuf<
|
||||
OtherAllocator> const& streambuf, std::size_t max_size);
|
||||
|
||||
private:
|
||||
void
|
||||
clear();
|
||||
|
||||
void
|
||||
move_assign(basic_streambuf& other, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_streambuf& other, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_streambuf const& other, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_streambuf const& other, std::true_type);
|
||||
|
||||
void
|
||||
delete_list();
|
||||
|
||||
void
|
||||
debug_check() const;
|
||||
};
|
||||
|
||||
/** 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`.
|
||||
*/
|
||||
using streambuf = basic_streambuf<std::allocator<char>>;
|
||||
|
||||
/** Format output to a @ref basic_streambuf.
|
||||
|
||||
@param streambuf The @ref basic_streambuf to write to.
|
||||
|
||||
@param t The object to write.
|
||||
|
||||
@return A reference to the @ref basic_streambuf.
|
||||
*/
|
||||
template<class Allocator, class T>
|
||||
basic_streambuf<Allocator>&
|
||||
operator<<(basic_streambuf<Allocator>& streambuf, T const& t);
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/streambuf.ipp>
|
||||
|
||||
#endif
|
||||
53
include/beast/core/to_string.hpp
Normal file
53
include/beast/core/to_string.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_TO_STRING_HPP
|
||||
#define BEAST_TO_STRING_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Convert a @b `ConstBufferSequence` to a `std::string`.
|
||||
|
||||
This function will convert the octets in a buffer sequence to a string.
|
||||
All octets will be inserted into the resulting string, including null
|
||||
or unprintable characters.
|
||||
|
||||
@param buffers The buffer sequence to convert.
|
||||
|
||||
@return A string representing the contents of the input area.
|
||||
|
||||
@note This function participates in overload resolution only if
|
||||
the buffers parameter meets the requirements of @b `ConstBufferSequence`.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
std::string
|
||||
#else
|
||||
typename std::enable_if<
|
||||
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
std::string>::type
|
||||
#endif
|
||||
to_string(ConstBufferSequence const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
std::string s;
|
||||
s.reserve(buffer_size(buffers));
|
||||
for(auto const& buffer : buffers)
|
||||
s.append(buffer_cast<char const*>(buffer),
|
||||
buffer_size(buffer));
|
||||
return s;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
64
include/beast/core/write_dynabuf.hpp
Normal file
64
include/beast/core/write_dynabuf.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WRITE_DYNABUF_HPP
|
||||
#define BEAST_WRITE_DYNABUF_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/detail/write_dynabuf.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Write to a @b `DynamicBuffer`.
|
||||
|
||||
This function appends the serialized representation of each provided
|
||||
argument into the dynamic buffer. It is capable of converting the
|
||||
following types of arguments:
|
||||
|
||||
@li `boost::asio::const_buffer`
|
||||
|
||||
@li `boost::asio::mutable_buffer`
|
||||
|
||||
@li A type meeting the requirements of @b `ConvertibleToConstBuffer`
|
||||
|
||||
@li A type meeting the requirements of @b `ConstBufferSequence`
|
||||
|
||||
@li A type meeting the requirements of @b `MutableBufferSequence`
|
||||
|
||||
For all types not listed above, the function will invoke
|
||||
`boost::lexical_cast` on the argument in an attempt to convert to
|
||||
a string, which is then appended to the dynamic buffer.
|
||||
|
||||
When this function serializes numbers, it converts them to
|
||||
their text representation as if by a call to `std::to_string`.
|
||||
|
||||
@param dynabuf The dynamic buffer to write to.
|
||||
|
||||
@param args A list of one or more arguments to write.
|
||||
|
||||
@throws unspecified Any exceptions thrown by `boost::lexical_cast`.
|
||||
|
||||
@note This function participates in overload resolution only if
|
||||
the `dynabuf` parameter meets the requirements of @b `DynamicBuffer`.
|
||||
*/
|
||||
template<class DynamicBuffer, class... Args>
|
||||
#if GENERATING_DOCS
|
||||
void
|
||||
#else
|
||||
typename std::enable_if<is_DynamicBuffer<DynamicBuffer>::value>::type
|
||||
#endif
|
||||
write(DynamicBuffer& dynabuf, Args const&... args)
|
||||
{
|
||||
detail::write_dynabuf(dynabuf, args...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
29
include/beast/http.hpp
Normal file
29
include/beast/http.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_HPP
|
||||
#define BEAST_HTTP_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
|
||||
#include <beast/http/basic_fields.hpp>
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/chunk_encode.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/reason.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/streambuf_body.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
|
||||
#endif
|
||||
101
include/beast/http/basic_dynabuf_body.hpp
Normal file
101
include/beast/http/basic_dynabuf_body.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
|
||||
#define BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A message body represented by a @b `DynamicBuffer`
|
||||
|
||||
Meets the requirements of @b `Body`.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
struct basic_dynabuf_body
|
||||
{
|
||||
/// The type of the `message::body` member
|
||||
using value_type = DynamicBuffer;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& sb_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
basic_dynabuf_body, Fields>& m) noexcept
|
||||
: sb_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
sb_.commit(buffer_copy(
|
||||
sb_.prepare(size), buffer(data, size)));
|
||||
}
|
||||
};
|
||||
|
||||
class writer
|
||||
{
|
||||
DynamicBuffer const& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<
|
||||
isRequest, basic_dynabuf_body, Fields> const& m) noexcept
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec) noexcept
|
||||
{
|
||||
beast::detail::ignore_unused(ec);
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
content_length() const noexcept
|
||||
{
|
||||
return body_.size();
|
||||
}
|
||||
|
||||
template<class WriteFunction>
|
||||
bool
|
||||
write(error_code&, WriteFunction&& wf) noexcept
|
||||
{
|
||||
wf(body_.data());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
307
include/beast/http/basic_fields.hpp
Normal file
307
include/beast/http/basic_fields.hpp
Normal file
@@ -0,0 +1,307 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_FIELDS_HPP
|
||||
#define BEAST_HTTP_BASIC_FIELDS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <beast/http/detail/basic_fields.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
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 a 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.
|
||||
When the container is iterated, the fields are presented in the order
|
||||
of insertion. For fields with the same name, the container behaves
|
||||
as a `std::multiset`; there will be a separate value for each occurrence
|
||||
of the field name.
|
||||
|
||||
@note Meets the requirements of @b FieldSequence.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_fields :
|
||||
#if ! GENERATING_DOCS
|
||||
private beast::detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_fields_base::element>>,
|
||||
#endif
|
||||
public detail::basic_fields_base
|
||||
{
|
||||
using alloc_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_fields_base::element>;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<alloc_type>;
|
||||
|
||||
using size_type =
|
||||
typename std::allocator_traits<Allocator>::size_type;
|
||||
|
||||
void
|
||||
delete_all();
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::true_type);
|
||||
|
||||
template<class FieldSequence>
|
||||
void
|
||||
copy_from(FieldSequence const& fs)
|
||||
{
|
||||
for(auto const& e : fs)
|
||||
insert(e.first, e.second);
|
||||
}
|
||||
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
|
||||
/** The value type of the field sequence.
|
||||
|
||||
Meets the requirements of @b Field.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using value_type = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// A const iterator to the field sequence
|
||||
#if GENERATING_DOCS
|
||||
using iterator = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// A const iterator to the field sequence
|
||||
#if GENERATING_DOCS
|
||||
using const_iterator = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// Default constructor.
|
||||
basic_fields() = default;
|
||||
|
||||
/// Destructor
|
||||
~basic_fields();
|
||||
|
||||
/** Construct the fields.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_fields(Allocator const& alloc);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_fields(basic_fields&& other);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_fields& operator=(basic_fields&& other);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_fields(basic_fields const&);
|
||||
|
||||
/// Copy assignment.
|
||||
basic_fields& operator=(basic_fields const&);
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherAlloc>
|
||||
basic_fields& operator=(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/// Construct from a field sequence.
|
||||
template<class FwdIt>
|
||||
basic_fields(FwdIt first, FwdIt last);
|
||||
|
||||
/// Returns `true` if the field sequence contains no elements.
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return set_.empty();
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the field sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return set_.size();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Returns `true` if the specified field exists.
|
||||
bool
|
||||
exists(boost::string_ref const& name) const
|
||||
{
|
||||
return set_.find(name, less{}) != set_.end();
|
||||
}
|
||||
|
||||
/// Returns the number of values for the specified field.
|
||||
std::size_t
|
||||
count(boost::string_ref const& 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.
|
||||
*/
|
||||
iterator
|
||||
find(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or `""`.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
boost::string_ref
|
||||
operator[](boost::string_ref const& name) const;
|
||||
|
||||
/// Clear the contents of the basic_fields.
|
||||
void
|
||||
clear() noexcept;
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
If more than one field with the specified name exists, all
|
||||
matching fields will be removed.
|
||||
|
||||
@param name The name of the field(s) to remove.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(boost::string_ref const& name);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
insert(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
insert(boost::string_ref name, T const& value)
|
||||
{
|
||||
insert(name, boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
replace(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
replace(boost::string_ref const& name, T const& value)
|
||||
{
|
||||
replace(name,
|
||||
boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_fields.ipp>
|
||||
|
||||
#endif
|
||||
856
include/beast/http/basic_parser_v1.hpp
Normal file
856
include/beast/http/basic_parser_v1.hpp
Normal file
@@ -0,0 +1,856 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_PARSER_v1_HPP
|
||||
#define BEAST_HTTP_BASIC_PARSER_v1_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/detail/basic_parser_v1.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Parse flags
|
||||
|
||||
The set of parser bit flags are returned by @ref basic_parser_v1::flags.
|
||||
*/
|
||||
enum parse_flag
|
||||
{
|
||||
chunked = 1,
|
||||
connection_keep_alive = 2,
|
||||
connection_close = 4,
|
||||
connection_upgrade = 8,
|
||||
trailing = 16,
|
||||
upgrade = 32,
|
||||
skipbody = 64,
|
||||
contentlength = 128,
|
||||
paused = 256
|
||||
};
|
||||
|
||||
/** Body maximum size option.
|
||||
|
||||
Sets the maximum number of cumulative bytes allowed including
|
||||
all body octets. Octets in chunk-encoded bodies are counted
|
||||
after decoding. A value of zero indicates no limit on
|
||||
the number of body octets.
|
||||
|
||||
The default body maximum size for requests is 4MB (four
|
||||
megabytes or 4,194,304 bytes) and unlimited for responses.
|
||||
|
||||
@note Objects of this type are used with @ref basic_parser_v1::set_option.
|
||||
*/
|
||||
struct body_max_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
body_max_size(std::size_t v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Header maximum size option.
|
||||
|
||||
Sets the maximum number of cumulative bytes allowed
|
||||
including all header octets. A value of zero indicates
|
||||
no limit on the number of header octets.
|
||||
|
||||
The default header maximum size is 16KB (16,384 bytes).
|
||||
|
||||
@note Objects of this type are used with @ref basic_parser_v1::set_option.
|
||||
*/
|
||||
struct header_max_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
header_max_size(std::size_t v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** A value indicating how the parser should treat the body.
|
||||
|
||||
This value is returned from the `on_header` callback in
|
||||
the derived class. It controls what the parser does next
|
||||
in terms of the message body.
|
||||
*/
|
||||
enum class body_what
|
||||
{
|
||||
/** The parser should expect a body, keep reading.
|
||||
*/
|
||||
normal,
|
||||
|
||||
/** Skip parsing of the body.
|
||||
|
||||
When returned by `on_header` this causes parsing to
|
||||
complete and control to return to the caller. This
|
||||
could be used when sending a response to a HEAD
|
||||
request, for example.
|
||||
*/
|
||||
skip,
|
||||
|
||||
/** The message represents an UPGRADE request.
|
||||
|
||||
When returned by `on_body_prepare` this causes parsing
|
||||
to complete and control to return to the caller.
|
||||
*/
|
||||
upgrade,
|
||||
|
||||
/** Suspend parsing before reading the body.
|
||||
|
||||
When returned by `on_body_prepare` this causes parsing
|
||||
to pause. Control is returned to the caller, and the
|
||||
parser state is preserved such that a subsequent call
|
||||
to the parser will begin reading the message body.
|
||||
|
||||
This could be used by callers to inspect the HTTP
|
||||
header before committing to read the body. For example,
|
||||
to choose the body type based on the fields. Or to
|
||||
respond to an Expect: 100-continue request.
|
||||
*/
|
||||
pause
|
||||
};
|
||||
|
||||
/// The value returned when no content length is known or applicable.
|
||||
static std::uint64_t constexpr no_content_length =
|
||||
(std::numeric_limits<std::uint64_t>::max)();
|
||||
|
||||
/** 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 and uses minimal
|
||||
state. It will handle chunked encoding and it understands the
|
||||
semantics of the Connection and Content-Length header fields.
|
||||
|
||||
The interface uses CRTP (Curiously Recurring Template Pattern).
|
||||
To use this class, derive from basic_parser. When bytes are
|
||||
presented, the implementation will make a series of zero or
|
||||
more calls to derived class members functions (referred to as
|
||||
"callbacks" from here on) 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.
|
||||
|
||||
@code
|
||||
template<bool isRequest>
|
||||
struct exemplar : basic_parser_v1<isRequest, exemplar>
|
||||
{
|
||||
// Called when the first valid octet of a new message is received
|
||||
//
|
||||
void on_start(error_code&);
|
||||
|
||||
// Called for each piece of the Request-Method
|
||||
//
|
||||
void on_method(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the Request-URI
|
||||
//
|
||||
void on_uri(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the reason-phrase
|
||||
//
|
||||
void on_reason(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called after the entire Request-Line has been parsed successfully.
|
||||
//
|
||||
void on_request(error_code&);
|
||||
|
||||
// Called after the entire Response-Line has been parsed successfully.
|
||||
//
|
||||
void on_response(error_code&);
|
||||
|
||||
// Called for each piece of the current header field.
|
||||
//
|
||||
void on_field(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the current header value.
|
||||
//
|
||||
void on_value(boost::string_ref const&, error_code&)
|
||||
|
||||
// Called when the entire header has been parsed successfully.
|
||||
//
|
||||
void
|
||||
on_header(std::uint64_t content_length, error_code&);
|
||||
|
||||
// Called after on_header, before the body is parsed
|
||||
//
|
||||
body_what
|
||||
on_body_what(std::uint64_t content_length, error_code&);
|
||||
|
||||
// Called for each piece of the body.
|
||||
//
|
||||
// If the header indicates chunk encoding, the chunk
|
||||
// encoding is removed from the buffer before being
|
||||
// passed to the callback.
|
||||
//
|
||||
void on_body(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called when the entire message has been parsed successfully.
|
||||
// At this point, @ref complete returns `true`, and the parser
|
||||
// is ready to parse another message if `keep_alive` would
|
||||
// return `true`.
|
||||
//
|
||||
void on_complete(error_code&) {}
|
||||
};
|
||||
@endcode
|
||||
|
||||
The return value of `on_body_what` is special, it controls
|
||||
whether or not the parser should expect a body. See @ref body_what
|
||||
for choices of the return value.
|
||||
|
||||
If a callback sets an error, parsing stops at the current octet
|
||||
and the error is returned to the caller. Callbacks must not throw
|
||||
exceptions.
|
||||
|
||||
@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.
|
||||
*/
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser_v1 : public detail::parser_base
|
||||
{
|
||||
private:
|
||||
template<bool, class>
|
||||
friend class basic_parser_v1;
|
||||
|
||||
using self = basic_parser_v1;
|
||||
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||
|
||||
enum field_state : std::uint8_t
|
||||
{
|
||||
h_general = 0,
|
||||
h_C,
|
||||
h_CO,
|
||||
h_CON,
|
||||
|
||||
h_matching_connection,
|
||||
h_matching_proxy_connection,
|
||||
h_matching_content_length,
|
||||
h_matching_transfer_encoding,
|
||||
h_matching_upgrade,
|
||||
|
||||
h_connection,
|
||||
h_content_length0,
|
||||
h_content_length,
|
||||
h_content_length_ows,
|
||||
h_transfer_encoding,
|
||||
h_upgrade,
|
||||
|
||||
h_matching_transfer_encoding_chunked,
|
||||
h_matching_transfer_encoding_general,
|
||||
h_matching_connection_keep_alive,
|
||||
h_matching_connection_close,
|
||||
h_matching_connection_upgrade,
|
||||
|
||||
h_transfer_encoding_chunked,
|
||||
h_transfer_encoding_chunked_ows,
|
||||
|
||||
h_connection_keep_alive,
|
||||
h_connection_keep_alive_ows,
|
||||
h_connection_close,
|
||||
h_connection_close_ows,
|
||||
h_connection_upgrade,
|
||||
h_connection_upgrade_ows,
|
||||
h_connection_token,
|
||||
h_connection_token_ows
|
||||
};
|
||||
|
||||
std::size_t h_max_;
|
||||
std::size_t h_left_;
|
||||
std::size_t b_max_;
|
||||
std::size_t b_left_;
|
||||
std::uint64_t content_length_;
|
||||
pmf_t cb_;
|
||||
state s_ : 8;
|
||||
unsigned fs_ : 8;
|
||||
unsigned pos_ : 8; // position in field state
|
||||
unsigned http_major_ : 16;
|
||||
unsigned http_minor_ : 16;
|
||||
unsigned status_code_ : 16;
|
||||
unsigned flags_ : 9;
|
||||
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
basic_parser_v1();
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1& operator=(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other);
|
||||
|
||||
/** Set options on the parser.
|
||||
|
||||
@param args One or more parser options to set.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
void
|
||||
set_option(Args&&... args)
|
||||
#else
|
||||
template<class A1, class A2, class... An>
|
||||
void
|
||||
set_option(A1&& a1, A2&& a2, An&&... an)
|
||||
#endif
|
||||
{
|
||||
set_option(std::forward<A1>(a1));
|
||||
set_option(std::forward<A2>(a2),
|
||||
std::forward<An>(an)...);
|
||||
}
|
||||
|
||||
/// Set the header maximum size option
|
||||
void
|
||||
set_option(header_max_size const& o)
|
||||
{
|
||||
h_max_ = o.value;
|
||||
h_left_ = h_max_;
|
||||
}
|
||||
|
||||
/// Set the body maximum size option
|
||||
void
|
||||
set_option(body_max_size const& o)
|
||||
{
|
||||
b_max_ = o.value;
|
||||
b_left_ = b_max_;
|
||||
}
|
||||
|
||||
/// Returns internal flags associated with the parser.
|
||||
unsigned
|
||||
flags() const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message end is indicated by eof.
|
||||
|
||||
This function returns true if the semantics of the message require
|
||||
that the end of the message is signaled by an end of file. For
|
||||
example, if the message is a HTTP/1.0 message and the Content-Length
|
||||
is unspecified, the end of the message is indicated by an end of file.
|
||||
|
||||
@return `true` if write_eof must be used to indicate the message end.
|
||||
*/
|
||||
bool
|
||||
needs_eof() const
|
||||
{
|
||||
return needs_eof(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
/** Returns the major HTTP version number.
|
||||
|
||||
Examples:
|
||||
* Returns 1 for HTTP/1.1
|
||||
* Returns 1 for HTTP/1.0
|
||||
|
||||
@return The HTTP major version number.
|
||||
*/
|
||||
unsigned
|
||||
http_major() const
|
||||
{
|
||||
return http_major_;
|
||||
}
|
||||
|
||||
/** Returns the minor HTTP version number.
|
||||
|
||||
Examples:
|
||||
* Returns 1 for HTTP/1.1
|
||||
* Returns 0 for HTTP/1.0
|
||||
|
||||
@return The HTTP minor version number.
|
||||
*/
|
||||
unsigned
|
||||
http_minor() const
|
||||
{
|
||||
return http_minor_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
A value of `true` indicates that the parser has successfully
|
||||
completed parsing a HTTP upgrade message.
|
||||
|
||||
@return `true` if the message is an upgrade message.
|
||||
*/
|
||||
bool
|
||||
upgrade() const
|
||||
{
|
||||
return upgrade_;
|
||||
}
|
||||
|
||||
/** Returns the numeric HTTP Status-Code of a response.
|
||||
|
||||
@return The Status-Code.
|
||||
*/
|
||||
unsigned
|
||||
status_code() const
|
||||
{
|
||||
return status_code_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the connection should be kept open.
|
||||
|
||||
@note This function is only valid to call when the parser
|
||||
is complete.
|
||||
*/
|
||||
bool
|
||||
keep_alive() const;
|
||||
|
||||
/** Returns `true` if the parse has completed succesfully.
|
||||
|
||||
When the parse has completed successfully, and the semantics
|
||||
of the parsed message indicate that the connection is still
|
||||
active, a subsequent call to `write` will begin parsing a
|
||||
new message.
|
||||
|
||||
@return `true` If the parsing has completed successfully.
|
||||
*/
|
||||
bool
|
||||
complete() const
|
||||
{
|
||||
return
|
||||
s_ == s_restart ||
|
||||
s_ == s_closed_complete ||
|
||||
(flags_ & parse_flag::paused);
|
||||
}
|
||||
|
||||
/** Write a sequence of buffers to the parser.
|
||||
|
||||
@param buffers An object meeting the requirements of
|
||||
ConstBufferSequence that represents the input sequence.
|
||||
|
||||
@param ec Set to the error, if any error occurred.
|
||||
|
||||
@return The number of bytes consumed in the input sequence.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
! std::is_convertible<ConstBufferSequence,
|
||||
boost::asio::const_buffer>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
/** Write a single buffer of data to the parser.
|
||||
|
||||
@param buffer The buffer to write.
|
||||
@param ec Set to the error, if any error occurred.
|
||||
|
||||
@return The number of bytes consumed in the buffer.
|
||||
*/
|
||||
std::size_t
|
||||
write(boost::asio::const_buffer const& buffer, error_code& ec);
|
||||
|
||||
/** Called to indicate the end of file.
|
||||
|
||||
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.
|
||||
|
||||
@note This is typically called when a socket read returns eof.
|
||||
*/
|
||||
void
|
||||
write_eof(error_code& ec);
|
||||
|
||||
protected:
|
||||
/** Reset the parsing state.
|
||||
|
||||
The state of the parser is reset to expect the beginning of
|
||||
a new request or response. The old state is discarded.
|
||||
*/
|
||||
void
|
||||
reset();
|
||||
|
||||
private:
|
||||
Derived&
|
||||
impl()
|
||||
{
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::true_type)
|
||||
{
|
||||
s_ = s_req_start;
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::false_type)
|
||||
{
|
||||
s_ = s_res_start;
|
||||
}
|
||||
|
||||
void
|
||||
init(std::true_type)
|
||||
{
|
||||
// Request: 16KB max header, 4MB max body
|
||||
h_max_ = 16 * 1024;
|
||||
b_max_ = 4 * 1024 * 1024;
|
||||
}
|
||||
|
||||
void
|
||||
init(std::false_type)
|
||||
{
|
||||
// Response: 16KB max header, unlimited body
|
||||
h_max_ = 16 * 1024;
|
||||
b_max_ = 0;
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
init(std::integral_constant<bool, isRequest>{});
|
||||
reset();
|
||||
}
|
||||
|
||||
bool
|
||||
needs_eof(std::true_type) const;
|
||||
|
||||
bool
|
||||
needs_eof(std::false_type) const;
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_start : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_start<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_start(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type { };
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_method : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_method<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_method(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_uri : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_uri<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_uri(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_reason : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_reason<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_reason(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_request : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_request<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_request(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_response : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_response<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_response(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_field : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_field<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_field(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_value : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_value<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_value(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_headers : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_headers<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_header(
|
||||
std::declval<std::uint64_t>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
// VFALCO Can we use std::is_detected? Is C++11 capable?
|
||||
template<class C>
|
||||
class check_on_body_what_t
|
||||
{
|
||||
template<class T, class R = std::is_convertible<decltype(
|
||||
std::declval<T>().on_body_what(
|
||||
std::declval<std::uint64_t>(),
|
||||
std::declval<error_code&>())),
|
||||
body_what>>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using check_on_body_what =
|
||||
std::integral_constant<bool, check_on_body_what_t<C>::value>;
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_body : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_body<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_body(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_complete : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_complete<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_complete(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
void call_on_start(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_start<Derived>::value,
|
||||
"on_start requirements not met");
|
||||
impl().on_start(ec);
|
||||
}
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_method<Derived>::value,
|
||||
"on_method requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_method(s, ec);
|
||||
}
|
||||
|
||||
void call_on_method(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
call_on_method(ec, s,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_uri(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_uri<Derived>::value,
|
||||
"on_uri requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_uri(s, ec);
|
||||
}
|
||||
|
||||
void call_on_uri(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_uri(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
call_on_uri(ec, s,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_reason(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_reason<Derived>::value,
|
||||
"on_reason requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_reason(s, ec);
|
||||
}
|
||||
|
||||
void call_on_reason(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
||||
{
|
||||
call_on_reason(ec, s,
|
||||
std::integral_constant<bool, ! isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_request(error_code& ec, std::true_type)
|
||||
{
|
||||
static_assert(check_on_request<Derived>::value,
|
||||
"on_request requirements not met");
|
||||
impl().on_request(ec);
|
||||
}
|
||||
|
||||
void call_on_request(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_request(error_code& ec)
|
||||
{
|
||||
call_on_request(ec,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_response(error_code& ec, std::true_type)
|
||||
{
|
||||
static_assert(check_on_response<Derived>::value,
|
||||
"on_response requirements not met");
|
||||
impl().on_response(ec);
|
||||
}
|
||||
|
||||
void call_on_response(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_response(error_code& ec)
|
||||
{
|
||||
call_on_response(ec,
|
||||
std::integral_constant<bool, ! isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_field(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_field<Derived>::value,
|
||||
"on_field requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_field(s, ec);
|
||||
}
|
||||
|
||||
void call_on_value(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_value<Derived>::value,
|
||||
"on_value requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_value(s, ec);
|
||||
}
|
||||
|
||||
void
|
||||
call_on_headers(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_headers<Derived>::value,
|
||||
"on_header requirements not met");
|
||||
impl().on_header(content_length_, ec);
|
||||
}
|
||||
|
||||
body_what
|
||||
call_on_body_what(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_body_what<Derived>::value,
|
||||
"on_body_what requirements not met");
|
||||
return impl().on_body_what(content_length_, ec);
|
||||
}
|
||||
|
||||
void call_on_body(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_body<Derived>::value,
|
||||
"on_body requirements not met");
|
||||
if(b_max_ && s.size() > b_left_)
|
||||
{
|
||||
ec = parse_error::body_too_big;
|
||||
return;
|
||||
}
|
||||
b_left_ -= s.size();
|
||||
impl().on_body(s, ec);
|
||||
}
|
||||
|
||||
void call_on_complete(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_complete<Derived>::value,
|
||||
"on_complete requirements not met");
|
||||
impl().on_complete(ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_parser_v1.ipp>
|
||||
|
||||
#endif
|
||||
75
include/beast/http/chunk_encode.hpp
Normal file
75
include/beast/http/chunk_encode.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
#define BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/http/detail/chunk_encode.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Returns a chunk-encoded ConstBufferSequence.
|
||||
|
||||
This returns a buffer sequence representing the
|
||||
first chunk of a chunked transfer coded body.
|
||||
|
||||
@param fin `true` if this is the last chunk.
|
||||
|
||||
@param buffers The input buffer sequence.
|
||||
|
||||
@return A chunk-encoded ConstBufferSequence representing the input.
|
||||
|
||||
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
beast::detail::buffer_cat_helper<
|
||||
detail::chunk_encode_delim,
|
||||
ConstBufferSequence,
|
||||
boost::asio::const_buffers_1>
|
||||
#endif
|
||||
chunk_encode(bool fin, ConstBufferSequence const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
return buffer_cat(
|
||||
detail::chunk_encode_delim{buffer_size(buffers)},
|
||||
buffers,
|
||||
fin ? boost::asio::const_buffers_1{"\r\n0\r\n\r\n", 7}
|
||||
: boost::asio::const_buffers_1{"\r\n", 2});
|
||||
}
|
||||
|
||||
/** Returns a chunked encoding final chunk.
|
||||
|
||||
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
||||
*/
|
||||
inline
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
boost::asio::const_buffers_1
|
||||
#endif
|
||||
chunk_encode_final()
|
||||
{
|
||||
return boost::asio::const_buffers_1{"0\r\n\r\n", 5};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
231
include/beast/http/concepts.hpp
Normal file
231
include/beast/http/concepts.hpp
Normal file
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_TYPE_CHECK_HPP
|
||||
#define BEAST_HTTP_TYPE_CHECK_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct write_function
|
||||
{
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(ConstBufferSequence const&);
|
||||
};
|
||||
|
||||
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 {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_content_length : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_content_length<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().content_length()
|
||||
)> > : std::true_type
|
||||
{
|
||||
static_assert(std::is_convertible<
|
||||
decltype(std::declval<T>().content_length()),
|
||||
std::uint64_t>::value,
|
||||
"Writer::content_length requirements not met");
|
||||
};
|
||||
|
||||
template<class T, class M>
|
||||
class is_Writer
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().init(std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
// VFALCO This is unfortunate, we have to provide the template
|
||||
// argument type because this is not a deduced context?
|
||||
//
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().template write<detail::write_function>(
|
||||
std::declval<error_code&>(),
|
||||
std::declval<detail::write_function>()))
|
||||
, bool>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
public:
|
||||
static_assert(std::is_same<
|
||||
typename M::body_type::writer, T>::value,
|
||||
"Mismatched writer and message");
|
||||
|
||||
using type = std::integral_constant<bool,
|
||||
std::is_nothrow_constructible<T, M const&>::value
|
||||
&& type1::value
|
||||
&& type2::value
|
||||
>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_Parser
|
||||
{
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().complete()),
|
||||
bool>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().write(
|
||||
std::declval<boost::asio::const_buffers_1 const&>(),
|
||||
std::declval<error_code&>())),
|
||||
std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().write_eof(std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value &&
|
||||
type2::value &&
|
||||
type3::value
|
||||
>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/// Determine if `T` meets the requirements of @b Body.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_Body : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_Body = detail::has_value_type<T>;
|
||||
#endif
|
||||
|
||||
/** Determine if a @b Body has a nested type `reader`.
|
||||
|
||||
@tparam T The type to check, which must meet the
|
||||
requirements of @b Body.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class T>
|
||||
struct has_reader : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_reader : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_reader<T, beast::detail::void_t<
|
||||
typename T::reader
|
||||
> > : std::true_type {};
|
||||
#endif
|
||||
|
||||
/** Determine if a @b Body has a nested type `writer`.
|
||||
|
||||
@tparam T The type to check, which must meet the
|
||||
requirements of @b Body.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class T>
|
||||
struct has_writer : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_writer : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_writer<T, beast::detail::void_t<
|
||||
typename T::writer
|
||||
> > : std::true_type {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b Reader for `M`.
|
||||
|
||||
@tparam T The type to test.
|
||||
|
||||
@tparam M The message type to test with, which must be of
|
||||
type `message`.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class T, class M>
|
||||
struct is_Reader : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
template<class T, class M, class = beast::detail::void_t<>>
|
||||
struct is_Reader : std::false_type {};
|
||||
|
||||
template<class T, class M>
|
||||
struct is_Reader<T, M, beast::detail::void_t<decltype(
|
||||
std::declval<T>().init(
|
||||
std::declval<error_code&>()),
|
||||
std::declval<T>().write(
|
||||
std::declval<void const*>(),
|
||||
std::declval<std::size_t>(),
|
||||
std::declval<error_code&>())
|
||||
)> > : std::integral_constant<bool,
|
||||
std::is_nothrow_constructible<T, M&>::value
|
||||
>
|
||||
{
|
||||
static_assert(std::is_same<
|
||||
typename M::body_type::reader, T>::value,
|
||||
"Mismatched reader and message");
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b Writer for `M`.
|
||||
|
||||
@tparam T The type to test.
|
||||
|
||||
@tparam M The message type to test with, which must be of
|
||||
type `message`.
|
||||
*/
|
||||
template<class T, class M>
|
||||
#if GENERATING_DOCS
|
||||
struct is_Writer : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
using is_Writer = typename detail::is_Writer<T, M>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b Parser.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_Parser : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_Parser = typename detail::is_Parser<T>::type;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
214
include/beast/http/detail/basic_fields.hpp
Normal file
214
include/beast/http/detail/basic_fields.hpp
Normal file
@@ -0,0 +1,214 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
|
||||
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Allocator>
|
||||
class basic_fields;
|
||||
|
||||
namespace detail {
|
||||
|
||||
class basic_fields_base
|
||||
{
|
||||
public:
|
||||
struct value_type
|
||||
{
|
||||
std::string first;
|
||||
std::string second;
|
||||
|
||||
value_type(boost::string_ref const& name_,
|
||||
boost::string_ref const& value_)
|
||||
: first(name_)
|
||||
, second(value_)
|
||||
{
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
name() const
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
value() const
|
||||
{
|
||||
return second;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_fields;
|
||||
|
||||
struct element
|
||||
: boost::intrusive::set_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
, boost::intrusive::list_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
{
|
||||
value_type data;
|
||||
|
||||
element(boost::string_ref const& name,
|
||||
boost::string_ref const& value)
|
||||
: data(name, value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct less : private beast::detail::ci_less
|
||||
{
|
||||
template<class String>
|
||||
bool
|
||||
operator()(String const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs, rhs.data.first);
|
||||
}
|
||||
|
||||
template<class String>
|
||||
bool
|
||||
operator()(element const& lhs, String const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs.data.first, rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(
|
||||
lhs.data.first, rhs.data.first);
|
||||
}
|
||||
};
|
||||
|
||||
using list_t = boost::intrusive::make_list<element,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using set_t = boost::intrusive::make_multiset<element,
|
||||
boost::intrusive::constant_time_size<true>,
|
||||
boost::intrusive::compare<less>>::type;
|
||||
|
||||
// data
|
||||
set_t set_;
|
||||
list_t list_;
|
||||
|
||||
basic_fields_base(set_t&& set, list_t&& list)
|
||||
: set_(std::move(set))
|
||||
, list_(std::move(list))
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
class const_iterator;
|
||||
|
||||
using iterator = const_iterator;
|
||||
|
||||
basic_fields_base() = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class basic_fields_base::const_iterator
|
||||
{
|
||||
using iter_type = list_t::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_fields;
|
||||
|
||||
friend class basic_fields_base;
|
||||
|
||||
const_iterator(iter_type it)
|
||||
: it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename basic_fields_base::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
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 it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return it_->data;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
146
include/beast/http/detail/basic_parser_v1.hpp
Normal file
146
include/beast/http/detail/basic_parser_v1.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
struct parser_str_t
|
||||
{
|
||||
static char constexpr close[6] = "close";
|
||||
static char constexpr chunked[8] = "chunked";
|
||||
static char constexpr keep_alive[11] = "keep-alive";
|
||||
|
||||
static char constexpr upgrade[8] = "upgrade";
|
||||
static char constexpr connection[11] = "connection";
|
||||
static char constexpr content_length[15] = "content-length";
|
||||
static char constexpr proxy_connection[17] = "proxy-connection";
|
||||
static char constexpr transfer_encoding[18] = "transfer-encoding";
|
||||
};
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::close[6];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::chunked[8];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::keep_alive[11];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::upgrade[8];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::connection[11];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::content_length[15];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::proxy_connection[17];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::transfer_encoding[18];
|
||||
|
||||
using parser_str = parser_str_t<>;
|
||||
|
||||
class parser_base
|
||||
{
|
||||
protected:
|
||||
enum state : std::uint8_t
|
||||
{
|
||||
s_dead = 1,
|
||||
|
||||
s_req_start,
|
||||
s_req_method0,
|
||||
s_req_method,
|
||||
s_req_url0,
|
||||
s_req_url,
|
||||
s_req_http,
|
||||
s_req_http_H,
|
||||
s_req_http_HT,
|
||||
s_req_http_HTT,
|
||||
s_req_http_HTTP,
|
||||
s_req_major,
|
||||
s_req_dot,
|
||||
s_req_minor,
|
||||
s_req_cr,
|
||||
s_req_lf,
|
||||
|
||||
s_res_start,
|
||||
s_res_H,
|
||||
s_res_HT,
|
||||
s_res_HTT,
|
||||
s_res_HTTP,
|
||||
s_res_major,
|
||||
s_res_dot,
|
||||
s_res_minor,
|
||||
s_res_space_1,
|
||||
s_res_status0,
|
||||
s_res_status1,
|
||||
s_res_status2,
|
||||
s_res_space_2,
|
||||
s_res_reason0,
|
||||
s_res_reason,
|
||||
s_res_line_lf,
|
||||
s_res_line_done,
|
||||
|
||||
s_header_name0,
|
||||
s_header_name,
|
||||
s_header_value0_lf,
|
||||
s_header_value0_almost_done,
|
||||
s_header_value0,
|
||||
s_header_value,
|
||||
s_header_value_lf,
|
||||
s_header_value_almost_done,
|
||||
s_header_value_unfold,
|
||||
|
||||
s_headers_almost_done,
|
||||
s_headers_done,
|
||||
|
||||
s_chunk_size0,
|
||||
s_chunk_size,
|
||||
s_chunk_ext_name0,
|
||||
s_chunk_ext_name,
|
||||
s_chunk_ext_val,
|
||||
s_chunk_size_lf,
|
||||
s_chunk_data0,
|
||||
s_chunk_data,
|
||||
s_chunk_data_cr,
|
||||
s_chunk_data_lf,
|
||||
|
||||
s_body_pause,
|
||||
s_body_identity0,
|
||||
s_body_identity,
|
||||
s_body_identity_eof0,
|
||||
s_body_identity_eof,
|
||||
|
||||
s_complete,
|
||||
s_restart,
|
||||
s_closed_complete
|
||||
};
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
111
include/beast/http/detail/chunk_encode.hpp
Normal file
111
include/beast/http/detail/chunk_encode.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
||||
#define BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class chunk_encode_delim
|
||||
{
|
||||
boost::asio::const_buffer cb_;
|
||||
|
||||
// Storage for the longest hex string we might need, plus delimiters.
|
||||
std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
copy(chunk_encode_delim const& other);
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
setup(std::size_t n);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_buffer;
|
||||
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
chunk_encode_delim(chunk_encode_delim const& other)
|
||||
{
|
||||
copy(other);
|
||||
}
|
||||
|
||||
explicit
|
||||
chunk_encode_delim(std::size_t n)
|
||||
{
|
||||
setup(n);
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return &cb_;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
};
|
||||
|
||||
template<class>
|
||||
void
|
||||
chunk_encode_delim::
|
||||
copy(chunk_encode_delim const& other)
|
||||
{
|
||||
auto const n =
|
||||
boost::asio::buffer_size(other.cb_);
|
||||
buf_ = other.buf_;
|
||||
cb_ = boost::asio::const_buffer(
|
||||
&buf_[buf_.size() - n], n);
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
chunk_encode_delim::
|
||||
setup(std::size_t n)
|
||||
{
|
||||
buf_[buf_.size() - 2] = '\r';
|
||||
buf_[buf_.size() - 1] = '\n';
|
||||
auto it = to_hex(buf_.end() - 2, n);
|
||||
cb_ = boost::asio::const_buffer{&*it,
|
||||
static_cast<std::size_t>(
|
||||
std::distance(it, buf_.end()))};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
411
include/beast/http/detail/rfc7230.hpp
Normal file
411
include/beast/http/detail/rfc7230.hpp
Normal file
@@ -0,0 +1,411 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||
#define BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
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
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(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
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
char
|
||||
is_tchar(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
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(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
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(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
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
// returns 0 if not a valid token char
|
||||
//
|
||||
inline
|
||||
char
|
||||
to_field_char(char c)
|
||||
{
|
||||
/* token = 1*<any CHAR except CTLs or separators>
|
||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
sep = "(" | ")" | "<" | ">" | "@"
|
||||
| "," | ";" | ":" | "\" | <">
|
||||
| "/" | "[" | "]" | "?" | "="
|
||||
| "{" | "}" | SP | HT
|
||||
*/
|
||||
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, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
|
||||
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
|
||||
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(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
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||
}
|
||||
|
||||
inline
|
||||
std::int8_t
|
||||
unhex(char c)
|
||||
{
|
||||
static 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
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
void
|
||||
skip_ows(FwdIt& it, FwdIt const& end)
|
||||
{
|
||||
while(it != end)
|
||||
{
|
||||
auto const c = *it;
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
void
|
||||
skip_token(FwdIt& it, FwdIt const& last)
|
||||
{
|
||||
while(it != last && is_tchar(*it))
|
||||
++it;
|
||||
}
|
||||
|
||||
inline
|
||||
boost::string_ref
|
||||
trim(boost::string_ref const& 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 = boost::string_ref::const_iterator;
|
||||
|
||||
iter_type it;
|
||||
iter_type first;
|
||||
iter_type last;
|
||||
std::pair<boost::string_ref, boost::string_ref> 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_tchar(*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_tchar(*it))
|
||||
return err();
|
||||
auto const p2 = it;
|
||||
skip_token(++it, last);
|
||||
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
73
include/beast/http/empty_body.hpp
Normal file
73
include/beast/http/empty_body.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_EMPTY_BODY_HPP
|
||||
#define BEAST_HTTP_EMPTY_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** An empty content-body.
|
||||
|
||||
Meets the requirements of @b `Body`.
|
||||
*/
|
||||
struct empty_body
|
||||
{
|
||||
#if GENERATING_DOCS
|
||||
/// The type of the `message::body` member
|
||||
using value_type = void;
|
||||
#else
|
||||
struct value_type {};
|
||||
#endif
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
|
||||
struct writer
|
||||
{
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, empty_body, Fields> const& m) noexcept
|
||||
{
|
||||
beast::detail::ignore_unused(m);
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec) noexcept
|
||||
{
|
||||
beast::detail::ignore_unused(ec);
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
content_length() const noexcept
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class WriteFunction>
|
||||
bool
|
||||
write(error_code&, WriteFunction&& wf) noexcept
|
||||
{
|
||||
wf(boost::asio::null_buffers{});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
25
include/beast/http/fields.hpp
Normal file
25
include/beast/http/fields.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_FIELDS_HPP
|
||||
#define BEAST_HTTP_FIELDS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/basic_fields.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// A typical HTTP header fields container
|
||||
using fields =
|
||||
basic_fields<std::allocator<char>>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
233
include/beast/http/header_parser_v1.hpp
Normal file
233
include/beast/http/header_parser_v1.hpp
Normal file
@@ -0,0 +1,233 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct request_parser_base
|
||||
{
|
||||
std::string method_;
|
||||
std::string uri_;
|
||||
};
|
||||
|
||||
struct response_parser_base
|
||||
{
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** A parser for a HTTP/1 request or response header.
|
||||
|
||||
This class uses the HTTP/1 wire format parser to
|
||||
convert a series of octets into a request or
|
||||
response @ref header.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
class header_parser_v1
|
||||
: public basic_parser_v1<isRequest,
|
||||
header_parser_v1<isRequest, Fields>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::request_parser_base,
|
||||
detail::response_parser_base>::type
|
||||
{
|
||||
public:
|
||||
/// The type of the header this parser produces.
|
||||
using header_type = header<isRequest, Fields>;
|
||||
|
||||
private:
|
||||
// VFALCO Check Fields requirements?
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
header_type h_;
|
||||
bool flush_ = false;
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
header_parser_v1() = default;
|
||||
|
||||
/// Move constructor
|
||||
header_parser_v1(header_parser_v1&&) = default;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
header_parser_v1(header_parser_v1 const&) = delete;
|
||||
|
||||
/// Move assignment (disallowed)
|
||||
header_parser_v1& operator=(header_parser_v1&&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
header_parser_v1& operator=(header_parser_v1 const&) = delete;
|
||||
|
||||
/** Construct the parser.
|
||||
|
||||
@param args Forwarded to the header constructor.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
explicit
|
||||
header_parser_v1(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<! std::is_same<
|
||||
typename std::decay<Arg1>::type, header_parser_v1>::value>>
|
||||
explicit
|
||||
header_parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||
: h_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Returns the parsed header
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
header_type const&
|
||||
get() const
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns the parsed header.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
header_type&
|
||||
get()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed header.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref complete would return `true`.
|
||||
|
||||
Requires:
|
||||
@ref header_type is @b MoveConstructible
|
||||
*/
|
||||
header_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(h_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(h_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser_v1<isRequest, header_parser_v1>;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(! flush_)
|
||||
return;
|
||||
flush_ = false;
|
||||
BOOST_ASSERT(! field_.empty());
|
||||
h_.fields.insert(field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
|
||||
void on_start(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_uri(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->uri_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_reason(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_request_or_response(std::true_type)
|
||||
{
|
||||
h_.method = std::move(this->method_);
|
||||
h_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void on_request_or_response(std::false_type)
|
||||
{
|
||||
h_.status = this->status_code();
|
||||
h_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
field_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_value(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
value_.append(s.data(), s.size());
|
||||
flush_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
on_header(std::uint64_t, error_code&)
|
||||
{
|
||||
flush();
|
||||
h_.version = 10 * this->http_major() + this->http_minor();
|
||||
}
|
||||
|
||||
body_what
|
||||
on_body_what(std::uint64_t, error_code&)
|
||||
{
|
||||
return body_what::pause;
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
266
include/beast/http/impl/basic_fields.ipp
Normal file
266
include/beast/http/impl/basic_fields.ipp
Normal file
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
|
||||
#define BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
|
||||
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
delete_all()
|
||||
{
|
||||
for(auto it = list_.begin(); it != list_.end();)
|
||||
{
|
||||
auto& e = *it++;
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(
|
||||
this->member(), &e, 1);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
move_assign(basic_fields& other, std::false_type)
|
||||
{
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
copy_from(other);
|
||||
other.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
set_ = std::move(other.set_);
|
||||
list_ = std::move(other.list_);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
move_assign(basic_fields& other, std::true_type)
|
||||
{
|
||||
this->member() = std::move(other.member());
|
||||
set_ = std::move(other.set_);
|
||||
list_ = std::move(other.list_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
copy_assign(basic_fields const& other, std::false_type)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
copy_assign(basic_fields const& other, std::true_type)
|
||||
{
|
||||
this->member() = other.member();
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::
|
||||
~basic_fields()
|
||||
{
|
||||
delete_all();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(Allocator const& alloc)
|
||||
: beast::detail::empty_base_optimization<
|
||||
alloc_type>(alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(basic_fields&& other)
|
||||
: beast::detail::empty_base_optimization<alloc_type>(
|
||||
std::move(other.member()))
|
||||
, detail::basic_fields_base(
|
||||
std::move(other.set_), std::move(other.list_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_fields<Allocator>::
|
||||
operator=(basic_fields&& other) ->
|
||||
basic_fields&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
clear();
|
||||
move_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_move_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(basic_fields const& other)
|
||||
: basic_fields(alloc_traits::
|
||||
select_on_container_copy_construction(other.member()))
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_fields<Allocator>::
|
||||
operator=(basic_fields const& other) ->
|
||||
basic_fields&
|
||||
{
|
||||
clear();
|
||||
copy_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_copy_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(basic_fields<OtherAlloc> const& other)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_fields<Allocator>::
|
||||
operator=(basic_fields<OtherAlloc> const& other) ->
|
||||
basic_fields&
|
||||
{
|
||||
clear();
|
||||
copy_from(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class FwdIt>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(FwdIt first, FwdIt last)
|
||||
{
|
||||
for(;first != last; ++first)
|
||||
insert(first->name(), first->value());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_fields<Allocator>::
|
||||
count(boost::string_ref const& name) const
|
||||
{
|
||||
auto const it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return 0;
|
||||
auto const last = set_.upper_bound(name, less{});
|
||||
return static_cast<std::size_t>(std::distance(it, last));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_fields<Allocator>::
|
||||
find(boost::string_ref const& name) const ->
|
||||
iterator
|
||||
{
|
||||
auto const it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return list_.end();
|
||||
return list_.iterator_to(*it);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
boost::string_ref
|
||||
basic_fields<Allocator>::
|
||||
operator[](boost::string_ref const& name) const
|
||||
{
|
||||
auto const it = find(name);
|
||||
if(it == end())
|
||||
return {};
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
clear() noexcept
|
||||
{
|
||||
delete_all();
|
||||
list_.clear();
|
||||
set_.clear();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_fields<Allocator>::
|
||||
erase(boost::string_ref const& name)
|
||||
{
|
||||
auto it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return 0;
|
||||
auto const last = set_.upper_bound(name, less{});
|
||||
std::size_t n = 1;
|
||||
for(;;)
|
||||
{
|
||||
auto& e = *it++;
|
||||
set_.erase(set_.iterator_to(e));
|
||||
list_.erase(list_.iterator_to(e));
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(), &e, 1);
|
||||
if(it == last)
|
||||
break;
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
insert(boost::string_ref const& name,
|
||||
boost::string_ref value)
|
||||
{
|
||||
value = detail::trim(value);
|
||||
auto const p = alloc_traits::allocate(this->member(), 1);
|
||||
alloc_traits::construct(this->member(), p, name, value);
|
||||
set_.insert_before(set_.upper_bound(name, less{}), *p);
|
||||
list_.push_back(*p);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
replace(boost::string_ref const& name,
|
||||
boost::string_ref value)
|
||||
{
|
||||
value = detail::trim(value);
|
||||
erase(name);
|
||||
insert(name, value);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
1289
include/beast/http/impl/basic_parser_v1.ipp
Normal file
1289
include/beast/http/impl/basic_parser_v1.ipp
Normal file
File diff suppressed because it is too large
Load Diff
266
include/beast/http/impl/message.ipp
Normal file
266
include/beast/http/impl/message.ipp
Normal file
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
#define BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<true, Fields>& m1,
|
||||
header<true, Fields>& m2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(m1.version, m2.version);
|
||||
swap(m1.method, m2.method);
|
||||
swap(m1.url, m2.url);
|
||||
swap(m1.fields, m2.fields);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<false, Fields>& a,
|
||||
header<false, Fields>& b)
|
||||
{
|
||||
using std::swap;
|
||||
swap(a.version, b.version);
|
||||
swap(a.status, b.status);
|
||||
swap(a.reason, b.reason);
|
||||
swap(a.fields, b.fields);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
swap(
|
||||
message<isRequest, Body, Fields>& m1,
|
||||
message<isRequest, Body, Fields>& m2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(m1.base(), m2.base());
|
||||
swap(m1.body, m2.body);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
bool
|
||||
is_keep_alive(header<isRequest, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
if(msg.version == 11)
|
||||
{
|
||||
if(token_list{msg.fields["Connection"]}.exists("close"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if(token_list{msg.fields["Connection"]}.exists("keep-alive"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
bool
|
||||
is_upgrade(header<isRequest, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
if(msg.version == 10)
|
||||
return false;
|
||||
if(token_list{msg.fields["Connection"]}.exists("upgrade"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct prepare_info
|
||||
{
|
||||
boost::optional<connection> connection_value;
|
||||
boost::optional<std::uint64_t> content_length;
|
||||
};
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
inline
|
||||
void
|
||||
prepare_options(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg)
|
||||
{
|
||||
beast::detail::ignore_unused(pi, msg);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_option(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
connection value)
|
||||
{
|
||||
beast::detail::ignore_unused(msg);
|
||||
pi.connection_value = value;
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Opt, class... Opts>
|
||||
void
|
||||
prepare_options(prepare_info& pi,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
Opt&& opt, Opts&&... opts)
|
||||
{
|
||||
prepare_option(pi, msg, opt);
|
||||
prepare_options(pi, msg,
|
||||
std::forward<Opts>(opts)...);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_content_length(prepare_info& pi,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
std::true_type)
|
||||
{
|
||||
typename Body::writer w(msg);
|
||||
// VFALCO This is a design problem!
|
||||
error_code ec;
|
||||
w.init(ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
pi.content_length = w.content_length();
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
prepare_content_length(prepare_info& pi,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
std::false_type)
|
||||
{
|
||||
beast::detail::ignore_unused(msg);
|
||||
pi.content_length = boost::none;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class... Options>
|
||||
void
|
||||
prepare(message<isRequest, Body, Fields>& msg,
|
||||
Options&&... options)
|
||||
{
|
||||
using beast::detail::make_exception;
|
||||
|
||||
// VFALCO TODO
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_writer<Body>::value,
|
||||
"Body has no writer");
|
||||
static_assert(is_Writer<typename Body::writer,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Writer requirements not met");
|
||||
detail::prepare_info pi;
|
||||
detail::prepare_content_length(pi, msg,
|
||||
detail::has_content_length<typename Body::writer>{});
|
||||
detail::prepare_options(pi, msg,
|
||||
std::forward<Options>(options)...);
|
||||
|
||||
if(msg.fields.exists("Connection"))
|
||||
throw make_exception<std::invalid_argument>(
|
||||
"prepare called with Connection field set", __FILE__, __LINE__);
|
||||
|
||||
if(msg.fields.exists("Content-Length"))
|
||||
throw make_exception<std::invalid_argument>(
|
||||
"prepare called with Content-Length field set", __FILE__, __LINE__);
|
||||
|
||||
if(token_list{msg.fields["Transfer-Encoding"]}.exists("chunked"))
|
||||
throw make_exception<std::invalid_argument>(
|
||||
"prepare called with Transfer-Encoding: chunked set", __FILE__, __LINE__);
|
||||
|
||||
if(pi.connection_value != connection::upgrade)
|
||||
{
|
||||
if(pi.content_length)
|
||||
{
|
||||
struct set_field
|
||||
{
|
||||
void
|
||||
operator()(message<true, Body, Fields>& msg,
|
||||
detail::prepare_info const& pi) const
|
||||
{
|
||||
using beast::detail::ci_equal;
|
||||
if(*pi.content_length > 0 ||
|
||||
ci_equal(msg.method, "POST"))
|
||||
{
|
||||
msg.fields.insert(
|
||||
"Content-Length", *pi.content_length);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
operator()(message<false, Body, Fields>& msg,
|
||||
detail::prepare_info const& pi) const
|
||||
{
|
||||
if((msg.status / 100 ) != 1 &&
|
||||
msg.status != 204 &&
|
||||
msg.status != 304)
|
||||
{
|
||||
msg.fields.insert(
|
||||
"Content-Length", *pi.content_length);
|
||||
}
|
||||
}
|
||||
};
|
||||
set_field{}(msg, pi);
|
||||
}
|
||||
else if(msg.version >= 11)
|
||||
{
|
||||
msg.fields.insert("Transfer-Encoding", "chunked");
|
||||
}
|
||||
}
|
||||
|
||||
auto const content_length =
|
||||
msg.fields.exists("Content-Length");
|
||||
|
||||
if(pi.connection_value)
|
||||
{
|
||||
switch(*pi.connection_value)
|
||||
{
|
||||
case connection::upgrade:
|
||||
msg.fields.insert("Connection", "upgrade");
|
||||
break;
|
||||
|
||||
case connection::keep_alive:
|
||||
if(msg.version < 11)
|
||||
{
|
||||
if(content_length)
|
||||
msg.fields.insert("Connection", "keep-alive");
|
||||
}
|
||||
break;
|
||||
|
||||
case connection::close:
|
||||
if(msg.version >= 11)
|
||||
msg.fields.insert("Connection", "close");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rfc7230 6.7.
|
||||
if(msg.version < 11 && token_list{
|
||||
msg.fields["Connection"]}.exists("upgrade"))
|
||||
throw make_exception<std::invalid_argument>(
|
||||
"invalid version for Connection: upgrade", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
302
include/beast/http/impl/parse.ipp
Normal file
302
include/beast/http/impl/parse.ipp
Normal file
@@ -0,0 +1,302 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_PARSE_IPP_HPP
|
||||
#define BEAST_HTTP_IMPL_PARSE_IPP_HPP
|
||||
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream,
|
||||
class DynamicBuffer, class Parser, class Handler>
|
||||
class parse_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
Parser& p;
|
||||
bool got_some = false;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& sb_, Parser& p_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, p(p_)
|
||||
{
|
||||
BOOST_ASSERT(! p.complete());
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
parse_op(parse_op&&) = default;
|
||||
parse_op(parse_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
parse_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, parse_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, parse_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(parse_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, parse_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream,
|
||||
class DynamicBuffer, class Parser, class Handler>
|
||||
void
|
||||
parse_op<Stream, DynamicBuffer, Parser, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Parse any bytes left over in the buffer
|
||||
auto const used =
|
||||
d.p.write(d.db.data(), ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
}
|
||||
if(used > 0)
|
||||
{
|
||||
d.got_some = true;
|
||||
d.db.consume(used);
|
||||
}
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
}
|
||||
// Buffer must be empty,
|
||||
// otherwise parse should be complete
|
||||
BOOST_ASSERT(d.db.size() == 0);
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
// read
|
||||
d.state = 2;
|
||||
auto const size =
|
||||
read_size_helper(d.db, 65536);
|
||||
BOOST_ASSERT(size > 0);
|
||||
d.s.async_read_some(
|
||||
d.db.prepare(size), std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
// got data
|
||||
case 2:
|
||||
{
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
// If we haven't processed any bytes,
|
||||
// give the eof to the handler immediately.
|
||||
if(! d.got_some)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// Feed the eof to the parser to complete
|
||||
// the parse, and call the handler. The
|
||||
// next call to parse will deliver the eof.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
BOOST_ASSERT(ec || d.p.complete());
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
BOOST_ASSERT(bytes_transferred > 0);
|
||||
d.db.commit(bytes_transferred);
|
||||
auto const used = d.p.write(d.db.data(), ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// The parser must either consume
|
||||
// bytes or generate an error.
|
||||
BOOST_ASSERT(used > 0);
|
||||
d.got_some = true;
|
||||
d.db.consume(used);
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// If the parse is not complete,
|
||||
// all input must be consumed.
|
||||
BOOST_ASSERT(used == bytes_transferred);
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Parser<Parser>::value,
|
||||
"Parser requirements not met");
|
||||
error_code ec;
|
||||
parse(stream, dynabuf, parser, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
Parser& parser, error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Parser<Parser>::value,
|
||||
"Parser requirements not met");
|
||||
bool got_some = false;
|
||||
for(;;)
|
||||
{
|
||||
auto used =
|
||||
parser.write(dynabuf.data(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
dynabuf.consume(used);
|
||||
if(used > 0)
|
||||
got_some = true;
|
||||
if(parser.complete())
|
||||
break;
|
||||
dynabuf.commit(stream.read_some(
|
||||
dynabuf.prepare(read_size_helper(
|
||||
dynabuf, 65536)), ec));
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
return;
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! got_some)
|
||||
return;
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
parser.write_eof(ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(parser.complete());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class AsyncReadStream,
|
||||
class DynamicBuffer, class Parser, class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_parse(AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser, ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Parser<Parser>::value,
|
||||
"Parser requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::parse_op<AsyncReadStream, DynamicBuffer,
|
||||
Parser, decltype(completion.handler)>{
|
||||
completion.handler, stream, dynabuf, parser};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
105
include/beast/http/impl/parse_error.ipp
Normal file
105
include/beast/http/impl/parse_error.ipp
Normal file
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_PARSE_ERROR_IPP
|
||||
#define BEAST_HTTP_IMPL_PARSE_ERROR_IPP
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::http::parse_error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class parse_error_category : public error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "http";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<parse_error>(ev))
|
||||
{
|
||||
case parse_error::connection_closed: return "data after Connection close";
|
||||
case parse_error::bad_method: return "bad method";
|
||||
case parse_error::bad_uri: return "bad request-target";
|
||||
case parse_error::bad_version: return "bad HTTP-Version";
|
||||
case parse_error::bad_crlf: return "missing CRLF";
|
||||
case parse_error::bad_status: return "bad status-code";
|
||||
case parse_error::bad_reason: return "bad reason-phrase";
|
||||
case parse_error::bad_field: return "bad field token";
|
||||
case parse_error::bad_value: return "bad field-value";
|
||||
case parse_error::bad_content_length: return "bad Content-Length";
|
||||
case parse_error::illegal_content_length: return "illegal Content-Length with chunked Transfer-Encoding";
|
||||
case parse_error::invalid_chunk_size: return "invalid chunk size";
|
||||
case parse_error::invalid_ext_name: return "invalid ext name";
|
||||
case parse_error::invalid_ext_val: return "invalid ext val";
|
||||
case parse_error::header_too_big: return "header size limit exceeded";
|
||||
case parse_error::body_too_big: return "body size limit exceeded";
|
||||
default:
|
||||
case parse_error::short_read: return "unexpected end of data";
|
||||
}
|
||||
}
|
||||
|
||||
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_parse_error_category()
|
||||
{
|
||||
static parse_error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_error_code(parse_error ev)
|
||||
{
|
||||
return error_code{
|
||||
static_cast<std::underlying_type<parse_error>::type>(ev),
|
||||
detail::get_parse_error_category()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
389
include/beast/http/impl/read.ipp
Normal file
389
include/beast/http/impl/read.ipp
Normal file
@@ -0,0 +1,389 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/header_parser_v1.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Fields,
|
||||
class Handler>
|
||||
class read_header_op
|
||||
{
|
||||
using parser_type =
|
||||
header_parser_v1<isRequest, Fields>;
|
||||
|
||||
using message_type =
|
||||
header<isRequest, Fields>;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
bool started = false;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& sb_, message_type& m_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, m(m_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_header_op(read_header_op&&) = default;
|
||||
read_header_op(read_header_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_header_op(
|
||||
DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_header_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_header_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_header_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_header_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Fields,
|
||||
class Handler>
|
||||
void
|
||||
read_header_op<Stream, DynamicBuffer, isRequest, Fields, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
async_parse(d.s, d.db, d.p, std::move(*this));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& msg)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
error_code ec;
|
||||
beast::http::read(stream, dynabuf, msg, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& m,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
header_parser_v1<isRequest, Fields> p;
|
||||
beast::http::parse(stream, dynabuf, p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p.complete());
|
||||
m = p.release();
|
||||
}
|
||||
|
||||
template<class AsyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields,
|
||||
class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& m,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::read_header_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Fields, decltype(
|
||||
completion.handler)>{completion.handler,
|
||||
stream, dynabuf, m};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
class read_op
|
||||
{
|
||||
using parser_type =
|
||||
parser_v1<isRequest, Body, Fields>;
|
||||
|
||||
using message_type =
|
||||
message<isRequest, Body, Fields>;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
bool started = false;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& sb_, message_type& m_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, m(m_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
void
|
||||
read_op<Stream, DynamicBuffer, isRequest, Body, Fields, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
async_parse(d.s, d.db, d.p, std::move(*this));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_reader<Body>::value,
|
||||
"Body has no reader");
|
||||
static_assert(is_Reader<typename Body::reader,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Reader requirements not met");
|
||||
error_code ec;
|
||||
beast::http::read(stream, dynabuf, msg, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& m,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_reader<Body>::value,
|
||||
"Body has no reader");
|
||||
static_assert(is_Reader<typename Body::reader,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Reader requirements not met");
|
||||
parser_v1<isRequest, Body, Fields> p;
|
||||
beast::http::parse(stream, dynabuf, p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p.complete());
|
||||
m = p.release();
|
||||
}
|
||||
|
||||
template<class AsyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& m,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_reader<Body>::value,
|
||||
"Body has no reader");
|
||||
static_assert(is_Reader<typename Body::reader,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Reader requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Body, Fields, decltype(
|
||||
completion.handler)>{completion.handler,
|
||||
stream, dynabuf, m};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
549
include/beast/http/impl/rfc7230.ipp
Normal file
549
include/beast/http/impl/rfc7230.ipp
Normal file
@@ -0,0 +1,549 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
#define BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <iterator>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class param_list::const_iterator
|
||||
{
|
||||
using iter_type = boost::string_ref::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(boost::string_ref const& 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(boost::string_ref const& 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 = boost::string_ref{
|
||||
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 beast::detail::ci_equal(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_tchar(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_tchar(*it_))
|
||||
break;
|
||||
}
|
||||
v_.first = boost::string_ref{&*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{boost::string_ref{&*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_tchar(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_tchar(*it_))
|
||||
break;
|
||||
}
|
||||
v_ = boost::string_ref{&*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 beast::detail::ci_equal(s, v);
|
||||
}
|
||||
) != end();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
710
include/beast/http/impl/write.ipp
Normal file
710
include/beast/http/impl/write.ipp
Normal file
@@ -0,0 +1,710 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_WRITE_IPP
|
||||
#define BEAST_HTTP_IMPL_WRITE_IPP
|
||||
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/chunk_encode.hpp>
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/core/write_dynabuf.hpp>
|
||||
#include <beast/core/detail/sync_ostream.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class DynamicBuffer, class Fields>
|
||||
void
|
||||
write_start_line(DynamicBuffer& dynabuf,
|
||||
header<true, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
write(dynabuf, msg.method);
|
||||
write(dynabuf, " ");
|
||||
write(dynabuf, msg.url);
|
||||
switch(msg.version)
|
||||
{
|
||||
case 10:
|
||||
write(dynabuf, " HTTP/1.0\r\n");
|
||||
break;
|
||||
case 11:
|
||||
write(dynabuf, " HTTP/1.1\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class Fields>
|
||||
void
|
||||
write_start_line(DynamicBuffer& dynabuf,
|
||||
header<false, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
switch(msg.version)
|
||||
{
|
||||
case 10:
|
||||
write(dynabuf, "HTTP/1.0 ");
|
||||
break;
|
||||
case 11:
|
||||
write(dynabuf, "HTTP/1.1 ");
|
||||
break;
|
||||
}
|
||||
write(dynabuf, msg.status);
|
||||
write(dynabuf, " ");
|
||||
write(dynabuf, msg.reason);
|
||||
write(dynabuf, "\r\n");
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class FieldSequence>
|
||||
void
|
||||
write_fields(DynamicBuffer& dynabuf, FieldSequence const& fields)
|
||||
{
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
//static_assert(is_FieldSequence<FieldSequence>::value,
|
||||
// "FieldSequence requirements not met");
|
||||
for(auto const& field : fields)
|
||||
{
|
||||
write(dynabuf, field.name());
|
||||
write(dynabuf, ": ");
|
||||
write(dynabuf, field.value());
|
||||
write(dynabuf, "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream, class Handler>
|
||||
class write_streambuf_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
streambuf sb;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_,
|
||||
streambuf&& sb_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, sb(std::move(sb_))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_streambuf_op(write_streambuf_op&&) = default;
|
||||
write_streambuf_op(write_streambuf_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_streambuf_op(DeducedHandler&& h, Stream& s,
|
||||
Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_streambuf_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_streambuf_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_streambuf_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_streambuf_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class Handler>
|
||||
void
|
||||
write_streambuf_op<Stream, Handler>::
|
||||
operator()(error_code ec, std::size_t, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
d.state = 99;
|
||||
boost::asio::async_write(d.s,
|
||||
d.sb.data(), std::move(*this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
header<isRequest, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
error_code ec;
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
header<isRequest, Fields> const& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
streambuf sb;
|
||||
detail::write_start_line(sb, msg);
|
||||
detail::write_fields(sb, msg.fields);
|
||||
beast::write(sb, "\r\n");
|
||||
boost::asio::write(stream, sb.data(), ec);
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Fields,
|
||||
class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
async_write(AsyncWriteStream& stream,
|
||||
header<isRequest, Fields> const& msg,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
beast::async_completion<WriteHandler,
|
||||
void(error_code)> completion{handler};
|
||||
streambuf sb;
|
||||
detail::write_start_line(sb, msg);
|
||||
detail::write_fields(sb, msg.fields);
|
||||
beast::write(sb, "\r\n");
|
||||
detail::write_streambuf_op<AsyncWriteStream,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, stream, std::move(sb)};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
struct write_preparation
|
||||
{
|
||||
message<isRequest, Body, Fields> const& msg;
|
||||
typename Body::writer w;
|
||||
streambuf sb;
|
||||
bool chunked;
|
||||
bool close;
|
||||
|
||||
explicit
|
||||
write_preparation(
|
||||
message<isRequest, Body, Fields> const& msg_)
|
||||
: msg(msg_)
|
||||
, w(msg)
|
||||
, chunked(token_list{
|
||||
msg.fields["Transfer-Encoding"]}.exists("chunked"))
|
||||
, close(token_list{
|
||||
msg.fields["Connection"]}.exists("close") ||
|
||||
(msg.version < 11 && ! msg.fields.exists(
|
||||
"Content-Length")))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
w.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
write_start_line(sb, msg);
|
||||
write_fields(sb, msg.fields);
|
||||
beast::write(sb, "\r\n");
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
// VFALCO How do we use handler_alloc in write_preparation?
|
||||
write_preparation<
|
||||
isRequest, Body, Fields> wp;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_,
|
||||
message<isRequest, Body, Fields> const& m_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, wp(m_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class writef0_lambda
|
||||
{
|
||||
write_op& self_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
writef0_lambda(write_op& self)
|
||||
: self_(self)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(ConstBufferSequence const& buffers) const
|
||||
{
|
||||
auto& d = *self_.d_;
|
||||
// write header and body
|
||||
if(d.wp.chunked)
|
||||
boost::asio::async_write(d.s,
|
||||
buffer_cat(d.wp.sb.data(),
|
||||
chunk_encode(false, buffers)),
|
||||
std::move(self_));
|
||||
else
|
||||
boost::asio::async_write(d.s,
|
||||
buffer_cat(d.wp.sb.data(),
|
||||
buffers), std::move(self_));
|
||||
}
|
||||
};
|
||||
|
||||
class writef_lambda
|
||||
{
|
||||
write_op& self_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
writef_lambda(write_op& self)
|
||||
: self_(self)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(ConstBufferSequence const& buffers) const
|
||||
{
|
||||
auto& d = *self_.d_;
|
||||
// write body
|
||||
if(d.wp.chunked)
|
||||
boost::asio::async_write(d.s,
|
||||
chunk_encode(false, buffers),
|
||||
std::move(self_));
|
||||
else
|
||||
boost::asio::async_write(d.s,
|
||||
buffers, std::move(self_));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_op<Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()(error_code ec, std::size_t, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
d.wp.init(ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(bind_handler(
|
||||
std::move(*this), ec, 0, false));
|
||||
return;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
auto const result =
|
||||
d.wp.w.write(ec,
|
||||
writef0_lambda{*this});
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(bind_handler(
|
||||
std::move(*this), ec, false));
|
||||
return;
|
||||
}
|
||||
if(result)
|
||||
d.state = d.wp.chunked ? 4 : 5;
|
||||
else
|
||||
d.state = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
// sent header and body
|
||||
case 2:
|
||||
d.wp.sb.consume(d.wp.sb.size());
|
||||
d.state = 3;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
auto const result =
|
||||
d.wp.w.write(ec,
|
||||
writef_lambda{*this});
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(result)
|
||||
d.state = d.wp.chunked ? 4 : 5;
|
||||
else
|
||||
d.state = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
case 4:
|
||||
// VFALCO Unfortunately the current interface to the
|
||||
// Writer concept prevents us from coalescing the
|
||||
// final body chunk with the final chunk delimiter.
|
||||
//
|
||||
// write final chunk
|
||||
d.state = 5;
|
||||
boost::asio::async_write(d.s,
|
||||
chunk_encode_final(), std::move(*this));
|
||||
return;
|
||||
|
||||
case 5:
|
||||
if(d.wp.close)
|
||||
{
|
||||
// VFALCO TODO Decide on an error code
|
||||
ec = boost::asio::error::eof;
|
||||
}
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class SyncWriteStream, class DynamicBuffer>
|
||||
class writef0_lambda
|
||||
{
|
||||
DynamicBuffer const& sb_;
|
||||
SyncWriteStream& stream_;
|
||||
bool chunked_;
|
||||
error_code& ec_;
|
||||
|
||||
public:
|
||||
writef0_lambda(SyncWriteStream& stream,
|
||||
DynamicBuffer const& sb, bool chunked, error_code& ec)
|
||||
: sb_(sb)
|
||||
, stream_(stream)
|
||||
, chunked_(chunked)
|
||||
, ec_(ec)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(ConstBufferSequence const& buffers) const
|
||||
{
|
||||
// write header and body
|
||||
if(chunked_)
|
||||
boost::asio::write(stream_, buffer_cat(
|
||||
sb_.data(), chunk_encode(false, buffers)), ec_);
|
||||
else
|
||||
boost::asio::write(stream_, buffer_cat(
|
||||
sb_.data(), buffers), ec_);
|
||||
}
|
||||
};
|
||||
|
||||
template<class SyncWriteStream>
|
||||
class writef_lambda
|
||||
{
|
||||
SyncWriteStream& stream_;
|
||||
bool chunked_;
|
||||
error_code& ec_;
|
||||
|
||||
public:
|
||||
writef_lambda(SyncWriteStream& stream,
|
||||
bool chunked, error_code& ec)
|
||||
: stream_(stream)
|
||||
, chunked_(chunked)
|
||||
, ec_(ec)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(ConstBufferSequence const& buffers) const
|
||||
{
|
||||
// write body
|
||||
if(chunked_)
|
||||
boost::asio::write(stream_,
|
||||
chunk_encode(false, buffers), ec_);
|
||||
else
|
||||
boost::asio::write(stream_, buffers, ec_);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_writer<Body>::value,
|
||||
"Body has no writer");
|
||||
static_assert(is_Writer<typename Body::writer,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Writer requirements not met");
|
||||
error_code ec;
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_writer<Body>::value,
|
||||
"Body has no writer");
|
||||
static_assert(is_Writer<typename Body::writer,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Writer requirements not met");
|
||||
detail::write_preparation<isRequest, Body, Fields> wp(msg);
|
||||
wp.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
auto result = wp.w.write(
|
||||
ec, detail::writef0_lambda<
|
||||
SyncWriteStream, decltype(wp.sb)>{
|
||||
stream, wp.sb, wp.chunked, ec});
|
||||
if(ec)
|
||||
return;
|
||||
wp.sb.consume(wp.sb.size());
|
||||
if(! result)
|
||||
{
|
||||
detail::writef_lambda<SyncWriteStream> wf{
|
||||
stream, wp.chunked, ec};
|
||||
for(;;)
|
||||
{
|
||||
result = wp.w.write(ec, wf);
|
||||
if(ec)
|
||||
return;
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(wp.chunked)
|
||||
{
|
||||
// VFALCO Unfortunately the current interface to the
|
||||
// Writer concept prevents us from using coalescing the
|
||||
// final body chunk with the final chunk delimiter.
|
||||
//
|
||||
// write final chunk
|
||||
boost::asio::write(stream, chunk_encode_final(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
if(wp.close)
|
||||
{
|
||||
// VFALCO TODO Decide on an error code
|
||||
ec = boost::asio::error::eof;
|
||||
}
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
async_write(AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_writer<Body>::value,
|
||||
"Body has no writer");
|
||||
static_assert(is_Writer<typename Body::writer,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Writer requirements not met");
|
||||
beast::async_completion<WriteHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::write_op<AsyncWriteStream, decltype(completion.handler),
|
||||
isRequest, Body, Fields>{completion.handler, stream, msg};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<isRequest, Fields> const& msg)
|
||||
{
|
||||
beast::detail::sync_ostream oss{os};
|
||||
error_code ec;
|
||||
write(oss, msg, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
return os;
|
||||
}
|
||||
|
||||
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(has_writer<Body>::value,
|
||||
"Body has no writer");
|
||||
static_assert(is_Writer<typename Body::writer,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Writer requirements not met");
|
||||
beast::detail::sync_ostream oss{os};
|
||||
error_code ec;
|
||||
write(oss, msg, ec);
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
throw system_error{ec};
|
||||
return os;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
485
include/beast/http/message.hpp
Normal file
485
include/beast/http/message.hpp
Normal file
@@ -0,0 +1,485 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_MESSAGE_HPP
|
||||
#define BEAST_HTTP_MESSAGE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/core/detail/integer_sequence.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/** A container for a HTTP request or response header.
|
||||
|
||||
A header includes the Start Line and Fields.
|
||||
|
||||
Some use-cases:
|
||||
|
||||
@li When the message has no body, such as a response to a HEAD request.
|
||||
|
||||
@li When the caller wishes to defer instantiation of the body.
|
||||
|
||||
@li Invoke algorithms which operate on the header only.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
struct header
|
||||
|
||||
#else
|
||||
template<bool isRequest, class Fields>
|
||||
struct header;
|
||||
|
||||
template<class Fields>
|
||||
struct header<true, Fields>
|
||||
#endif
|
||||
{
|
||||
/// Indicates if the header is a request or response.
|
||||
#if GENERATING_DOCS
|
||||
static bool constexpr is_request = isRequest;
|
||||
|
||||
#else
|
||||
static bool constexpr is_request = true;
|
||||
#endif
|
||||
|
||||
/// The type representing the fields.
|
||||
using fields_type = Fields;
|
||||
|
||||
/** The HTTP version.
|
||||
|
||||
This holds both the major and minor version numbers,
|
||||
using these formulas:
|
||||
@code
|
||||
major = version / 10;
|
||||
minor = version % 10;
|
||||
@endcode
|
||||
*/
|
||||
int version;
|
||||
|
||||
/** The Request Method
|
||||
|
||||
@note This field is present only if `isRequest == true`.
|
||||
*/
|
||||
std::string method;
|
||||
|
||||
/** The Request URI
|
||||
|
||||
@note This field is present only if `isRequest == true`.
|
||||
*/
|
||||
std::string url;
|
||||
|
||||
/// The HTTP field values.
|
||||
fields_type fields;
|
||||
|
||||
/// Default constructor
|
||||
header() = default;
|
||||
|
||||
/// Move constructor
|
||||
header(header&&) = default;
|
||||
|
||||
/// Copy constructor
|
||||
header(header const&) = default;
|
||||
|
||||
/// Move assignment
|
||||
header& operator=(header&&) = default;
|
||||
|
||||
/// Copy assignment
|
||||
header& operator=(header const&) = default;
|
||||
|
||||
/** Construct the header.
|
||||
|
||||
All arguments are forwarded to the constructor
|
||||
of the `fields` member.
|
||||
|
||||
@note This constructor participates in overload resolution
|
||||
if and only if the first parameter is not convertible to
|
||||
`header`.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
explicit
|
||||
header(Args&&... args);
|
||||
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
(sizeof...(ArgN) > 0) || ! std::is_convertible<
|
||||
typename std::decay<Arg1>::type,
|
||||
header>::value>::type>
|
||||
explicit
|
||||
header(Arg1&& arg1, ArgN&&... argn)
|
||||
: fields(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** A container for a HTTP request or response header.
|
||||
|
||||
A header includes the Start Line and Fields.
|
||||
|
||||
Some use-cases:
|
||||
|
||||
@li When the message has no body, such as a response to a HEAD request.
|
||||
|
||||
@li When the caller wishes to defer instantiation of the body.
|
||||
|
||||
@li Invoke algorithms which operate on the header only.
|
||||
*/
|
||||
template<class Fields>
|
||||
struct header<false, Fields>
|
||||
{
|
||||
/// Indicates if the header is a request or response.
|
||||
static bool constexpr is_request = false;
|
||||
|
||||
/// The type representing the fields.
|
||||
using fields_type = Fields;
|
||||
|
||||
/** The HTTP version.
|
||||
|
||||
This holds both the major and minor version numbers,
|
||||
using these formulas:
|
||||
@code
|
||||
major = version / 10;
|
||||
minor = version % 10;
|
||||
@endcode
|
||||
*/
|
||||
int version;
|
||||
|
||||
/// The HTTP field values.
|
||||
fields_type fields;
|
||||
|
||||
/// Default constructor
|
||||
header() = default;
|
||||
|
||||
/// Move constructor
|
||||
header(header&&) = default;
|
||||
|
||||
/// Copy constructor
|
||||
header(header const&) = default;
|
||||
|
||||
/// Move assignment
|
||||
header& operator=(header&&) = default;
|
||||
|
||||
/// Copy assignment
|
||||
header& operator=(header const&) = default;
|
||||
|
||||
/** Construct the header.
|
||||
|
||||
All arguments are forwarded to the constructor
|
||||
of the `fields` member.
|
||||
|
||||
@note This constructor participates in overload resolution
|
||||
if and only if the first parameter is not convertible to
|
||||
`header`.
|
||||
*/
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
(sizeof...(ArgN) > 0) || ! std::is_convertible<
|
||||
typename std::decay<Arg1>::type,
|
||||
header>::value>::type>
|
||||
explicit
|
||||
header(Arg1&& arg1, ArgN&&... argn)
|
||||
: fields(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** The Response Status-Code.
|
||||
|
||||
@note This field is present only if `isRequest == false`.
|
||||
*/
|
||||
int status;
|
||||
|
||||
/** The Response Reason-Phrase.
|
||||
|
||||
The Reason-Phrase is obsolete as of rfc7230.
|
||||
|
||||
@note This field is present only if `isRequest == false`.
|
||||
*/
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
/** A container for a complete HTTP message.
|
||||
|
||||
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.
|
||||
|
||||
@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>
|
||||
struct message : header<isRequest, Fields>
|
||||
{
|
||||
/// The base class used to hold the header portion of the message.
|
||||
using base_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;
|
||||
|
||||
/// A value representing the body.
|
||||
typename Body::value_type body;
|
||||
|
||||
/// Default constructor
|
||||
message() = default;
|
||||
|
||||
/// Move constructor
|
||||
message(message&&) = default;
|
||||
|
||||
/// Copy constructor
|
||||
message(message const&) = default;
|
||||
|
||||
/// Move assignment
|
||||
message& operator=(message&&) = default;
|
||||
|
||||
/// Copy assignment
|
||||
message& operator=(message const&) = default;
|
||||
|
||||
/** Construct a message from a header.
|
||||
|
||||
Additional arguments, if any, are forwarded to
|
||||
the constructor of the body member.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
message(base_type&& base, Args&&... args)
|
||||
: base_type(std::move(base))
|
||||
, body(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a message from a header.
|
||||
|
||||
Additional arguments, if any, are forwarded to
|
||||
the constructor of the body member.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
message(base_type const& base, Args&&... args)
|
||||
: base_type(base)
|
||||
, body(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a message.
|
||||
|
||||
@param u An argument forwarded to the body constructor.
|
||||
|
||||
@note This constructor participates in overload resolution
|
||||
only if `u` is not convertible to `base_type`.
|
||||
*/
|
||||
template<class U
|
||||
#if ! GENERATING_DOCS
|
||||
, class = typename std::enable_if<
|
||||
! std::is_convertible<typename
|
||||
std::decay<U>::type, base_type>::value>::type
|
||||
#endif
|
||||
>
|
||||
explicit
|
||||
message(U&& u)
|
||||
: body(std::forward<U>(u))
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a message.
|
||||
|
||||
@param u An argument forwarded to the body constructor.
|
||||
|
||||
@param v An argument forwarded to the fields constructor.
|
||||
|
||||
@note This constructor participates in overload resolution
|
||||
only if `u` is not convertible to `base_type`.
|
||||
*/
|
||||
template<class U, class V
|
||||
#if ! GENERATING_DOCS
|
||||
,class = typename std::enable_if<! std::is_convertible<
|
||||
typename std::decay<U>::type, base_type>::value>::type
|
||||
#endif
|
||||
>
|
||||
message(U&& u, V&& v)
|
||||
: base_type(std::forward<V>(v))
|
||||
, body(std::forward<U>(u))
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a message.
|
||||
|
||||
@param un A tuple forwarded as a parameter pack to the body constructor.
|
||||
*/
|
||||
template<class... Un>
|
||||
message(std::piecewise_construct_t, std::tuple<Un...> un)
|
||||
: message(std::piecewise_construct, un,
|
||||
beast::detail::make_index_sequence<sizeof...(Un)>{})
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a message.
|
||||
|
||||
@param un A tuple forwarded as a parameter pack to the body constructor.
|
||||
|
||||
@param vn A tuple forwarded as a parameter pack to the fields constructor.
|
||||
*/
|
||||
template<class... Un, class... Vn>
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<Un...>&& un, std::tuple<Vn...>&& vn)
|
||||
: message(std::piecewise_construct, un, vn,
|
||||
beast::detail::make_index_sequence<sizeof...(Un)>{},
|
||||
beast::detail::make_index_sequence<sizeof...(Vn)>{})
|
||||
{
|
||||
}
|
||||
|
||||
/// Returns the header portion of the message
|
||||
base_type&
|
||||
base()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns the header portion of the message
|
||||
base_type const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
template<class... Un, size_t... IUn>
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<Un...>& tu, beast::detail::index_sequence<IUn...>)
|
||||
: body(std::forward<Un>(std::get<IUn>(tu))...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Un, class... Vn,
|
||||
std::size_t... IUn, std::size_t... IVn>
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<Un...>& tu, std::tuple<Vn...>& tv,
|
||||
beast::detail::index_sequence<IUn...>,
|
||||
beast::detail::index_sequence<IVn...>)
|
||||
: base_type(std::forward<Vn>(std::get<IVn>(tv))...)
|
||||
, body(std::forward<Un>(std::get<IUn>(tu))...)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/** 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);
|
||||
|
||||
/// A typical HTTP request header
|
||||
using request_header = header<true, fields>;
|
||||
|
||||
/// Typical HTTP response header
|
||||
using response_header = header<false, fields>;
|
||||
|
||||
/// 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>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Returns `true` if the HTTP/1 message indicates a keep alive.
|
||||
|
||||
Undefined behavior if version is greater than 11.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
bool
|
||||
is_keep_alive(header<isRequest, Fields> const& msg);
|
||||
|
||||
/** Returns `true` if the HTTP/1 message indicates an Upgrade request or response.
|
||||
|
||||
Undefined behavior if version is greater than 11.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
bool
|
||||
is_upgrade(header<isRequest, Fields> const& msg);
|
||||
|
||||
/** HTTP/1 connection prepare options.
|
||||
|
||||
@note These values are used with @ref prepare.
|
||||
*/
|
||||
enum class connection
|
||||
{
|
||||
/// Specify Connection: close.
|
||||
close,
|
||||
|
||||
/// Specify Connection: keep-alive where possible.
|
||||
keep_alive,
|
||||
|
||||
/// Specify Connection: upgrade.
|
||||
upgrade
|
||||
};
|
||||
|
||||
/** Prepare a HTTP message.
|
||||
|
||||
This function will adjust the Content-Length, Transfer-Encoding,
|
||||
and Connection fields of the message based on the properties of
|
||||
the body and the options passed in.
|
||||
|
||||
@param msg The message to prepare. The fields may be modified.
|
||||
|
||||
@param options A list of prepare options.
|
||||
*/
|
||||
template<
|
||||
bool isRequest, class Body, class Fields,
|
||||
class... Options>
|
||||
void
|
||||
prepare(message<isRequest, Body, Fields>& msg,
|
||||
Options&&... options);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/message.ipp>
|
||||
|
||||
#endif
|
||||
155
include/beast/http/parse.hpp
Normal file
155
include/beast/http/parse.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_PARSE_HPP
|
||||
#define BEAST_HTTP_PARSE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Parse an object from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions are met:
|
||||
|
||||
@li The parser indicates that parsing is complete.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function 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 object
|
||||
being parsed. This additional data is stored in the stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@note This algorithm is generic, and not specific to HTTP
|
||||
messages. It is up to the parser to determine what predicate
|
||||
defines a complete operation.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser);
|
||||
|
||||
/** Parse an object from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions are met:
|
||||
|
||||
@li The parser indicates that parsing is complete.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function 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 object
|
||||
being parsed. This additional data is stored in the stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@note This algorithm is generic, and not specific to HTTP
|
||||
messages. It is up to the parser to determine what predicate
|
||||
defines a complete operation.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser, error_code& ec);
|
||||
|
||||
/** Start an asynchronous operation to parse an object from a stream.
|
||||
|
||||
This function is used to asynchronously read from a stream and
|
||||
pass the data to the specified parser. The function call always
|
||||
returns immediately. The asynchronous operation will continue
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li The parser indicates that parsing is complete.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This operation is implemented in terms of one 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 operations 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.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data. This object must remain valid
|
||||
until the completion handler is invoked.
|
||||
|
||||
@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_service::post`.
|
||||
*/
|
||||
template<class AsyncReadStream,
|
||||
class DynamicBuffer, class Parser, class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_parse(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
Parser& parser, ReadHandler&& handler);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/parse.ipp>
|
||||
|
||||
#endif
|
||||
48
include/beast/http/parse_error.hpp
Normal file
48
include/beast/http/parse_error.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_PARSE_ERROR_HPP
|
||||
#define BEAST_HTTP_PARSE_ERROR_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class parse_error
|
||||
{
|
||||
connection_closed = 1,
|
||||
|
||||
bad_method,
|
||||
bad_uri,
|
||||
bad_version,
|
||||
bad_crlf,
|
||||
|
||||
bad_status,
|
||||
bad_reason,
|
||||
|
||||
bad_field,
|
||||
bad_value,
|
||||
bad_content_length,
|
||||
illegal_content_length,
|
||||
|
||||
invalid_chunk_size,
|
||||
invalid_ext_name,
|
||||
invalid_ext_val,
|
||||
|
||||
header_too_big,
|
||||
body_too_big,
|
||||
short_read
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/parse_error.ipp>
|
||||
|
||||
#endif
|
||||
339
include/beast/http/parser_v1.hpp
Normal file
339
include/beast/http/parser_v1.hpp
Normal file
@@ -0,0 +1,339 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_PARSER_V1_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/header_parser_v1.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Skip body option.
|
||||
|
||||
The options controls whether or not the parser expects to see a
|
||||
HTTP body, regardless of the presence or absence of certain fields
|
||||
such as Content-Length.
|
||||
|
||||
Depending on the request, some responses do not carry a body.
|
||||
For example, a 200 response to a CONNECT request from a tunneling
|
||||
proxy. In these cases, callers use the @ref skip_body option to
|
||||
inform the parser that no body is expected. The parser will consider
|
||||
the message complete after the header has been received.
|
||||
|
||||
Example:
|
||||
@code
|
||||
parser_v1<true, empty_body, fields> p;
|
||||
p.set_option(skip_body{true});
|
||||
@endcode
|
||||
|
||||
@note Objects of this type are passed to @ref parser_v1::set_option.
|
||||
*/
|
||||
struct skip_body
|
||||
{
|
||||
bool value;
|
||||
|
||||
explicit
|
||||
skip_body(bool v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** A parser for producing HTTP/1 messages.
|
||||
|
||||
This class uses the basic HTTP/1 wire format parser to convert
|
||||
a series of octets into a `message`.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
class parser_v1
|
||||
: public basic_parser_v1<isRequest,
|
||||
parser_v1<isRequest, Body, Fields>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::request_parser_base,
|
||||
detail::response_parser_base>::type
|
||||
{
|
||||
public:
|
||||
/// The type of message this parser produces.
|
||||
using message_type =
|
||||
message<isRequest, Body, Fields>;
|
||||
|
||||
private:
|
||||
using reader =
|
||||
typename message_type::body_type::reader;
|
||||
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_reader<Body>::value,
|
||||
"Body has no reader");
|
||||
static_assert(is_Reader<reader, message_type>::value,
|
||||
"Reader requirements not met");
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
message_type m_;
|
||||
boost::optional<reader> r_;
|
||||
std::uint8_t skip_body_ = 0;
|
||||
bool flush_ = false;
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
parser_v1() = default;
|
||||
|
||||
/// Move constructor
|
||||
parser_v1(parser_v1&&) = default;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
parser_v1(parser_v1 const&) = delete;
|
||||
|
||||
/// Move assignment (disallowed)
|
||||
parser_v1& operator=(parser_v1&&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
parser_v1& operator=(parser_v1 const&) = delete;
|
||||
|
||||
/** Construct the parser.
|
||||
|
||||
@param args Forwarded to the message constructor.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if the first argument is not a parser or fields parser.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
explicit
|
||||
parser_v1(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! std::is_same<typename
|
||||
std::decay<Arg1>::type,
|
||||
header_parser_v1<isRequest, Fields>>::value &&
|
||||
! std::is_same<typename
|
||||
std::decay<Arg1>::type, parser_v1>::value
|
||||
>::type>
|
||||
explicit
|
||||
parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||
: m_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Construct the parser from a fields parser.
|
||||
|
||||
@param parser The fields parser to construct from.
|
||||
@param args Forwarded to the message body constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
parser_v1(header_parser_v1<isRequest, Fields>& parser,
|
||||
Args&&... args)
|
||||
: m_(parser.release(), std::forward<Args>(args)...)
|
||||
{
|
||||
static_cast<basic_parser_v1<
|
||||
isRequest, parser_v1<
|
||||
isRequest, Body, Fields>>&>(*this) = parser;
|
||||
}
|
||||
|
||||
/// Set the skip body option.
|
||||
void
|
||||
set_option(skip_body const& o)
|
||||
{
|
||||
skip_body_ = o.value ? 1 : 0;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
message_type const&
|
||||
get() const
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
message_type&
|
||||
get()
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed message.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref complete would return `true`.
|
||||
|
||||
Requires:
|
||||
`message<isRequest, Body, Fields>` is @b MoveConstructible
|
||||
*/
|
||||
message_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(m_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(m_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser_v1<isRequest, parser_v1>;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(! flush_)
|
||||
return;
|
||||
flush_ = false;
|
||||
BOOST_ASSERT(! field_.empty());
|
||||
m_.fields.insert(field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
|
||||
void on_start(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_uri(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->uri_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_reason(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_request_or_response(std::true_type)
|
||||
{
|
||||
m_.method = std::move(this->method_);
|
||||
m_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void on_request_or_response(std::false_type)
|
||||
{
|
||||
m_.status = this->status_code();
|
||||
m_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void on_request(error_code&)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code&)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
field_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_value(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
value_.append(s.data(), s.size());
|
||||
flush_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
on_header(std::uint64_t, error_code&)
|
||||
{
|
||||
flush();
|
||||
m_.version = 10 * this->http_major() + this->http_minor();
|
||||
}
|
||||
|
||||
body_what
|
||||
on_body_what(std::uint64_t, error_code& ec)
|
||||
{
|
||||
if(skip_body_)
|
||||
return body_what::skip;
|
||||
r_.emplace(m_);
|
||||
r_->init(ec);
|
||||
return body_what::normal;
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const& s, error_code& ec)
|
||||
{
|
||||
r_->write(s.data(), s.size(), ec);
|
||||
}
|
||||
|
||||
void on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Create a new parser from a fields parser.
|
||||
|
||||
Associates a Body type with a fields parser, and returns
|
||||
a new parser which parses a complete message object
|
||||
containing the original message fields and a new body
|
||||
of the specified body type.
|
||||
|
||||
This function allows HTTP messages to be parsed in two stages.
|
||||
First, the fields are parsed and control is returned. Then,
|
||||
the caller can choose at run-time, the type of Body to
|
||||
associate with the message. And finally, complete the parse
|
||||
in a second call.
|
||||
|
||||
@param parser The fields parser to construct from. Ownership
|
||||
of the message fields in the fields parser is transferred
|
||||
as if by call to @ref header_parser_v1::release.
|
||||
|
||||
@param args Forwarded to the body constructor of the message
|
||||
in the new parser.
|
||||
|
||||
@return A parser for a message with the specified @b Body type.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
headers_parser<true, fields> ph;
|
||||
...
|
||||
auto p = with_body<string_body>(ph);
|
||||
...
|
||||
message<true, string_body, fields> m = p.release();
|
||||
@endcode
|
||||
*/
|
||||
template<class Body,
|
||||
bool isRequest, class Fields, class... Args>
|
||||
parser_v1<isRequest, Body, Fields>
|
||||
with_body(header_parser_v1<isRequest, Fields>& parser,
|
||||
Args&&... args)
|
||||
{
|
||||
return parser_v1<isRequest, Body, Fields>(
|
||||
parser, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
303
include/beast/http/read.hpp
Normal file
303
include/beast/http/read.hpp
Normal file
@@ -0,0 +1,303 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_READ_HPP
|
||||
#define BEAST_HTTP_READ_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Read a HTTP/1 header from a stream.
|
||||
|
||||
This function is used to synchronously read a header
|
||||
from a stream. The call blocks until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li An entire header is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function 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
|
||||
fields being parsed. This additional data is stored in the
|
||||
stream buffer, which may be used in subsequent calls.
|
||||
|
||||
If the message corresponding to the header being received
|
||||
contains a message body, it is the callers responsibility
|
||||
to cause the body to be read in before attempting to read
|
||||
the next message.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the header. Any contents
|
||||
will be overwritten. The type must support copy assignment
|
||||
or move assignment.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& msg);
|
||||
|
||||
/** Read a HTTP/1 header from a stream.
|
||||
|
||||
This function is used to synchronously read a header
|
||||
from a stream. The call blocks until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li An entire header is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function 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
|
||||
fields being parsed. This additional data is stored in the
|
||||
stream buffer, which may be used in subsequent calls.
|
||||
|
||||
If the message corresponding to the header being received
|
||||
contains a message body, it is the callers responsibility
|
||||
to cause the body to be read in before attempting to read
|
||||
the next message.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the header. Any contents
|
||||
will be overwritten. The type must support copy assignment
|
||||
or move assignment.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a HTTP/1 header asynchronously from a stream.
|
||||
|
||||
This function is used to asynchronously read a header from
|
||||
a stream. The function call always returns immediately. The
|
||||
asynchronous operation will continue until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li An entire header is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
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 operations until this operation completes.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message fields being parsed. This additional data is
|
||||
stored in the stream buffer, which may be used in subsequent calls.
|
||||
|
||||
If the message corresponding to the header being received
|
||||
contains a message body, it is the callers responsibility
|
||||
to cause the body to be read in before attempting to read
|
||||
the next message.
|
||||
|
||||
@param stream The stream to read the message from.
|
||||
The type must support the @b `AsyncReadStream` concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the header. Any contents
|
||||
will be overwritten. The type must support copy assignment or
|
||||
move assignment. The object must remain valid at least until
|
||||
the completion 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
|
||||
); @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_service::post`.
|
||||
*/
|
||||
template<class AsyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& msg,
|
||||
ReadHandler&& handler);
|
||||
|
||||
/** Read a HTTP/1 message from a stream.
|
||||
|
||||
This function is used to synchronously read a message from
|
||||
a stream. The call blocks until one of the following conditions
|
||||
is true:
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function 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 parsed. This additional data is stored in the stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the message. Any
|
||||
contents will be overwritten. The type must support
|
||||
copy assignment or move assignment.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg);
|
||||
|
||||
/** Read a HTTP/1 message from a stream.
|
||||
|
||||
This function is used to synchronously read a message from
|
||||
a stream. The call blocks until one of the following conditions
|
||||
is true:
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function 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 parsed. This additional data is stored in the stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the message. Any
|
||||
contents will be overwritten. The type must support
|
||||
copy assignment or move assignment.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a HTTP/1 message asynchronously from a stream.
|
||||
|
||||
This function is used to asynchronously read a message from
|
||||
a stream. The function call always returns immediately. The
|
||||
asynchronous operation will continue until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
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 operations until this operation completes.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being parsed. This additional data is stored
|
||||
in the stream buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream to read the message from.
|
||||
The type must support the @b `AsyncReadStream` concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the header. Any contents
|
||||
will be overwritten. The type must support copy assignment or
|
||||
move assignment. The object must remain valid at least until
|
||||
the completion 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
|
||||
); @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_service::post`.
|
||||
*/
|
||||
template<class AsyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
ReadHandler&& handler);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/read.ipp>
|
||||
|
||||
#endif
|
||||
85
include/beast/http/reason.hpp
Normal file
85
include/beast/http/reason.hpp
Normal file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_REASON_HPP
|
||||
#define BEAST_HTTP_REASON_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
char const*
|
||||
reason_string(int status)
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case 100: return "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
case 206: return "Partial Content";
|
||||
case 300: return "Multiple Choices";
|
||||
case 301: return "Moved Permanently";
|
||||
case 302: return "Found";
|
||||
case 303: return "See Other";
|
||||
case 304: return "Not Modified";
|
||||
case 305: return "Use Proxy";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 402: return "Payment Required";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 406: return "Not Acceptable";
|
||||
case 407: return "Proxy Authentication Required";
|
||||
case 408: return "Request Timeout";
|
||||
case 409: return "Conflict";
|
||||
case 410: return "Gone";
|
||||
case 411: return "Length Required";
|
||||
case 412: return "Precondition Failed";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 414: return "Request-URI Too Long";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Requested Range Not Satisfiable";
|
||||
case 417: return "Expectation Failed";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 502: return "Bad Gateway";
|
||||
case 503: return "Service Unavailable";
|
||||
case 504: return "Gateway Timeout";
|
||||
case 505: return "HTTP Version Not Supported";
|
||||
|
||||
case 306: return "<reserved>";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<unknown-status>";
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
/** Returns the text for a known status code integer. */
|
||||
inline
|
||||
char const*
|
||||
reason_string(int status)
|
||||
{
|
||||
return detail::reason_string(status);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
284
include/beast/http/rfc7230.hpp
Normal file
284
include/beast/http/rfc7230.hpp
Normal file
@@ -0,0 +1,284 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_RFC7230_HPP
|
||||
#define BEAST_HTTP_RFC7230_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A list of parameters in a HTTP extension field value.
|
||||
|
||||
This container allows iteration of the parameter list in a 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
|
||||
{
|
||||
boost::string_ref 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<boost::string_ref, boost::string_ref>;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if GENERATING_DOCS
|
||||
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(boost::string_ref const& 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 a 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 = boost::string_ref::const_iterator;
|
||||
|
||||
boost::string_ref 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<boost::string_ref, param_list>;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if GENERATING_DOCS
|
||||
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(boost::string_ref const& 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 = boost::string_ref::const_iterator;
|
||||
|
||||
boost::string_ref s_;
|
||||
|
||||
public:
|
||||
/// The type of each element in the token list.
|
||||
using value_type = boost::string_ref;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if GENERATING_DOCS
|
||||
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(boost::string_ref const& 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);
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/rfc7230.ipp>
|
||||
|
||||
#endif
|
||||
27
include/beast/http/streambuf_body.hpp
Normal file
27
include/beast/http/streambuf_body.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_STREAMBUF_BODY_HPP
|
||||
#define BEAST_HTTP_STREAMBUF_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/basic_dynabuf_body.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A message body represented by a @ref streambuf
|
||||
|
||||
Meets the requirements of @b `Body`.
|
||||
*/
|
||||
using streambuf_body = basic_dynabuf_body<streambuf>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
101
include/beast/http/string_body.hpp
Normal file
101
include/beast/http/string_body.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_STRING_BODY_HPP
|
||||
#define BEAST_HTTP_STRING_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A Body represented by a std::string.
|
||||
|
||||
Meets the requirements of @b `Body`.
|
||||
*/
|
||||
struct string_body
|
||||
{
|
||||
/// The type of the `message::body` member
|
||||
using value_type = std::string;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& s_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
string_body, Fields>& m) noexcept
|
||||
: s_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
{
|
||||
auto const n = s_.size();
|
||||
s_.resize(n + size);
|
||||
std::memcpy(&s_[n], data, size);
|
||||
}
|
||||
};
|
||||
|
||||
class writer
|
||||
{
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<
|
||||
isRequest, string_body, Fields> const& msg) noexcept
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec) noexcept
|
||||
{
|
||||
beast::detail::ignore_unused(ec);
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
content_length() const noexcept
|
||||
{
|
||||
return body_.size();
|
||||
}
|
||||
|
||||
template<class WriteFunction>
|
||||
bool
|
||||
write(error_code&, WriteFunction&& wf) noexcept
|
||||
{
|
||||
wf(boost::asio::buffer(body_));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
291
include/beast/http/write.hpp
Normal file
291
include/beast/http/write.hpp
Normal file
@@ -0,0 +1,291 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_WRITE_HPP
|
||||
#define BEAST_HTTP_WRITE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Write a HTTP/1 header to a stream.
|
||||
|
||||
This function is used to synchronously write a header to
|
||||
a stream. The call will block until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li The entire header is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
Regardless of the semantic meaning of the header (for example,
|
||||
specifying "Content-Length: 0" and "Connection: close"),
|
||||
this function will not return `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b `SyncWriteStream` concept.
|
||||
|
||||
@param msg The header to write.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
header<isRequest, Fields> const& msg);
|
||||
|
||||
/** Write a HTTP/1 header to a stream.
|
||||
|
||||
This function is used to synchronously write a header to
|
||||
a stream. The call will block until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li The entire header is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
Regardless of the semantic meaning of the header (for example,
|
||||
specifying "Content-Length: 0" and "Connection: close"),
|
||||
this function will not return `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b `SyncWriteStream` concept.
|
||||
|
||||
@param msg The header to write.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
header<isRequest, Fields> const& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a HTTP/1 header asynchronously to a stream.
|
||||
|
||||
This function is used to asynchronously write a header to
|
||||
a stream. The function call always returns immediately. The
|
||||
asynchronous operation will continue until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li The entire header is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the stream'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 until this operation
|
||||
completes.
|
||||
|
||||
Regardless of the semantic meaning of the header (for example,
|
||||
specifying "Content-Length: 0" and "Connection: close"),
|
||||
this function will not return `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b `AsyncWriteStream` concept.
|
||||
|
||||
@param msg The header to write. The object must remain valid
|
||||
at least until the completion 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
|
||||
); @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_service::post`.
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Fields,
|
||||
class WriteHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_write(AsyncWriteStream& stream,
|
||||
header<isRequest, Fields> const& msg,
|
||||
WriteHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Write a HTTP/1 message to a stream.
|
||||
|
||||
This function is used to write a message to a stream. 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 implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error thrown from this
|
||||
function will be `boost::asio::error::eof`.
|
||||
|
||||
@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.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg);
|
||||
|
||||
/** Write a HTTP/1 message on a stream.
|
||||
|
||||
This function is used to write a message to a stream. 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 implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error returned from this
|
||||
function will be `boost::asio::error::eof`.
|
||||
|
||||
@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.
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a HTTP/1 message asynchronously to a stream.
|
||||
|
||||
This function is used to asynchronously write a message to
|
||||
a 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 written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the stream'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 until this operation
|
||||
completes.
|
||||
|
||||
The implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the operation will complete with
|
||||
the error set to `boost::asio::error::eof`.
|
||||
|
||||
@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 completion 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
|
||||
); @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_service::post`.
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_write(AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
WriteHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Serialize a 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 a 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
|
||||
|
||||
#include <beast/http/impl/write.ipp>
|
||||
|
||||
#endif
|
||||
23
include/beast/version.hpp
Normal file
23
include/beast/version.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_VERSION_HPP
|
||||
#define BEAST_VERSION_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
|
||||
// follows http://semver.org
|
||||
|
||||
// BEAST_VERSION % 100 is the patch level
|
||||
// BEAST_VERSION / 100 % 1000 is the minor version
|
||||
// BEAST_VERSION / 100000 is the major version
|
||||
//
|
||||
#define BEAST_VERSION 100000
|
||||
|
||||
#define BEAST_VERSION_STRING "1.0.0-b34"
|
||||
|
||||
#endif
|
||||
19
include/beast/websocket.hpp
Normal file
19
include/beast/websocket.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_HPP
|
||||
#define BEAST_WEBSOCKET_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
|
||||
#include <beast/websocket/error.hpp>
|
||||
#include <beast/websocket/option.hpp>
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/stream.hpp>
|
||||
#include <beast/websocket/teardown.hpp>
|
||||
|
||||
#endif
|
||||
75
include/beast/websocket/detail/debug.hpp
Normal file
75
include/beast/websocket/detail/debug.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_DEBUG_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_DEBUG_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
to_hex(boost::asio::const_buffer b)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
std::stringstream ss;
|
||||
auto p = buffer_cast<std::uint8_t const*>(b);
|
||||
auto n = buffer_size(b);
|
||||
while(n--)
|
||||
{
|
||||
ss <<
|
||||
std::setfill('0') <<
|
||||
std::setw(2) <<
|
||||
std::hex << int(*p++) << " ";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
std::string
|
||||
to_hex(Buffers const& bs)
|
||||
{
|
||||
std::string s;
|
||||
for(auto const& b : bs)
|
||||
s.append(to_hex(boost::asio::const_buffer(b)));
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
std::string
|
||||
buffers_to_string(Buffers const& bs)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
std::string s;
|
||||
s.reserve(buffer_size(bs));
|
||||
for(auto const& b : bs)
|
||||
s.append(buffer_cast<char const*>(b),
|
||||
buffer_size(b));
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
format(std::string s)
|
||||
{
|
||||
auto const w = 84;
|
||||
for(int n = w*(s.size()/w); n>0; n-=w)
|
||||
s.insert(n, 1, '\n');
|
||||
return s;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
167
include/beast/websocket/detail/decorator.hpp
Normal file
167
include/beast/websocket/detail/decorator.hpp
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
|
||||
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/version.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
using request_type = http::request<http::empty_body>;
|
||||
|
||||
using response_type = http::response<http::string_body>;
|
||||
|
||||
struct abstract_decorator
|
||||
{
|
||||
virtual
|
||||
~abstract_decorator() = default;
|
||||
|
||||
virtual
|
||||
void
|
||||
operator()(request_type& req) const = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
operator()(response_type& res) const = 0;
|
||||
};
|
||||
|
||||
template<class F>
|
||||
class decorator : public abstract_decorator
|
||||
{
|
||||
F f_;
|
||||
|
||||
class call_req_possible
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U const>().operator()(
|
||||
std::declval<request_type&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
public:
|
||||
using type = decltype(check<F>(0));
|
||||
};
|
||||
|
||||
class call_res_possible
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U const>().operator()(
|
||||
std::declval<response_type&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
public:
|
||||
using type = decltype(check<F>(0));
|
||||
};
|
||||
|
||||
public:
|
||||
decorator(F&& t)
|
||||
: f_(std::move(t))
|
||||
{
|
||||
}
|
||||
|
||||
decorator(F const& t)
|
||||
: f_(t)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(request_type& req) const override
|
||||
{
|
||||
(*this)(req, typename call_req_possible::type{});
|
||||
}
|
||||
|
||||
void
|
||||
operator()(response_type& res) const override
|
||||
{
|
||||
(*this)(res, typename call_res_possible::type{});
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
operator()(request_type& req, std::true_type) const
|
||||
{
|
||||
f_(req);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(request_type& req, std::false_type) const
|
||||
{
|
||||
req.fields.replace("User-Agent",
|
||||
std::string{"Beast/"} + BEAST_VERSION_STRING);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(response_type& res, std::true_type) const
|
||||
{
|
||||
f_(res);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(response_type& res, std::false_type) const
|
||||
{
|
||||
res.fields.replace("Server",
|
||||
std::string{"Beast/"} + BEAST_VERSION_STRING);
|
||||
}
|
||||
};
|
||||
|
||||
class decorator_type
|
||||
{
|
||||
std::shared_ptr<abstract_decorator> p_;
|
||||
|
||||
public:
|
||||
decorator_type() = delete;
|
||||
decorator_type(decorator_type&&) = default;
|
||||
decorator_type(decorator_type const&) = default;
|
||||
decorator_type& operator=(decorator_type&&) = default;
|
||||
decorator_type& operator=(decorator_type const&) = default;
|
||||
|
||||
template<class F, class =
|
||||
typename std::enable_if<! std::is_same<
|
||||
typename std::decay<F>::type,
|
||||
decorator_type>::value>>
|
||||
decorator_type(F&& f)
|
||||
: p_(std::make_shared<decorator<F>>(
|
||||
std::forward<F>(f)))
|
||||
{
|
||||
BOOST_ASSERT(p_);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(request_type& req)
|
||||
{
|
||||
(*p_)(req);
|
||||
BOOST_ASSERT(p_);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(response_type& res)
|
||||
{
|
||||
(*p_)(res);
|
||||
BOOST_ASSERT(p_);
|
||||
}
|
||||
};
|
||||
|
||||
struct default_decorator
|
||||
{
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
71
include/beast/websocket/detail/endian.hpp
Normal file
71
include/beast/websocket/detail/endian.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_ENDIAN_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_ENDIAN_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
232
include/beast/websocket/detail/frame.hpp
Normal file
232
include/beast/websocket/detail/frame.hpp
Normal file
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_FRAME_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_FRAME_HPP
|
||||
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/detail/endian.hpp>
|
||||
#include <beast/websocket/detail/utf8_checker.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/static_streambuf.hpp>
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Contents of a WebSocket frame header
|
||||
struct frame_header
|
||||
{
|
||||
opcode op;
|
||||
bool fin;
|
||||
bool mask;
|
||||
bool rsv1;
|
||||
bool rsv2;
|
||||
bool rsv3;
|
||||
std::uint64_t len;
|
||||
std::uint32_t key;
|
||||
};
|
||||
|
||||
// holds the largest possible frame header
|
||||
using fh_streambuf =
|
||||
static_streambuf_n<14>;
|
||||
|
||||
// holds the largest possible control frame
|
||||
using frame_streambuf =
|
||||
static_streambuf_n< 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;
|
||||
}
|
||||
|
||||
// Returns `true` if a close code is valid
|
||||
inline
|
||||
bool
|
||||
is_valid(close_code::value code)
|
||||
{
|
||||
auto const v = code;
|
||||
switch(v)
|
||||
{
|
||||
case 1000:
|
||||
case 1001:
|
||||
case 1002:
|
||||
case 1003:
|
||||
case 1007:
|
||||
case 1008:
|
||||
case 1009:
|
||||
case 1010:
|
||||
case 1011:
|
||||
case 1012:
|
||||
case 1013:
|
||||
return true;
|
||||
|
||||
// explicitly reserved
|
||||
case 1004:
|
||||
case 1005:
|
||||
case 1006:
|
||||
case 1014:
|
||||
case 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_data& data, Buffers const& bs)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::mutable_buffers_1;
|
||||
BOOST_ASSERT(buffer_size(bs) <= data.max_size());
|
||||
data.resize(buffer_size(bs));
|
||||
buffer_copy(mutable_buffers_1{
|
||||
data.data(), data.size()}, bs);
|
||||
}
|
||||
|
||||
// Read close_reason, return true on success
|
||||
// This is for the close payload
|
||||
//
|
||||
template<class Buffers>
|
||||
void
|
||||
read(close_reason& cr,
|
||||
Buffers const& bs, close_code::value& 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;
|
||||
}
|
||||
consuming_buffers<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(cr.code))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(n > 0)
|
||||
{
|
||||
cr.reason.resize(n);
|
||||
buffer_copy(buffer(&cr.reason[0], n), cb);
|
||||
if(! detail::check_utf8(
|
||||
cr.reason.data(), cr.reason.size()))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cr.reason = "";
|
||||
}
|
||||
code = close_code::none;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
60
include/beast/websocket/detail/hybi13.hpp
Normal file
60
include/beast/websocket/detail/hybi13.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
|
||||
|
||||
#include <beast/core/detail/base64.hpp>
|
||||
#include <beast/core/detail/sha1.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class Gen>
|
||||
std::string
|
||||
make_sec_ws_key(Gen& g)
|
||||
{
|
||||
std::array<std::uint8_t, 16> a;
|
||||
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;
|
||||
}
|
||||
return beast::detail::base64_encode(
|
||||
a.data(), a.size());
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
make_sec_ws_accept(boost::string_ref const& key)
|
||||
{
|
||||
std::string s(key.data(), key.size());
|
||||
s += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
beast::detail::sha1_context ctx;
|
||||
beast::detail::init(ctx);
|
||||
beast::detail::update(ctx, s.data(), s.size());
|
||||
std::array<std::uint8_t,
|
||||
beast::detail::sha1_context::digest_size> digest;
|
||||
beast::detail::finish(ctx, digest.data());
|
||||
return beast::detail::base64_encode(
|
||||
digest.data(), digest.size());
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
160
include/beast/websocket/detail/invokable.hpp
Normal file
160
include/beast/websocket/detail/invokable.hpp
Normal file
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_INVOKABLE_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_INVOKABLE_HPP
|
||||
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// "Parks" a composed operation, to invoke later
|
||||
//
|
||||
class invokable
|
||||
{
|
||||
struct base
|
||||
{
|
||||
base() = default;
|
||||
base(base &&) = default;
|
||||
virtual ~base() = default;
|
||||
virtual void move(void* p) = 0;
|
||||
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
|
||||
move(void* p) override
|
||||
{
|
||||
::new(p) holder(std::move(*this));
|
||||
}
|
||||
|
||||
void
|
||||
operator()() override
|
||||
{
|
||||
F f_(std::move(f));
|
||||
this->~holder();
|
||||
// invocation of f_() can
|
||||
// assign a new invokable.
|
||||
f_();
|
||||
}
|
||||
};
|
||||
|
||||
struct exemplar
|
||||
{
|
||||
struct H
|
||||
{
|
||||
void operator()();
|
||||
};
|
||||
|
||||
struct T
|
||||
{
|
||||
using handler_type = H;
|
||||
};
|
||||
|
||||
handler_ptr<T, H> hp;
|
||||
|
||||
void operator()();
|
||||
};
|
||||
|
||||
using buf_type = char[sizeof(holder<exemplar>)];
|
||||
|
||||
base* base_ = nullptr;
|
||||
alignas(holder<exemplar>) buf_type buf_;
|
||||
|
||||
public:
|
||||
~invokable()
|
||||
{
|
||||
if(base_)
|
||||
base_->~base();
|
||||
}
|
||||
|
||||
invokable() = default;
|
||||
|
||||
invokable(invokable&& other)
|
||||
{
|
||||
if(other.base_)
|
||||
{
|
||||
// type-pun
|
||||
base_ = reinterpret_cast<base*>(&buf_[0]);
|
||||
other.base_->move(buf_);
|
||||
other.base_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
invokable&
|
||||
operator=(invokable&& other)
|
||||
{
|
||||
// Engaged invokables must be invoked before
|
||||
// assignment otherwise the io_service
|
||||
// completion invariants are broken.
|
||||
BOOST_ASSERT(! base_);
|
||||
|
||||
if(other.base_)
|
||||
{
|
||||
// type-pun
|
||||
base_ = reinterpret_cast<base*>(&buf_[0]);
|
||||
other.base_->move(buf_);
|
||||
other.base_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class F>
|
||||
void
|
||||
emplace(F&& f);
|
||||
|
||||
bool
|
||||
maybe_invoke()
|
||||
{
|
||||
if(base_)
|
||||
{
|
||||
auto const basep = base_;
|
||||
base_ = nullptr;
|
||||
(*basep)();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<class F>
|
||||
void
|
||||
invokable::emplace(F&& f)
|
||||
{
|
||||
static_assert(sizeof(buf_type) >= sizeof(holder<F>),
|
||||
"buffer too small");
|
||||
BOOST_ASSERT(! base_);
|
||||
::new(buf_) holder<F>(std::forward<F>(f));
|
||||
// type-pun
|
||||
base_ = reinterpret_cast<base*>(&buf_[0]);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
265
include/beast/websocket/detail/mask.hpp
Normal file
265
include/beast/websocket/detail/mask.hpp
Normal file
@@ -0,0 +1,265 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_MASK_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_MASK_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <type_traits>
|
||||
|
||||
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)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
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);
|
||||
case 2: p[1] ^= static_cast<std::uint8_t>(key >> 8);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 64-bit optimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_fast(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
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(auto const& b : bs)
|
||||
mask_inplace(b, key);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
472
include/beast/websocket/detail/pmd_extension.hpp
Normal file
472
include/beast/websocket/detail/pmd_extension.hpp
Normal file
@@ -0,0 +1,472 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <beast/zlib/deflate_stream.hpp>
|
||||
#include <beast/zlib/inflate_stream.hpp>
|
||||
#include <beast/websocket/option.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <utility>
|
||||
|
||||
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(boost::string_ref const& s)
|
||||
{
|
||||
if(s.size() == 0)
|
||||
return -1;
|
||||
if(s.size() > 2)
|
||||
return -1;
|
||||
if(s[0] < '1' || s[0] > '9')
|
||||
return -1;
|
||||
int i = 0;
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c < '0' || c > '9')
|
||||
return -1;
|
||||
i = 10 * i + (c - '0');
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// Parse permessage-deflate request fields
|
||||
//
|
||||
template<class Fields>
|
||||
void
|
||||
pmd_read(pmd_offer& offer, Fields 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;
|
||||
|
||||
using beast::detail::ci_equal;
|
||||
http::ext_list list{
|
||||
fields["Sec-WebSocket-Extensions"]};
|
||||
for(auto const& ext : list)
|
||||
{
|
||||
if(ci_equal(ext.first, "permessage-deflate"))
|
||||
{
|
||||
for(auto const& param : ext.second)
|
||||
{
|
||||
if(ci_equal(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(ci_equal(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(ci_equal(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(ci_equal(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 Fields>
|
||||
void
|
||||
pmd_write(Fields& fields, pmd_offer const& offer)
|
||||
{
|
||||
std::string s;
|
||||
s = "permessage-deflate";
|
||||
if(offer.server_max_window_bits != 0)
|
||||
{
|
||||
if(offer.server_max_window_bits != -1)
|
||||
{
|
||||
s += "; server_max_window_bits=";
|
||||
s += std::to_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 += std::to_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.replace("Sec-WebSocket-Extensions", s);
|
||||
}
|
||||
|
||||
// Negotiate a permessage-deflate client offer
|
||||
//
|
||||
template<class Fields>
|
||||
void
|
||||
pmd_negotiate(
|
||||
Fields& 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;
|
||||
|
||||
std::string 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 += std::to_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 += std::to_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 += std::to_string(
|
||||
config.client_max_window_bits);
|
||||
break;
|
||||
}
|
||||
if(config.accept)
|
||||
fields.replace("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;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// Decompress into a DynamicBuffer
|
||||
//
|
||||
template<class InflateStream, class DynamicBuffer>
|
||||
void
|
||||
inflate(
|
||||
InflateStream& zi,
|
||||
DynamicBuffer& dynabuf,
|
||||
boost::asio::const_buffer const& in,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
zlib::z_params zs;
|
||||
zs.avail_in = buffer_size(in);
|
||||
zs.next_in = buffer_cast<void const*>(in);
|
||||
for(;;)
|
||||
{
|
||||
// VFALCO we could be smarter about the size
|
||||
auto const bs = dynabuf.prepare(
|
||||
read_size_helper(dynabuf, 65536));
|
||||
auto const out = *bs.begin();
|
||||
zs.avail_out = buffer_size(out);
|
||||
zs.next_out = buffer_cast<void*>(out);
|
||||
zi.write(zs, zlib::Flush::sync, ec);
|
||||
dynabuf.commit(zs.total_out);
|
||||
zs.total_out = 0;
|
||||
if( ec == zlib::error::need_buffers ||
|
||||
ec == zlib::error::end_of_stream)
|
||||
{
|
||||
ec = {};
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
consuming_buffers<ConstBufferSequence>& cb,
|
||||
bool fin,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
BOOST_ASSERT(buffer_size(out) >= 6);
|
||||
zlib::z_params zs;
|
||||
zs.avail_in = 0;
|
||||
zs.next_in = nullptr;
|
||||
zs.avail_out = buffer_size(out);
|
||||
zs.next_out = buffer_cast<void*>(out);
|
||||
for(auto const& in : cb)
|
||||
{
|
||||
zs.avail_in = buffer_size(in);
|
||||
if(zs.avail_in == 0)
|
||||
continue;
|
||||
zs.next_in = buffer_cast<void const*>(in);
|
||||
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 == buffer_size(out));
|
||||
ec = {};
|
||||
break;
|
||||
}
|
||||
if(zs.avail_out == 0)
|
||||
{
|
||||
BOOST_ASSERT(zs.total_out == buffer_size(out));
|
||||
break;
|
||||
}
|
||||
BOOST_ASSERT(zs.avail_in == 0);
|
||||
}
|
||||
cb.consume(zs.total_in);
|
||||
if(zs.avail_out > 0 && fin)
|
||||
{
|
||||
auto const remain = 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 = {};
|
||||
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(
|
||||
buffer_cast<void*>(out), zs.total_out);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
out = buffer(
|
||||
buffer_cast<void*>(out), zs.total_out);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
568
include/beast/websocket/detail/stream_base.hpp
Normal file
568
include/beast/websocket/detail/stream_base.hpp
Normal file
@@ -0,0 +1,568 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_STREAM_BASE_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_STREAM_BASE_HPP
|
||||
|
||||
#include <beast/websocket/error.hpp>
|
||||
#include <beast/websocket/option.hpp>
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/detail/decorator.hpp>
|
||||
#include <beast/websocket/detail/frame.hpp>
|
||||
#include <beast/websocket/detail/invokable.hpp>
|
||||
#include <beast/websocket/detail/mask.hpp>
|
||||
#include <beast/websocket/detail/pmd_extension.hpp>
|
||||
#include <beast/websocket/detail/utf8_checker.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/zlib/deflate_stream.hpp>
|
||||
#include <beast/zlib/inflate_stream.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
/// Identifies the role of a WebSockets stream.
|
||||
enum class role_type
|
||||
{
|
||||
/// Stream is operating as a client.
|
||||
client,
|
||||
|
||||
/// Stream is operating as a server.
|
||||
server
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct stream_base
|
||||
{
|
||||
protected:
|
||||
friend class frame_test;
|
||||
|
||||
struct op {};
|
||||
|
||||
detail::maskgen maskgen_; // source of mask keys
|
||||
decorator_type d_; // adorns http messages
|
||||
bool keep_alive_ = false; // close on failed upgrade
|
||||
std::size_t rd_msg_max_ =
|
||||
16 * 1024 * 1024; // max message size
|
||||
bool wr_autofrag_ = true; // auto fragment
|
||||
std::size_t wr_buf_size_ = 4096; // write buffer size
|
||||
std::size_t rd_buf_size_ = 4096; // read buffer size
|
||||
opcode wr_opcode_ = opcode::text; // outgoing message type
|
||||
ping_cb ping_cb_; // ping callback
|
||||
role_type role_; // server or client
|
||||
bool failed_; // the connection failed
|
||||
|
||||
bool wr_close_; // sent close frame
|
||||
op* wr_block_; // op currenly writing
|
||||
|
||||
ping_data* ping_data_; // where to put the payload
|
||||
invokable rd_op_; // read parking
|
||||
invokable wr_op_; // write parking
|
||||
invokable ping_op_; // ping parking
|
||||
close_reason cr_; // set from received close frame
|
||||
|
||||
// State information for the message being received
|
||||
//
|
||||
struct rd_t
|
||||
{
|
||||
// opcode of current message being read
|
||||
opcode op;
|
||||
|
||||
// `true` if the next frame is a continuation.
|
||||
bool cont;
|
||||
|
||||
// Checks that test messages are valid utf8
|
||||
detail::utf8_checker utf8;
|
||||
|
||||
// Size of the current message so far.
|
||||
std::uint64_t size;
|
||||
|
||||
// Size of the read buffer.
|
||||
// This gets set to the read buffer size option at the
|
||||
// beginning of sending a message, so that the option can be
|
||||
// changed mid-send without affecting the current message.
|
||||
std::size_t buf_size;
|
||||
|
||||
// The read buffer. Used for compression and masking.
|
||||
std::unique_ptr<std::uint8_t[]> buf;
|
||||
};
|
||||
|
||||
rd_t rd_;
|
||||
|
||||
// State information for the message being sent
|
||||
//
|
||||
struct wr_t
|
||||
{
|
||||
// `true` if next frame is a continuation,
|
||||
// `false` if next frame starts a new message
|
||||
bool cont;
|
||||
|
||||
// `true` if this message should be auto-fragmented
|
||||
// This gets set to the auto-fragment option at the beginning
|
||||
// of sending a message, so that the option can be changed
|
||||
// mid-send without affecting the current message.
|
||||
bool autofrag;
|
||||
|
||||
// `true` if this message should be compressed.
|
||||
// This gets set to the compress option at the beginning of
|
||||
// of sending a message, so that the option can be changed
|
||||
// mid-send without affecting the current message.
|
||||
bool compress;
|
||||
|
||||
// Size of the write buffer.
|
||||
// This gets set to the write buffer size option at the
|
||||
// beginning of sending a message, so that the option can be
|
||||
// changed mid-send without affecting the current message.
|
||||
std::size_t buf_size;
|
||||
|
||||
// The write buffer. Used for compression and masking.
|
||||
// The buffer is allocated or reallocated at the beginning of
|
||||
// sending a message.
|
||||
std::unique_ptr<std::uint8_t[]> buf;
|
||||
};
|
||||
|
||||
wr_t wr_;
|
||||
|
||||
// State information for the permessage-deflate extension
|
||||
struct pmd_t
|
||||
{
|
||||
// `true` if current read message is compressed
|
||||
bool rd_set;
|
||||
|
||||
zlib::deflate_stream zo;
|
||||
zlib::inflate_stream zi;
|
||||
};
|
||||
|
||||
// If not engaged, then permessage-deflate is not
|
||||
// enabled for the currently active session.
|
||||
std::unique_ptr<pmd_t> pmd_;
|
||||
|
||||
// Local options for permessage-deflate
|
||||
permessage_deflate pmd_opts_;
|
||||
|
||||
// Offer for clients, negotiated result for servers
|
||||
pmd_offer pmd_config_;
|
||||
|
||||
stream_base(stream_base&&) = default;
|
||||
stream_base(stream_base const&) = delete;
|
||||
stream_base& operator=(stream_base&&) = default;
|
||||
stream_base& operator=(stream_base const&) = delete;
|
||||
|
||||
stream_base()
|
||||
: d_(detail::default_decorator{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
open(role_type role);
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
close();
|
||||
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_fh1(detail::frame_header& fh,
|
||||
DynamicBuffer& db, close_code::value& code);
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
read_fh2(detail::frame_header& fh,
|
||||
DynamicBuffer& db, close_code::value& code);
|
||||
|
||||
// Called before receiving the first frame of each message
|
||||
template<class = void>
|
||||
void
|
||||
rd_begin();
|
||||
|
||||
// Called before sending the first frame of each message
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
wr_begin();
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
write_close(DynamicBuffer& db, close_reason const& rc);
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
write_ping(DynamicBuffer& db, opcode op, ping_data const& data);
|
||||
};
|
||||
|
||||
template<class>
|
||||
void
|
||||
stream_base::
|
||||
open(role_type role)
|
||||
{
|
||||
// VFALCO TODO analyze and remove dupe code in reset()
|
||||
role_ = role;
|
||||
failed_ = false;
|
||||
rd_.cont = false;
|
||||
wr_close_ = false;
|
||||
wr_block_ = nullptr; // should be nullptr on close anyway
|
||||
ping_data_ = nullptr; // should be nullptr on close anyway
|
||||
|
||||
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>
|
||||
void
|
||||
stream_base::
|
||||
close()
|
||||
{
|
||||
rd_.buf.reset();
|
||||
wr_.buf.reset();
|
||||
pmd_.reset();
|
||||
}
|
||||
|
||||
// Read fixed frame header from buffer
|
||||
// Requires at least 2 bytes
|
||||
//
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
stream_base::
|
||||
read_fh1(detail::frame_header& fh,
|
||||
DynamicBuffer& db, close_code::value& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const err =
|
||||
[&](close_code::value cv)
|
||||
{
|
||||
code = cv;
|
||||
return 0;
|
||||
};
|
||||
std::uint8_t b[2];
|
||||
BOOST_ASSERT(buffer_size(db.data()) >= sizeof(b));
|
||||
db.consume(buffer_copy(buffer(b), db.data()));
|
||||
std::size_t need;
|
||||
fh.len = b[1] & 0x7f;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126: need = 2; break;
|
||||
case 127: need = 8; break;
|
||||
default:
|
||||
need = 0;
|
||||
}
|
||||
fh.mask = (b[1] & 0x80) != 0;
|
||||
if(fh.mask)
|
||||
need += 4;
|
||||
fh.op = static_cast<opcode>(b[0] & 0x0f);
|
||||
fh.fin = (b[0] & 0x80) != 0;
|
||||
fh.rsv1 = (b[0] & 0x40) != 0;
|
||||
fh.rsv2 = (b[0] & 0x20) != 0;
|
||||
fh.rsv3 = (b[0] & 0x10) != 0;
|
||||
switch(fh.op)
|
||||
{
|
||||
case opcode::binary:
|
||||
case 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 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(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)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// masked frame from server
|
||||
if(role_ == role_type::client && fh.mask)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
code = close_code::none;
|
||||
return need;
|
||||
}
|
||||
|
||||
// Decode variable frame header from buffer
|
||||
//
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
stream_base::
|
||||
read_fh2(detail::frame_header& fh,
|
||||
DynamicBuffer& db, close_code::value& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using namespace boost::endian;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126:
|
||||
{
|
||||
std::uint8_t b[2];
|
||||
BOOST_ASSERT(buffer_size(db.data()) >= sizeof(b));
|
||||
db.consume(buffer_copy(buffer(b), db.data()));
|
||||
fh.len = big_uint16_to_native(&b[0]);
|
||||
// length not canonical
|
||||
if(fh.len < 126)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 127:
|
||||
{
|
||||
std::uint8_t b[8];
|
||||
BOOST_ASSERT(buffer_size(db.data()) >= sizeof(b));
|
||||
db.consume(buffer_copy(buffer(b), db.data()));
|
||||
fh.len = big_uint64_to_native(&b[0]);
|
||||
// length not canonical
|
||||
if(fh.len < 65536)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
std::uint8_t b[4];
|
||||
BOOST_ASSERT(buffer_size(db.data()) >= sizeof(b));
|
||||
db.consume(buffer_copy(buffer(b), db.data()));
|
||||
fh.key = little_uint32_to_native(&b[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// initialize this otherwise operator== breaks
|
||||
fh.key = 0;
|
||||
}
|
||||
if(! is_control(fh.op))
|
||||
{
|
||||
if(fh.op != opcode::cont)
|
||||
{
|
||||
rd_.size = 0;
|
||||
rd_.op = fh.op;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rd_.size > (std::numeric_limits<
|
||||
std::uint64_t>::max)() - fh.len)
|
||||
{
|
||||
code = close_code::too_big;
|
||||
return;
|
||||
}
|
||||
}
|
||||
rd_.cont = ! fh.fin;
|
||||
}
|
||||
code = close_code::none;
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
stream_base::
|
||||
rd_begin()
|
||||
{
|
||||
// Maintain the read buffer
|
||||
if(pmd_)
|
||||
{
|
||||
if(! rd_.buf || rd_.buf_size != rd_buf_size_)
|
||||
{
|
||||
rd_.buf_size = rd_buf_size_;
|
||||
rd_.buf.reset(new std::uint8_t[rd_.buf_size]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
stream_base::
|
||||
wr_begin()
|
||||
{
|
||||
wr_.autofrag = wr_autofrag_;
|
||||
wr_.compress = static_cast<bool>(pmd_);
|
||||
|
||||
// Maintain the write buffer
|
||||
if( wr_.compress ||
|
||||
role_ == detail::role_type::client)
|
||||
{
|
||||
if(! wr_.buf || wr_.buf_size != wr_buf_size_)
|
||||
{
|
||||
wr_.buf_size = wr_buf_size_;
|
||||
wr_.buf.reset(new std::uint8_t[wr_.buf_size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wr_.buf_size = wr_buf_size_;
|
||||
wr_.buf.reset();
|
||||
}
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
stream_base::
|
||||
write_close(DynamicBuffer& db, close_reason const& cr)
|
||||
{
|
||||
using namespace boost::endian;
|
||||
frame_header fh;
|
||||
fh.op = 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();
|
||||
fh.mask = role_ == detail::role_type::client;
|
||||
if(fh.mask)
|
||||
fh.key = maskgen_();
|
||||
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 b[2];
|
||||
::new(&b[0]) big_uint16_buf_t{
|
||||
(std::uint16_t)cr.code};
|
||||
auto d = db.prepare(2);
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::buffer(b));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
db.commit(2);
|
||||
}
|
||||
if(! cr.reason.empty())
|
||||
{
|
||||
auto d = db.prepare(cr.reason.size());
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::const_buffer(
|
||||
cr.reason.data(), cr.reason.size()));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
db.commit(cr.reason.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
stream_base::
|
||||
write_ping(
|
||||
DynamicBuffer& db, opcode op, ping_data const& data)
|
||||
{
|
||||
frame_header fh;
|
||||
fh.op = op;
|
||||
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 = maskgen_();
|
||||
detail::write(db, fh);
|
||||
if(data.empty())
|
||||
return;
|
||||
detail::prepared_key key;
|
||||
if(fh.mask)
|
||||
detail::prepare_key(key, fh.key);
|
||||
auto d = db.prepare(data.size());
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::const_buffers_1(
|
||||
data.data(), data.size()));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
db.commit(data.size());
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
317
include/beast/websocket/detail/utf8_checker.hpp
Normal file
317
include/beast/websocket/detail/utf8_checker.hpp
Normal file
@@ -0,0 +1,317 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
/* This is a modified work.
|
||||
|
||||
Original version and license:
|
||||
https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
|
||||
Additional changes:
|
||||
Optimized for predominantly 7-bit content, 2016
|
||||
https://github.com/uWebSockets/uWebSockets/blob/755bd362649c06abff102f18e273c5792c51c1a0/src/WebSocketProtocol.h#L198
|
||||
Copyright (c) 2016 Alex Hultman and contributors
|
||||
|
||||
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 acknowledgement 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.
|
||||
*/
|
||||
|
||||
/** 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;
|
||||
std::uint8_t* p_ = have_;
|
||||
std::uint8_t have_[4];
|
||||
|
||||
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_ = have_;
|
||||
}
|
||||
|
||||
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(is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for(auto const& b : bs)
|
||||
if(! write(buffer_cast<std::uint8_t const*>(b),
|
||||
buffer_size(b)))
|
||||
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*& in)
|
||||
{
|
||||
if (in[0] < 128)
|
||||
{
|
||||
++in;
|
||||
return true;
|
||||
}
|
||||
if ((in[0] & 0x60) == 0x40)
|
||||
{
|
||||
if ((in[1] & 0xc0) != 0x80)
|
||||
return false;
|
||||
in += 2;
|
||||
return true;
|
||||
}
|
||||
if ((in[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
if ((in[1] & 0xc0) != 0x80 ||
|
||||
(in[2] & 0xc0) != 0x80 ||
|
||||
(in[0] == 224 && in[1] < 160) ||
|
||||
(in[0] == 237 && in[1] > 159))
|
||||
return false;
|
||||
in += 3;
|
||||
return true;
|
||||
}
|
||||
if ((in[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
if (in[0] > 244 ||
|
||||
(in[1] & 0xc0) != 0x80 ||
|
||||
(in[2] & 0xc0) != 0x80 ||
|
||||
(in[3] & 0xc0) != 0x80 ||
|
||||
(in[0] == 240 && in[1] < 144) ||
|
||||
(in[0] == 244 && in[1] > 143))
|
||||
return false;
|
||||
in += 4;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto const valid_have =
|
||||
[&]()
|
||||
{
|
||||
if ((have_[0] & 0x60) == 0x40)
|
||||
return have_[0] <= 223;
|
||||
if ((have_[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
if (p_ - have_ > 1 &&
|
||||
((have_[1] & 0xc0) != 0x80 ||
|
||||
(have_[0] == 224 && have_[1] < 160) ||
|
||||
(have_[0] == 237 && have_[1] > 159)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if ((have_[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
auto const size = p_ - have_;
|
||||
if (size > 2 && (have_[2] & 0xc0) != 0x80)
|
||||
return false;
|
||||
if (size > 1 &&
|
||||
((have_[1] & 0xc0) != 0x80 ||
|
||||
(have_[0] == 240 && have_[1] < 144) ||
|
||||
(have_[0] == 244 && have_[1] > 143)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto const needed =
|
||||
[](std::uint8_t const in)
|
||||
{
|
||||
if (in < 128)
|
||||
return 1;
|
||||
if (in < 194)
|
||||
return 0;
|
||||
if (in < 224)
|
||||
return 2;
|
||||
if (in < 240)
|
||||
return 3;
|
||||
if (in < 245)
|
||||
return 4;
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto const end = in + size;
|
||||
if (need_ > 0)
|
||||
{
|
||||
auto n = (std::min)(size, need_);
|
||||
size -= n;
|
||||
need_ -= n;
|
||||
while(n--)
|
||||
*p_++ = *in++;
|
||||
if(need_ > 0)
|
||||
{
|
||||
BOOST_ASSERT(in == end);
|
||||
return valid_have();
|
||||
}
|
||||
std::uint8_t const* p = &have_[0];
|
||||
if (! valid(p))
|
||||
return false;
|
||||
p_ = have_;
|
||||
}
|
||||
|
||||
auto last = in + size - 7;
|
||||
while(in < last)
|
||||
{
|
||||
#if BEAST_WEBSOCKET_NO_UNALIGNED_READ
|
||||
auto constexpr align = sizeof(std::size_t) - 1;
|
||||
auto constexpr mask = static_cast<
|
||||
std::size_t>(0x8080808080808080 &
|
||||
~std::size_t{0});
|
||||
if(
|
||||
((reinterpret_cast<
|
||||
std::uintptr_t>(in) & align) == 0) &&
|
||||
(*reinterpret_cast<
|
||||
std::size_t const*>(in) & mask) == 0)
|
||||
in += sizeof(std::size_t);
|
||||
else if(! valid(in))
|
||||
return false;
|
||||
#else
|
||||
auto constexpr mask = static_cast<
|
||||
std::size_t>(0x8080808080808080 &
|
||||
~std::size_t{0});
|
||||
if(
|
||||
(*reinterpret_cast<
|
||||
std::size_t const*>(in) & mask) == 0)
|
||||
in += sizeof(std::size_t);
|
||||
else if(! valid(in))
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
last += 4;
|
||||
while(in < last)
|
||||
if(! valid(in))
|
||||
return false;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
auto n = end - in;
|
||||
if(! n)
|
||||
break;
|
||||
auto const need = needed(*in);
|
||||
if (need == 0)
|
||||
return false;
|
||||
if(need <= n)
|
||||
{
|
||||
if(! valid(in))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
need_ = need - n;
|
||||
while(n--)
|
||||
*p_++ = *in++;
|
||||
return valid_have();
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
#endif
|
||||
59
include/beast/websocket/error.hpp
Normal file
59
include/beast/websocket/error.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_ERROR_HPP
|
||||
#define BEAST_WEBSOCKET_ERROR_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
|
||||
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 request failed, connection is closed
|
||||
handshake_failed,
|
||||
|
||||
/// Upgrade request failed, but connection is still open
|
||||
keep_alive,
|
||||
|
||||
/// HTTP response is malformed
|
||||
response_malformed,
|
||||
|
||||
/// HTTP response failed the upgrade
|
||||
response_failed,
|
||||
|
||||
/// Upgrade request denied for invalid fields.
|
||||
response_denied,
|
||||
|
||||
/// Upgrade request is malformed
|
||||
request_malformed,
|
||||
|
||||
/// Upgrade request fields incorrect
|
||||
request_invalid,
|
||||
|
||||
/// Upgrade request denied
|
||||
request_denied,
|
||||
|
||||
/// General WebSocket error
|
||||
general
|
||||
};
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#include <beast/websocket/impl/error.ipp>
|
||||
|
||||
#endif
|
||||
426
include/beast/websocket/impl/accept.ipp
Normal file
426
include/beast/websocket/impl/accept.ipp
Normal file
@@ -0,0 +1,426 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
||||
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/prepare_buffers.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Respond to an upgrade HTTP request
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::response_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
http::response<http::string_body> res;
|
||||
error_code final_ec;
|
||||
int state = 0;
|
||||
|
||||
template<class Body, class Fields>
|
||||
data(Handler&, stream<NextLayer>& ws_,
|
||||
http::request<Body, Fields> const& req,
|
||||
bool cont_)
|
||||
: cont(cont_)
|
||||
, ws(ws_)
|
||||
, res(ws_.build_response(req))
|
||||
{
|
||||
// can't call stream::reset() here
|
||||
// otherwise accept_op will malfunction
|
||||
//
|
||||
if(res.status != 101)
|
||||
final_ec = error::handshake_failed;
|
||||
}
|
||||
};
|
||||
|
||||
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)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()(
|
||||
error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, response_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, response_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(response_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, response_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::response_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
// send response
|
||||
d.state = 1;
|
||||
http::async_write(d.ws.next_layer(),
|
||||
d.res, std::move(*this));
|
||||
return;
|
||||
|
||||
// sent response
|
||||
case 1:
|
||||
d.state = 99;
|
||||
ec = d.final_ec;
|
||||
if(! ec)
|
||||
{
|
||||
pmd_read(
|
||||
d.ws.pmd_config_, d.res.fields);
|
||||
d.ws.open(detail::role_type::server);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// read and respond to an upgrade request
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::accept_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
http::request<http::string_body> req;
|
||||
int state = 0;
|
||||
|
||||
template<class Buffers>
|
||||
data(Handler& handler, stream<NextLayer>& ws_,
|
||||
Buffers const& buffers)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, ws(ws_)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
ws.reset();
|
||||
ws.stream_.buffer().commit(buffer_copy(
|
||||
ws.stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
}
|
||||
};
|
||||
|
||||
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)...)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, accept_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, accept_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(accept_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, accept_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::accept_op<Handler>::
|
||||
operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
beast::detail::ignore_unused(bytes_transferred);
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
// read message
|
||||
d.state = 1;
|
||||
http::async_read(d.ws.next_layer(),
|
||||
d.ws.stream_.buffer(), d.req,
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got message
|
||||
case 1:
|
||||
{
|
||||
// respond to request
|
||||
auto& ws = d.ws;
|
||||
auto req = std::move(d.req);
|
||||
response_op<Handler>{
|
||||
d_.release_handler(), ws, req, true};
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class AcceptHandler>
|
||||
typename async_completion<
|
||||
AcceptHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_accept(AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
return async_accept(boost::asio::null_buffers{},
|
||||
std::forward<AcceptHandler>(handler));
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class AcceptHandler>
|
||||
typename async_completion<
|
||||
AcceptHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
AcceptHandler, void(error_code)
|
||||
> completion{handler};
|
||||
accept_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, bs};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields, class AcceptHandler>
|
||||
typename async_completion<
|
||||
AcceptHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_accept(http::request<Body, Fields> const& req,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
beast::async_completion<
|
||||
AcceptHandler, void(error_code)
|
||||
> completion{handler};
|
||||
reset();
|
||||
response_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, req,
|
||||
beast_asio_helpers::
|
||||
is_continuation(completion.handler)};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept()
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
accept(boost::asio::null_buffers{}, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
accept(boost::asio::null_buffers{}, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
accept(buffers, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
reset();
|
||||
stream_.buffer().commit(buffer_copy(
|
||||
stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
http::request<http::string_body> m;
|
||||
http::read(next_layer(), stream_.buffer(), m, ec);
|
||||
if(ec)
|
||||
return;
|
||||
accept(m, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::request<Body, Fields> const& request)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
accept(request, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::request<Body, Fields> const& req,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
reset();
|
||||
auto const res = build_response(req);
|
||||
http::write(stream_, res, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(res.status != 101)
|
||||
{
|
||||
ec = error::handshake_failed;
|
||||
// VFALCO TODO Respect keep alive setting, perform
|
||||
// teardown if Connection: close.
|
||||
return;
|
||||
}
|
||||
pmd_read(pmd_config_, req.fields);
|
||||
open(detail::role_type::server);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
248
include/beast/websocket/impl/close.ipp
Normal file
248
include/beast/websocket/impl/close.ipp
Normal file
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_CLOSE_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_CLOSE_IPP
|
||||
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/static_streambuf.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// send the close message and wait for the response
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::close_op
|
||||
{
|
||||
using fb_type = detail::frame_streambuf;
|
||||
|
||||
struct data : op
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
close_reason cr;
|
||||
fb_type fb;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, stream<NextLayer>& ws_,
|
||||
close_reason const& cr_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, ws(ws_)
|
||||
, cr(cr_)
|
||||
{
|
||||
ws.template write_close<
|
||||
static_streambuf>(fb, cr);
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
close_op(close_op&&) = default;
|
||||
close_op(close_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
close_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
(*this)(error_code{});
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t);
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, close_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, close_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(close_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, close_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::close_op<Handler>::
|
||||
operator()(error_code ec, std::size_t)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(ec)
|
||||
d.ws.failed_ = true;
|
||||
(*this)(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::close_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(ec)
|
||||
goto upcall;
|
||||
for(;;)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 2;
|
||||
d.ws.wr_op_.template emplace<
|
||||
close_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted));
|
||||
return;
|
||||
}
|
||||
d.ws.wr_block_ = &d;
|
||||
// [[fallthrough]]
|
||||
|
||||
case 1:
|
||||
// send close frame
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
d.state = 99;
|
||||
d.ws.wr_close_ = true;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
d.state = 3;
|
||||
// The current context is safe but might not be
|
||||
// the same as the one for this operation (since
|
||||
// we are being called from a write operation).
|
||||
// Call post to make sure we are invoked the same
|
||||
// way as the final handler for this operation.
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec));
|
||||
return;
|
||||
|
||||
case 3:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
goto upcall;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
|
||||
case 99:
|
||||
goto upcall;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke();
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class CloseHandler>
|
||||
typename async_completion<
|
||||
CloseHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_close(close_reason const& cr, CloseHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
beast::async_completion<
|
||||
CloseHandler, void(error_code)
|
||||
> completion{handler};
|
||||
close_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, cr};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
close(close_reason const& cr)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
close(cr, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
close(close_reason const& cr, error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
BOOST_ASSERT(! wr_close_);
|
||||
wr_close_ = true;
|
||||
detail::frame_streambuf fb;
|
||||
write_close<static_streambuf>(fb, cr);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
failed_ = ec != 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
100
include/beast/websocket/impl/error.ipp
Normal file
100
include/beast/websocket/impl/error.ipp
Normal file
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_ERROR_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_ERROR_IPP
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::websocket::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
class websocket_error_category : public error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "websocket";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
case error::closed: return "WebSocket connection closed normally";
|
||||
case error::failed: return "WebSocket connection failed due to a protocol violation";
|
||||
case error::handshake_failed: return "WebSocket Upgrade handshake failed";
|
||||
case error::keep_alive: return "WebSocket Upgrade handshake failed but connection is still open";
|
||||
|
||||
case error::response_malformed: return "malformed HTTP response";
|
||||
case error::response_failed: return "upgrade request failed";
|
||||
case error::response_denied: return "upgrade request denied";
|
||||
case error::request_malformed: return "malformed HTTP request";
|
||||
case error::request_invalid: return "upgrade request invalid";
|
||||
case error::request_denied: return "upgrade request denied";
|
||||
default:
|
||||
return "websocket 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 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
|
||||
|
||||
#endif
|
||||
211
include/beast/websocket/impl/handshake.ipp
Normal file
211
include/beast/websocket/impl/handshake.ipp
Normal file
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
|
||||
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// send the upgrade request and process the response
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::handshake_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
std::string key;
|
||||
http::request<http::empty_body> req;
|
||||
http::response<http::string_body> resp;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, stream<NextLayer>& ws_,
|
||||
boost::string_ref const& host,
|
||||
boost::string_ref const& resource)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, ws(ws_)
|
||||
, req(ws.build_request(host, resource, key))
|
||||
{
|
||||
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)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, handshake_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, handshake_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(handshake_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, handshake_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::handshake_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// send http upgrade
|
||||
d.state = 1;
|
||||
// VFALCO Do we need the ability to move
|
||||
// a message on the async_write?
|
||||
pmd_read(
|
||||
d.ws.pmd_config_, d.req.fields);
|
||||
http::async_write(d.ws.stream_,
|
||||
d.req, std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
// sent upgrade
|
||||
case 1:
|
||||
// read http response
|
||||
d.state = 2;
|
||||
http::async_read(d.ws.next_layer(),
|
||||
d.ws.stream_.buffer(), d.resp,
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got response
|
||||
case 2:
|
||||
{
|
||||
d.ws.do_response(d.resp, d.key, ec);
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class HandshakeHandler>
|
||||
typename async_completion<
|
||||
HandshakeHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_handshake(boost::string_ref const& host,
|
||||
boost::string_ref const& resource, HandshakeHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
beast::async_completion<
|
||||
HandshakeHandler, void(error_code)
|
||||
> completion{handler};
|
||||
handshake_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, host, resource};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake(boost::string_ref const& host,
|
||||
boost::string_ref const& resource)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
error_code ec;
|
||||
handshake(host, resource, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake(boost::string_ref const& host,
|
||||
boost::string_ref const& resource, error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
reset();
|
||||
std::string key;
|
||||
{
|
||||
auto const req =
|
||||
build_request(host, resource, key);
|
||||
pmd_read(pmd_config_, req.fields);
|
||||
http::write(stream_, req, ec);
|
||||
}
|
||||
if(ec)
|
||||
return;
|
||||
http::response<http::string_body> res;
|
||||
http::read(next_layer(), stream_.buffer(), res, ec);
|
||||
if(ec)
|
||||
return;
|
||||
do_response(res, key, ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
281
include/beast/websocket/impl/ping.ipp
Normal file
281
include/beast/websocket/impl/ping.ipp
Normal file
@@ -0,0 +1,281 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_PING_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_PING_IPP
|
||||
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/websocket/detail/frame.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// write a ping frame
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::ping_op
|
||||
{
|
||||
struct data : op
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
detail::frame_streambuf fb;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, stream<NextLayer>& ws_,
|
||||
opcode op_, ping_data const& payload)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, ws(ws_)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
ws.template write_ping<
|
||||
static_streambuf>(fb, op_, payload);
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
ping_op(ping_op&&) = default;
|
||||
ping_op(ping_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
ping_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
(*this)(error_code{});
|
||||
}
|
||||
|
||||
void operator()(error_code ec, std::size_t);
|
||||
|
||||
void operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, ping_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, ping_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(ping_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, ping_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, 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_;
|
||||
if(ec)
|
||||
d.ws.failed_ = true;
|
||||
(*this)(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
ping_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(ec)
|
||||
goto upcall;
|
||||
for(;;)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 2;
|
||||
d.ws.ping_op_.template emplace<
|
||||
ping_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted));
|
||||
return;
|
||||
}
|
||||
d.ws.wr_block_ = &d;
|
||||
// [[fallthrough]]
|
||||
|
||||
case 1:
|
||||
// send ping frame
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
d.state = 99;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
d.state = 3;
|
||||
// The current context is safe but might not be
|
||||
// the same as the one for this operation (since
|
||||
// we are being called from a write operation).
|
||||
// Call post to make sure we are invoked the same
|
||||
// way as the final handler for this operation.
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec));
|
||||
return;
|
||||
|
||||
case 3:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
goto upcall;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
|
||||
case 99:
|
||||
goto upcall;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.wr_op_.maybe_invoke();
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_ping(ping_data const& payload, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
beast::async_completion<
|
||||
WriteHandler, void(error_code)
|
||||
> completion{handler};
|
||||
ping_op<decltype(completion.handler)>{
|
||||
completion.handler, *this,
|
||||
opcode::ping, payload};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_pong(ping_data const& payload, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements requirements not met");
|
||||
beast::async_completion<
|
||||
WriteHandler, void(error_code)
|
||||
> completion{handler};
|
||||
ping_op<decltype(completion.handler)>{
|
||||
completion.handler, *this,
|
||||
opcode::pong, payload};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
ping(ping_data const& payload)
|
||||
{
|
||||
error_code ec;
|
||||
ping(payload, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
ping(ping_data const& payload, error_code& ec)
|
||||
{
|
||||
detail::frame_streambuf db;
|
||||
write_ping<static_streambuf>(
|
||||
db, opcode::ping, payload);
|
||||
boost::asio::write(stream_, db.data(), ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
pong(ping_data const& payload)
|
||||
{
|
||||
error_code ec;
|
||||
pong(payload, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
pong(ping_data const& payload, error_code& ec)
|
||||
{
|
||||
detail::frame_streambuf db;
|
||||
write_ping<static_streambuf>(
|
||||
db, opcode::pong, payload);
|
||||
boost::asio::write(stream_, db.data(), ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
1142
include/beast/websocket/impl/read.ipp
Normal file
1142
include/beast/websocket/impl/read.ipp
Normal file
File diff suppressed because it is too large
Load Diff
153
include/beast/websocket/impl/ssl.ipp
Normal file
153
include/beast/websocket/impl/ssl.ipp
Normal file
@@ -0,0 +1,153 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
|
||||
#define BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
|
||||
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
|
||||
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, class Handler>
|
||||
class teardown_ssl_op
|
||||
{
|
||||
using stream_type =
|
||||
boost::asio::ssl::stream<AsyncStream>;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
stream_type& stream;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, stream_type& stream_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, stream(stream_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
template<class DeducedHandler>
|
||||
explicit
|
||||
teardown_ssl_op(
|
||||
DeducedHandler&& h, stream_type& stream)
|
||||
: d_(std::forward<DeducedHandler>(h), stream)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(std::size_t size,
|
||||
teardown_ssl_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(void* p,
|
||||
std::size_t size, teardown_ssl_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(
|
||||
teardown_ssl_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f,
|
||||
teardown_ssl_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class AsyncStream, class Handler>
|
||||
void
|
||||
teardown_ssl_op<AsyncStream, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(!ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 99;
|
||||
d.stream.async_shutdown(*this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class AsyncStream>
|
||||
void
|
||||
teardown(teardown_tag,
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
error_code& ec)
|
||||
{
|
||||
stream.shutdown(ec);
|
||||
}
|
||||
|
||||
template<class AsyncStream, class TeardownHandler>
|
||||
void
|
||||
async_teardown(teardown_tag,
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_CompletionHandler<
|
||||
TeardownHandler, void(error_code)>::value,
|
||||
"TeardownHandler requirements not met");
|
||||
detail::teardown_ssl_op<AsyncStream, typename std::decay<
|
||||
TeardownHandler>::type>{std::forward<TeardownHandler>(
|
||||
handler), stream};
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
230
include/beast/websocket/impl/stream.ipp
Normal file
230
include/beast/websocket/impl/stream.ipp
Normal file
@@ -0,0 +1,230 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_STREAM_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_STREAM_IPP
|
||||
|
||||
#include <beast/websocket/teardown.hpp>
|
||||
#include <beast/websocket/detail/hybi13.hpp>
|
||||
#include <beast/websocket/detail/pmd_extension.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/http/reason.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/prepare_buffers.hpp>
|
||||
#include <beast/core/static_streambuf.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
template<class NextLayer>
|
||||
template<class... Args>
|
||||
stream<NextLayer>::
|
||||
stream(Args&&... args)
|
||||
: stream_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
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)
|
||||
throw std::invalid_argument{
|
||||
"invalid server_max_window_bits"};
|
||||
if( o.client_max_window_bits > 15 ||
|
||||
o.client_max_window_bits < 9)
|
||||
throw std::invalid_argument{
|
||||
"invalid client_max_window_bits"};
|
||||
if( o.compLevel < 0 ||
|
||||
o.compLevel > 9)
|
||||
throw std::invalid_argument{
|
||||
"invalid compLevel"};
|
||||
if( o.memLevel < 1 ||
|
||||
o.memLevel > 9)
|
||||
throw std::invalid_argument{
|
||||
"invalid memLevel"};
|
||||
pmd_opts_ = o;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
reset()
|
||||
{
|
||||
failed_ = false;
|
||||
rd_.cont = false;
|
||||
wr_close_ = false;
|
||||
wr_.cont = false;
|
||||
wr_block_ = nullptr; // should be nullptr on close anyway
|
||||
ping_data_ = nullptr; // should be nullptr on close anyway
|
||||
|
||||
stream_.buffer().consume(
|
||||
stream_.buffer().size());
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
http::request<http::empty_body>
|
||||
stream<NextLayer>::
|
||||
build_request(boost::string_ref const& host,
|
||||
boost::string_ref const& resource, std::string& key)
|
||||
{
|
||||
http::request<http::empty_body> req;
|
||||
req.url = { resource.data(), resource.size() };
|
||||
req.version = 11;
|
||||
req.method = "GET";
|
||||
req.fields.insert("Host", host);
|
||||
req.fields.insert("Upgrade", "websocket");
|
||||
key = detail::make_sec_ws_key(maskgen_);
|
||||
req.fields.insert("Sec-WebSocket-Key", key);
|
||||
req.fields.insert("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.fields, config);
|
||||
}
|
||||
d_(req);
|
||||
http::prepare(req, http::connection::upgrade);
|
||||
return req;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields>
|
||||
http::response<http::string_body>
|
||||
stream<NextLayer>::
|
||||
build_response(http::request<Body, Fields> const& req)
|
||||
{
|
||||
auto err =
|
||||
[&](std::string const& text)
|
||||
{
|
||||
http::response<http::string_body> res;
|
||||
res.status = 400;
|
||||
res.reason = http::reason_string(res.status);
|
||||
res.version = req.version;
|
||||
res.body = text;
|
||||
d_(res);
|
||||
prepare(res,
|
||||
(is_keep_alive(req) && keep_alive_) ?
|
||||
http::connection::keep_alive :
|
||||
http::connection::close);
|
||||
return res;
|
||||
};
|
||||
if(req.version < 11)
|
||||
return err("HTTP version 1.1 required");
|
||||
if(req.method != "GET")
|
||||
return err("Wrong method");
|
||||
if(! is_upgrade(req))
|
||||
return err("Expected Upgrade request");
|
||||
if(! req.fields.exists("Host"))
|
||||
return err("Missing Host");
|
||||
if(! req.fields.exists("Sec-WebSocket-Key"))
|
||||
return err("Missing Sec-WebSocket-Key");
|
||||
if(! http::token_list{req.fields["Upgrade"]}.exists("websocket"))
|
||||
return err("Missing websocket Upgrade token");
|
||||
{
|
||||
auto const version =
|
||||
req.fields["Sec-WebSocket-Version"];
|
||||
if(version.empty())
|
||||
return err("Missing Sec-WebSocket-Version");
|
||||
if(version != "13")
|
||||
{
|
||||
http::response<http::string_body> res;
|
||||
res.status = 426;
|
||||
res.reason = http::reason_string(res.status);
|
||||
res.version = req.version;
|
||||
res.fields.insert("Sec-WebSocket-Version", "13");
|
||||
d_(res);
|
||||
prepare(res,
|
||||
(is_keep_alive(req) && keep_alive_) ?
|
||||
http::connection::keep_alive :
|
||||
http::connection::close);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
http::response<http::string_body> res;
|
||||
{
|
||||
detail::pmd_offer offer;
|
||||
detail::pmd_offer unused;
|
||||
pmd_read(offer, req.fields);
|
||||
pmd_negotiate(
|
||||
res.fields, unused, offer, pmd_opts_);
|
||||
}
|
||||
res.status = 101;
|
||||
res.reason = http::reason_string(res.status);
|
||||
res.version = req.version;
|
||||
res.fields.insert("Upgrade", "websocket");
|
||||
{
|
||||
auto const key =
|
||||
req.fields["Sec-WebSocket-Key"];
|
||||
res.fields.insert("Sec-WebSocket-Accept",
|
||||
detail::make_sec_ws_accept(key));
|
||||
}
|
||||
res.fields.replace("Server", "Beast.WSProto");
|
||||
d_(res);
|
||||
http::prepare(res, http::connection::upgrade);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_response(http::response<Body, Fields> const& res,
|
||||
boost::string_ref const& key, error_code& ec)
|
||||
{
|
||||
// VFALCO Review these error codes
|
||||
auto fail = [&]{ ec = error::response_failed; };
|
||||
if(res.version < 11)
|
||||
return fail();
|
||||
if(res.status != 101)
|
||||
return fail();
|
||||
if(! is_upgrade(res))
|
||||
return fail();
|
||||
if(! http::token_list{res.fields["Upgrade"]}.exists("websocket"))
|
||||
return fail();
|
||||
if(! res.fields.exists("Sec-WebSocket-Accept"))
|
||||
return fail();
|
||||
if(res.fields["Sec-WebSocket-Accept"] !=
|
||||
detail::make_sec_ws_accept(key))
|
||||
return fail();
|
||||
detail::pmd_offer offer;
|
||||
pmd_read(offer, res.fields);
|
||||
// VFALCO see if offer satisfies pmd_config_,
|
||||
// return an error if not.
|
||||
pmd_config_ = offer; // overwrite for now
|
||||
open(detail::role_type::client);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
166
include/beast/websocket/impl/teardown.ipp
Normal file
166
include/beast/websocket/impl/teardown.ipp
Normal file
@@ -0,0 +1,166 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
|
||||
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Handler>
|
||||
class teardown_tcp_op
|
||||
{
|
||||
using socket_type =
|
||||
boost::asio::ip::tcp::socket;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
socket_type& socket;
|
||||
char buf[2048];
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, socket_type& socket_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, socket(socket_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
template<class DeducedHandler>
|
||||
teardown_tcp_op(
|
||||
DeducedHandler&& h,
|
||||
socket_type& socket)
|
||||
: d_(std::forward<DeducedHandler>(h), socket)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(std::size_t size,
|
||||
teardown_tcp_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(void* p,
|
||||
std::size_t size, teardown_tcp_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(teardown_tcp_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f,
|
||||
teardown_tcp_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Handler>
|
||||
void
|
||||
teardown_tcp_op<Handler>::
|
||||
operator()(error_code ec, std::size_t, bool again)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
d.socket.shutdown(
|
||||
boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
d.socket.async_read_some(
|
||||
buffer(d.buf), std::move(*this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
d.socket.close(ec);
|
||||
ec = error_code{};
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
void
|
||||
teardown(teardown_tag,
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
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(ec == boost::asio::error::eof)
|
||||
ec = error_code{};
|
||||
socket.close(ec);
|
||||
}
|
||||
|
||||
template<class TeardownHandler>
|
||||
inline
|
||||
void
|
||||
async_teardown(teardown_tag,
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_CompletionHandler<
|
||||
TeardownHandler, void(error_code)>::value,
|
||||
"TeardownHandler requirements not met");
|
||||
detail::teardown_tcp_op<typename std::decay<
|
||||
TeardownHandler>::type>{std::forward<
|
||||
TeardownHandler>(handler), socket};
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
950
include/beast/websocket/impl/write.ipp
Normal file
950
include/beast/websocket/impl/write.ipp
Normal file
@@ -0,0 +1,950 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_WRITE_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_WRITE_IPP
|
||||
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/prepare_buffers.hpp>
|
||||
#include <beast/core/static_streambuf.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/detail/clamp.hpp>
|
||||
#include <beast/websocket/detail/frame.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
class stream<NextLayer>::write_frame_op
|
||||
{
|
||||
struct data : op
|
||||
{
|
||||
Handler& handler;
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
consuming_buffers<Buffers> cb;
|
||||
bool fin;
|
||||
detail::frame_header fh;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::prepared_key key;
|
||||
std::uint64_t remain;
|
||||
int state = 0;
|
||||
int entry_state;
|
||||
|
||||
data(Handler& handler_, stream<NextLayer>& ws_,
|
||||
bool fin_, Buffers const& bs)
|
||||
: handler(handler_)
|
||||
, cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, ws(ws_)
|
||||
, cb(bs)
|
||||
, fin(fin_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_frame_op(write_frame_op&&) = default;
|
||||
write_frame_op(write_frame_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_frame_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
(*this)(error_code{}, 0, true);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0, true);
|
||||
}
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_frame_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_frame_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_frame_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_frame_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_frame_op<Buffers, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(ec)
|
||||
d.ws.failed_ = true;
|
||||
(*this)(ec, bytes_transferred, true);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_frame_op<Buffers, Handler>::
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
using beast::detail::clamp;
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
enum
|
||||
{
|
||||
do_init = 0,
|
||||
do_nomask_nofrag = 20,
|
||||
do_nomask_frag = 30,
|
||||
do_mask_nofrag = 40,
|
||||
do_mask_frag = 50,
|
||||
do_deflate = 60,
|
||||
do_maybe_suspend = 80,
|
||||
do_upcall = 99
|
||||
};
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(ec)
|
||||
goto upcall;
|
||||
for(;;)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case do_init:
|
||||
if(! d.ws.wr_.cont)
|
||||
{
|
||||
d.ws.wr_begin();
|
||||
d.fh.rsv1 = d.ws.wr_.compress;
|
||||
}
|
||||
else
|
||||
{
|
||||
d.fh.rsv1 = false;
|
||||
}
|
||||
d.fh.rsv2 = false;
|
||||
d.fh.rsv3 = false;
|
||||
d.fh.op = d.ws.wr_.cont ?
|
||||
opcode::cont : d.ws.wr_opcode_;
|
||||
d.fh.mask =
|
||||
d.ws.role_ == detail::role_type::client;
|
||||
|
||||
// entry_state determines which algorithm
|
||||
// we will use to send. If we suspend, we
|
||||
// will transition to entry_state + 1 on
|
||||
// the resume.
|
||||
if(d.ws.wr_.compress)
|
||||
{
|
||||
d.entry_state = do_deflate;
|
||||
}
|
||||
else if(! d.fh.mask)
|
||||
{
|
||||
if(! d.ws.wr_.autofrag)
|
||||
{
|
||||
d.entry_state = do_nomask_nofrag;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_.buf_size != 0);
|
||||
d.remain = buffer_size(d.cb);
|
||||
if(d.remain > d.ws.wr_.buf_size)
|
||||
d.entry_state = do_nomask_frag;
|
||||
else
|
||||
d.entry_state = do_nomask_nofrag;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(! d.ws.wr_.autofrag)
|
||||
{
|
||||
d.entry_state = do_mask_nofrag;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_.buf_size != 0);
|
||||
d.remain = buffer_size(d.cb);
|
||||
if(d.remain > d.ws.wr_.buf_size)
|
||||
d.entry_state = do_mask_frag;
|
||||
else
|
||||
d.entry_state = do_mask_nofrag;
|
||||
}
|
||||
}
|
||||
d.state = do_maybe_suspend;
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_nomask_nofrag:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
// [[fallthrough]]
|
||||
|
||||
case do_nomask_nofrag + 1:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
d.fh.fin = d.fin;
|
||||
d.fh.len = buffer_size(d.cb);
|
||||
detail::write<static_streambuf>(
|
||||
d.fh_buf, d.fh);
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame
|
||||
d.state = do_upcall;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(d.fh_buf.data(), d.cb),
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_nomask_frag:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
// [[fallthrough]]
|
||||
|
||||
case do_nomask_frag + 1:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
auto const n = clamp(
|
||||
d.remain, d.ws.wr_.buf_size);
|
||||
d.remain -= n;
|
||||
d.fh.len = n;
|
||||
d.fh.fin = d.fin ? d.remain == 0 : false;
|
||||
detail::write<static_streambuf>(
|
||||
d.fh_buf, d.fh);
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame
|
||||
d.state = d.remain == 0 ?
|
||||
do_upcall : do_nomask_frag + 2;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(d.fh_buf.data(),
|
||||
prepare_buffers(n, d.cb)),
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
case do_nomask_frag + 2:
|
||||
d.cb.consume(
|
||||
bytes_transferred - d.fh_buf.size());
|
||||
d.fh_buf.reset();
|
||||
d.fh.op = opcode::cont;
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
// Allow outgoing control frames to
|
||||
// be sent in between message frames:
|
||||
if(d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke())
|
||||
{
|
||||
d.state = do_maybe_suspend;
|
||||
d.ws.get_io_service().post(
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = d.entry_state;
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_mask_nofrag:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
// [[fallthrough]]
|
||||
|
||||
case do_mask_nofrag + 1:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
d.remain = buffer_size(d.cb);
|
||||
d.fh.fin = d.fin;
|
||||
d.fh.len = d.remain;
|
||||
d.fh.key = d.ws.maskgen_();
|
||||
detail::prepare_key(d.key, d.fh.key);
|
||||
detail::write<static_streambuf>(
|
||||
d.fh_buf, d.fh);
|
||||
auto const n =
|
||||
clamp(d.remain, d.ws.wr_.buf_size);
|
||||
auto const b =
|
||||
buffer(d.ws.wr_.buf.get(), n);
|
||||
buffer_copy(b, d.cb);
|
||||
detail::mask_inplace(b, d.key);
|
||||
d.remain -= n;
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame header and partial payload
|
||||
d.state = d.remain == 0 ?
|
||||
do_upcall : do_mask_nofrag + 2;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(d.fh_buf.data(), b),
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
case do_mask_nofrag + 2:
|
||||
{
|
||||
d.cb.consume(d.ws.wr_.buf_size);
|
||||
auto const n =
|
||||
clamp(d.remain, d.ws.wr_.buf_size);
|
||||
auto const b =
|
||||
buffer(d.ws.wr_.buf.get(), n);
|
||||
buffer_copy(b, d.cb);
|
||||
detail::mask_inplace(b, d.key);
|
||||
d.remain -= n;
|
||||
// Send parial payload
|
||||
if(d.remain == 0)
|
||||
d.state = do_upcall;
|
||||
boost::asio::async_write(
|
||||
d.ws.stream_, b, std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_mask_frag:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
// [[fallthrough]]
|
||||
|
||||
case do_mask_frag + 1:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
auto const n = clamp(
|
||||
d.remain, d.ws.wr_.buf_size);
|
||||
d.remain -= n;
|
||||
d.fh.len = n;
|
||||
d.fh.key = d.ws.maskgen_();
|
||||
d.fh.fin = d.fin ? d.remain == 0 : false;
|
||||
detail::prepare_key(d.key, d.fh.key);
|
||||
auto const b = buffer(
|
||||
d.ws.wr_.buf.get(), n);
|
||||
buffer_copy(b, d.cb);
|
||||
detail::mask_inplace(b, d.key);
|
||||
detail::write<static_streambuf>(
|
||||
d.fh_buf, d.fh);
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame
|
||||
d.state = d.remain == 0 ?
|
||||
do_upcall : do_mask_frag + 2;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(d.fh_buf.data(), b),
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
case do_mask_frag + 2:
|
||||
d.cb.consume(
|
||||
bytes_transferred - d.fh_buf.size());
|
||||
d.fh_buf.reset();
|
||||
d.fh.op = opcode::cont;
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
d.ws.wr_block_ = nullptr;
|
||||
// Allow outgoing control frames to
|
||||
// be sent in between message frames:
|
||||
if(d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke())
|
||||
{
|
||||
d.state = do_maybe_suspend;
|
||||
d.ws.get_io_service().post(
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = d.entry_state;
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_deflate:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
// [[fallthrough]]
|
||||
|
||||
case do_deflate + 1:
|
||||
{
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
auto b = buffer(d.ws.wr_.buf.get(),
|
||||
d.ws.wr_.buf_size);
|
||||
auto const more = detail::deflate(
|
||||
d.ws.pmd_->zo, b, d.cb, d.fin, ec);
|
||||
d.ws.failed_ = ec != 0;
|
||||
if(d.ws.failed_)
|
||||
goto upcall;
|
||||
auto const n = buffer_size(b);
|
||||
if(n == 0)
|
||||
{
|
||||
// The input was consumed, but there
|
||||
// is no output due to compression
|
||||
// latency.
|
||||
BOOST_ASSERT(! d.fin);
|
||||
BOOST_ASSERT(buffer_size(d.cb) == 0);
|
||||
|
||||
// We can skip the dispatch if the
|
||||
// asynchronous initiation function is
|
||||
// not on call stack but its hard to
|
||||
// figure out so be safe and dispatch.
|
||||
d.state = do_upcall;
|
||||
d.ws.get_io_service().post(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.fh.mask)
|
||||
{
|
||||
d.fh.key = d.ws.maskgen_();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, d.fh.key);
|
||||
detail::mask_inplace(b, key);
|
||||
}
|
||||
d.fh.fin = ! more;
|
||||
d.fh.len = n;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<static_streambuf>(fh_buf, d.fh);
|
||||
d.ws.wr_.cont = ! d.fin;
|
||||
// Send frame
|
||||
d.state = more ?
|
||||
do_deflate + 2 : do_deflate + 3;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(fh_buf.data(), b),
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
case do_deflate + 2:
|
||||
d.fh.op = opcode::cont;
|
||||
d.fh.rsv1 = false;
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
d.ws.wr_block_ = nullptr;
|
||||
// Allow outgoing control frames to
|
||||
// be sent in between message frames:
|
||||
if(d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke())
|
||||
{
|
||||
d.state = do_maybe_suspend;
|
||||
d.ws.get_io_service().post(
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = d.entry_state;
|
||||
break;
|
||||
|
||||
case do_deflate + 3:
|
||||
if(d.fh.fin && (
|
||||
(d.ws.role_ == detail::role_type::client &&
|
||||
d.ws.pmd_config_.client_no_context_takeover) ||
|
||||
(d.ws.role_ == detail::role_type::server &&
|
||||
d.ws.pmd_config_.server_no_context_takeover)))
|
||||
d.ws.pmd_->zo.reset();
|
||||
goto upcall;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_maybe_suspend:
|
||||
{
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = do_maybe_suspend + 1;
|
||||
d.ws.wr_op_.template emplace<
|
||||
write_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
d.state = do_upcall;
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted));
|
||||
return;
|
||||
}
|
||||
d.state = d.entry_state;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_maybe_suspend + 1:
|
||||
BOOST_ASSERT(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
d.state = do_maybe_suspend + 2;
|
||||
// The current context is safe but might not be
|
||||
// the same as the one for this operation (since
|
||||
// we are being called from a write operation).
|
||||
// Call post to make sure we are invoked the same
|
||||
// way as the final handler for this operation.
|
||||
d.ws.get_io_service().post(bind_handler(
|
||||
std::move(*this), ec));
|
||||
return;
|
||||
|
||||
case do_maybe_suspend + 2:
|
||||
BOOST_ASSERT(d.ws.wr_block_ == &d);
|
||||
if(d.ws.failed_ || d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
goto upcall;
|
||||
}
|
||||
d.state = d.entry_state + 1;
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_upcall:
|
||||
goto upcall;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
d.ws.rd_op_.maybe_invoke() ||
|
||||
d.ws.ping_op_.maybe_invoke();
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_write_frame(bool fin,
|
||||
ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
WriteHandler, void(error_code)
|
||||
> completion{handler};
|
||||
write_frame_op<ConstBufferSequence, decltype(
|
||||
completion.handler)>{completion.handler,
|
||||
*this, fin, bs};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_frame(bool fin, ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
write_frame(fin, buffers, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_frame(bool fin,
|
||||
ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using beast::detail::clamp;
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
detail::frame_header fh;
|
||||
if(! wr_.cont)
|
||||
{
|
||||
wr_begin();
|
||||
fh.rsv1 = wr_.compress;
|
||||
}
|
||||
else
|
||||
{
|
||||
fh.rsv1 = false;
|
||||
}
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
fh.op = wr_.cont ? opcode::cont : wr_opcode_;
|
||||
fh.mask = role_ == detail::role_type::client;
|
||||
auto remain = buffer_size(buffers);
|
||||
if(wr_.compress)
|
||||
{
|
||||
consuming_buffers<
|
||||
ConstBufferSequence> cb{buffers};
|
||||
for(;;)
|
||||
{
|
||||
auto b = buffer(
|
||||
wr_.buf.get(), wr_.buf_size);
|
||||
auto const more = detail::deflate(
|
||||
pmd_->zo, b, cb, fin, ec);
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
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 = maskgen_();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
detail::mask_inplace(b, key);
|
||||
}
|
||||
fh.fin = ! more;
|
||||
fh.len = n;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<static_streambuf>(fh_buf, fh);
|
||||
wr_.cont = ! fin;
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), b), ec);
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
if(! more)
|
||||
break;
|
||||
fh.op = opcode::cont;
|
||||
fh.rsv1 = false;
|
||||
}
|
||||
if(fh.fin && (
|
||||
(role_ == detail::role_type::client &&
|
||||
pmd_config_.client_no_context_takeover) ||
|
||||
(role_ == detail::role_type::server &&
|
||||
pmd_config_.server_no_context_takeover)))
|
||||
pmd_->zo.reset();
|
||||
return;
|
||||
}
|
||||
if(! fh.mask)
|
||||
{
|
||||
if(! wr_.autofrag)
|
||||
{
|
||||
// no mask, no autofrag
|
||||
fh.fin = fin;
|
||||
fh.len = remain;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<static_streambuf>(fh_buf, fh);
|
||||
wr_.cont = ! fin;
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), buffers), ec);
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no mask, autofrag
|
||||
BOOST_ASSERT(wr_.buf_size != 0);
|
||||
consuming_buffers<
|
||||
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_streambuf fh_buf;
|
||||
detail::write<static_streambuf>(fh_buf, fh);
|
||||
wr_.cont = ! fin;
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(),
|
||||
prepare_buffers(n, cb)), ec);
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
if(remain == 0)
|
||||
break;
|
||||
fh.op = opcode::cont;
|
||||
cb.consume(n);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(! wr_.autofrag)
|
||||
{
|
||||
// mask, no autofrag
|
||||
fh.fin = fin;
|
||||
fh.len = remain;
|
||||
fh.key = maskgen_();
|
||||
detail::prepared_key key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<static_streambuf>(fh_buf, fh);
|
||||
consuming_buffers<
|
||||
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_,
|
||||
buffer_cat(fh_buf.data(), b), ec);
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
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);
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
{
|
||||
// mask, autofrag
|
||||
BOOST_ASSERT(wr_.buf_size != 0);
|
||||
consuming_buffers<
|
||||
ConstBufferSequence> cb{buffers};
|
||||
for(;;)
|
||||
{
|
||||
fh.key = maskgen_();
|
||||
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_streambuf fh_buf;
|
||||
detail::write<static_streambuf>(fh_buf, fh);
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), b), ec);
|
||||
failed_ = ec != 0;
|
||||
if(failed_)
|
||||
return;
|
||||
if(remain == 0)
|
||||
break;
|
||||
fh.op = opcode::cont;
|
||||
cb.consume(n);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
class stream<NextLayer>::write_op
|
||||
{
|
||||
struct data : op
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
consuming_buffers<Buffers> cb;
|
||||
std::size_t remain;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, stream<NextLayer>& ws_,
|
||||
Buffers const& bs)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, ws(ws_)
|
||||
, cb(bs)
|
||||
, remain(boost::asio::buffer_size(cb))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
explicit
|
||||
write_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
ws, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_op<Buffers, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(! ec)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto const n = d.remain;
|
||||
d.remain -= n;
|
||||
auto const fin = d.remain <= 0;
|
||||
if(fin)
|
||||
d.state = 99;
|
||||
auto const pb = prepare_buffers(n, d.cb);
|
||||
d.cb.consume(n);
|
||||
d.ws.async_write_frame(fin, pb, std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
case 99:
|
||||
break;
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_write(ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
"AsyncStream requirements not met");
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
WriteHandler, void(error_code)> completion{handler};
|
||||
write_op<ConstBufferSequence, decltype(completion.handler)>{
|
||||
completion.handler, *this, bs};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
write(buffers, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write(ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
write_frame(true, buffers, ec);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
417
include/beast/websocket/option.hpp
Normal file
417
include/beast/websocket/option.hpp
Normal file
@@ -0,0 +1,417 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_OPTION_HPP
|
||||
#define BEAST_WEBSOCKET_OPTION_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/detail/decorator.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/** 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.
|
||||
|
||||
@note Objects of this type are used with
|
||||
@ref beast::websocket::stream::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the automatic fragmentation option:
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> stream(ios);
|
||||
stream.set_option(auto_fragment{true});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using auto_fragment = implementation_defined;
|
||||
#else
|
||||
struct auto_fragment
|
||||
{
|
||||
bool value;
|
||||
|
||||
explicit
|
||||
auto_fragment(bool v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** HTTP decorator option.
|
||||
|
||||
The decorator transforms the HTTP requests and responses used
|
||||
when requesting or responding to the WebSocket Upgrade. This may
|
||||
be used to set or change header fields. For example to set the
|
||||
Server or User-Agent fields. The default setting applies no
|
||||
transformation to the HTTP message.
|
||||
|
||||
The context in which the decorator is called depends on the
|
||||
type of operation performed:
|
||||
|
||||
@li For synchronous operations, the implementation will call the
|
||||
decorator before the operation unblocks.
|
||||
|
||||
@li For asynchronous operations, the implementation guarantees
|
||||
that calls to the decorator will be made from the same implicit
|
||||
or explicit strand used to call the asynchronous initiation
|
||||
function.
|
||||
|
||||
The default setting is no decorator.
|
||||
|
||||
@note Objects of this type are used with
|
||||
@ref beast::websocket::stream::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the decorator.
|
||||
@code
|
||||
struct identity
|
||||
{
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
operator()(http::message<isRequest, Body, Fields>& m)
|
||||
{
|
||||
if(isRequest)
|
||||
m.fields.replace("User-Agent", "MyClient");
|
||||
else
|
||||
m.fields.replace("Server", "MyServer");
|
||||
}
|
||||
};
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(decorate(identity{}));
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using decorate = implementation_defined;
|
||||
#else
|
||||
using decorate = detail::decorator_type;
|
||||
#endif
|
||||
|
||||
/** Keep-alive option.
|
||||
|
||||
Determines if the connection is closed after a failed upgrade
|
||||
request.
|
||||
|
||||
This setting only affects the behavior of HTTP requests that
|
||||
implicitly or explicitly ask for a keepalive. For HTTP requests
|
||||
that indicate the connection should be closed, the connection is
|
||||
closed as per rfc7230.
|
||||
|
||||
The default setting is to close connections after a failed
|
||||
upgrade request.
|
||||
|
||||
@note Objects of this type are used with
|
||||
@ref beast::websocket::stream::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the keep alive option.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(keep_alive{8192});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using keep_alive = implementation_defined;
|
||||
#else
|
||||
struct keep_alive
|
||||
{
|
||||
bool value;
|
||||
|
||||
explicit
|
||||
keep_alive(bool v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Message type option.
|
||||
|
||||
This controls the opcode set for outgoing messages. Valid
|
||||
choices are opcode::binary or opcode::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 opcode::text.
|
||||
|
||||
@note Objects of this type are used with
|
||||
@ref beast::websocket::stream::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the message type to binary.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(message_type{opcode::binary});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using message_type = implementation_defined;
|
||||
#else
|
||||
struct message_type
|
||||
{
|
||||
opcode value;
|
||||
|
||||
explicit
|
||||
message_type(opcode op)
|
||||
{
|
||||
if(op != opcode::binary && op != opcode::text)
|
||||
throw beast::detail::make_exception<std::invalid_argument>(
|
||||
"bad opcode", __FILE__, __LINE__);
|
||||
value = op;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
|
||||
using ping_cb = std::function<void(bool, ping_data const&)>;
|
||||
|
||||
} // detail
|
||||
|
||||
/** 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;
|
||||
};
|
||||
|
||||
/** Ping callback option.
|
||||
|
||||
Sets the callback to be invoked whenever a ping or pong is
|
||||
received during a call to one of the following functions:
|
||||
|
||||
@li @ref beast::websocket::stream::read
|
||||
@li @ref beast::websocket::stream::read_frame
|
||||
@li @ref beast::websocket::stream::async_read
|
||||
@li @ref beast::websocket::stream::async_read_frame
|
||||
|
||||
Unlike completion handlers, the callback will be invoked
|
||||
for each received ping and pong during a call to any
|
||||
synchronous or asynchronous read function. The operation is
|
||||
passive, with no associated error code, and triggered by reads.
|
||||
|
||||
The signature of the callback must be:
|
||||
@code
|
||||
void
|
||||
callback(
|
||||
bool is_pong, // `true` if this is a pong
|
||||
ping_data const& payload // Payload of the pong frame
|
||||
);
|
||||
@endcode
|
||||
|
||||
The value of `is_pong` will be `true` if a pong control frame
|
||||
is received, and `false` if a ping control frame is received.
|
||||
|
||||
If the read operation receiving a ping or pong frame is an
|
||||
asynchronous operation, the callback will be invoked using
|
||||
the same method as that used to invoke the final handler.
|
||||
|
||||
@note Objects of this type are used with
|
||||
@ref beast::websocket::stream::set_option.
|
||||
To remove the ping callback, construct the option with
|
||||
no parameters: `set_option(ping_callback{})`
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using ping_callback = implementation_defined;
|
||||
#else
|
||||
struct ping_callback
|
||||
{
|
||||
detail::ping_cb value;
|
||||
|
||||
ping_callback() = default;
|
||||
ping_callback(ping_callback&&) = default;
|
||||
ping_callback(ping_callback const&) = default;
|
||||
|
||||
explicit
|
||||
ping_callback(detail::ping_cb f)
|
||||
: value(std::move(f))
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Read buffer size option.
|
||||
|
||||
Sets the size of the read buffer used by the implementation to
|
||||
receive frames. The read buffer is needed when permessage-deflate
|
||||
is used.
|
||||
|
||||
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 read data.
|
||||
|
||||
The default setting is 4096. The minimum value is 8.
|
||||
|
||||
@note Objects of this type are used with
|
||||
@ref beast::websocket::stream::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the read buffer size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(read_buffer_size{16 * 1024});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using read_buffer_size = implementation_defined;
|
||||
#else
|
||||
struct read_buffer_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
read_buffer_size(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
if(n < 8)
|
||||
throw beast::detail::make_exception<std::invalid_argument>(
|
||||
"read buffer size is too small", __FILE__, __LINE__);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** 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`.
|
||||
|
||||
@note Objects of this type are used with
|
||||
@ref beast::websocket::stream::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the maximum read message size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(read_message_max{65536});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using read_message_max = implementation_defined;
|
||||
#else
|
||||
struct read_message_max
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
read_message_max(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** 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.
|
||||
|
||||
@note Objects of this type are used with
|
||||
@ref beast::websocket::stream::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the write buffer size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(write_buffer_size{8192});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using write_buffer_size = implementation_defined;
|
||||
#else
|
||||
struct write_buffer_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
write_buffer_size(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
if(n < 8)
|
||||
throw beast::detail::make_exception<std::invalid_argument>(
|
||||
"write buffer size is too small", __FILE__, __LINE__);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user