forked from boostorg/beast
Boost prep
This commit is contained in:
1 parent
3777cb8b9c
commit
7139dd639a
375 files changed
+5253
-3884
No files matched your search
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HPP
|
||||
#define BOOST_BEAST_HPP
|
||||
|
||||
#include <boost/beast/config.hpp> // must come first
|
||||
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include <boost/beast/zlib.hpp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CONFIG_HPP
|
||||
#define BOOST_BEAST_CONFIG_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
// Available to every header
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
/*
|
||||
_MSC_VER and _MSC_FULL_VER by version:
|
||||
|
||||
14.0 (2015) 1900 190023026
|
||||
14.0 (2015 Update 1) 1900 190023506
|
||||
14.0 (2015 Update 2) 1900 190023918
|
||||
14.0 (2015 Update 3) 1900 190024210
|
||||
*/
|
||||
|
||||
#ifdef BOOST_MSVC
|
||||
#if BOOST_MSVC_FULL_VER < 190024210
|
||||
static_assert(false,
|
||||
"This library requires Visual Studio 2015 Update 3 or later");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_HPP
|
||||
#define BOOST_BEAST_CORE_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
|
||||
#include <boost/beast/core/async_result.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/buffer_cat.hpp>
|
||||
#include <boost/beast/core/buffer_prefix.hpp>
|
||||
#include <boost/beast/core/buffered_read_stream.hpp>
|
||||
#include <boost/beast/core/buffers_adapter.hpp>
|
||||
#include <boost/beast/core/consuming_buffers.hpp>
|
||||
#include <boost/beast/core/drain_buffer.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/file.hpp>
|
||||
#include <boost/beast/core/file_base.hpp>
|
||||
#include <boost/beast/core/file_posix.hpp>
|
||||
#include <boost/beast/core/file_stdio.hpp>
|
||||
#include <boost/beast/core/file_win32.hpp>
|
||||
#include <boost/beast/core/flat_buffer.hpp>
|
||||
#include <boost/beast/core/handler_alloc.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/core/ostream.hpp>
|
||||
#include <boost/beast/core/read_size.hpp>
|
||||
#include <boost/beast/core/span.hpp>
|
||||
#include <boost/beast/core/flat_static_buffer.hpp>
|
||||
#include <boost/beast/core/static_string.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/string_param.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,209 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_ASYNC_COMPLETION_HPP
|
||||
#define BOOST_BEAST_ASYNC_COMPLETION_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** An interface for customising the behaviour of an asynchronous initiation function.
|
||||
|
||||
This class is used for determining:
|
||||
|
||||
@li The concrete completion handler type to be called at the end of the
|
||||
asynchronous operation;
|
||||
|
||||
@li the initiating function return type; and
|
||||
|
||||
@li how the return value of the initiating function is obtained.
|
||||
|
||||
The trait allows the handler and return types to be determined at the point
|
||||
where the specific completion handler signature is known.
|
||||
|
||||
This template takes advantage of specializations of both
|
||||
`boost::asio::async_result` and `boost::asio::handler_type` for user-defined
|
||||
completion token types. The primary template assumes that the
|
||||
@b CompletionToken is the completion handler.
|
||||
|
||||
@par Example
|
||||
|
||||
The example shows how to define an asynchronous initiation function
|
||||
whose completion handler receives an error code:
|
||||
|
||||
@code
|
||||
template<
|
||||
class AsyncStream, // A stream supporting asynchronous read and write
|
||||
class Handler // The handler to call with signature void(error_code)
|
||||
>
|
||||
async_return_type< // This provides the return type customization
|
||||
Handler, void(error_code)>
|
||||
do_async(
|
||||
AsyncStream& stream, // The stream to work on
|
||||
Handler&& handler) // Could be an rvalue or const reference
|
||||
{
|
||||
// Make sure we have an async stream
|
||||
static_assert(is_async_stream<AsyncWriteStream>::value,
|
||||
"AsyncStream requirements not met");
|
||||
|
||||
// This helper converts the handler into the real handler type
|
||||
async_completion<WriteHandler, void(error_code)> init{handler};
|
||||
|
||||
... // Create and invoke the composed operation
|
||||
|
||||
// This provides the return value and executor customization
|
||||
return init.result.get();
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see @ref async_completion, @ref async_return_type, @ref handler_type
|
||||
*/
|
||||
template<class CompletionToken, class Signature>
|
||||
class async_result
|
||||
{
|
||||
BOOST_STATIC_ASSERT(
|
||||
! std::is_reference<CompletionToken>::value);
|
||||
|
||||
boost::asio::async_result<typename
|
||||
boost::asio::handler_type<CompletionToken,
|
||||
Signature>::type> impl_;
|
||||
|
||||
async_result(async_result const&) = delete;
|
||||
async_result& operator=(async_result const&) = delete;
|
||||
|
||||
public:
|
||||
/// The concrete completion handler type for the specific signature.
|
||||
using completion_handler_type =
|
||||
typename boost::asio::handler_type<
|
||||
CompletionToken, Signature>::type;
|
||||
|
||||
/// The return type of the initiating function.
|
||||
using return_type =
|
||||
typename boost::asio::async_result<
|
||||
completion_handler_type>::type;
|
||||
|
||||
/** Construct an async result from a given handler.
|
||||
|
||||
When using a specalised async_result, the constructor has
|
||||
an opportunity to initialise some state associated with the
|
||||
completion handler, which is then returned from the initiating
|
||||
function.
|
||||
*/
|
||||
explicit
|
||||
async_result(completion_handler_type& h)
|
||||
: impl_(h)
|
||||
{
|
||||
}
|
||||
|
||||
/// Obtain the value to be returned from the initiating function.
|
||||
return_type
|
||||
get()
|
||||
{
|
||||
return impl_.get();
|
||||
}
|
||||
};
|
||||
|
||||
/** 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.
|
||||
|
||||
Example:
|
||||
@code
|
||||
...
|
||||
template<class CompletionToken>
|
||||
typename async_completion<CompletionToken, void(error_code)>::result_type
|
||||
async_initfn(..., CompletionToken&& handler)
|
||||
{
|
||||
async_completion<CompletionToken, void(error_code)> completion{handler};
|
||||
...
|
||||
return completion.result.get();
|
||||
}
|
||||
@endcode
|
||||
|
||||
@tparam CompletionToken Specifies the model used to obtain the result of
|
||||
the asynchronous operation.
|
||||
|
||||
@tparam Signature The call signature for the completion handler type invoked
|
||||
on completion of the asynchronous operation.
|
||||
|
||||
@note See <a href="http://cplusplus.github.io/networking-ts/draft.pdf">
|
||||
Working Draft, C++ Extensions for Networking</a>
|
||||
|
||||
@see @ref async_return_type, @ref handler_type
|
||||
*/
|
||||
template<class CompletionToken, 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 completion_handler_type = typename async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
Signature>::completion_handler_type;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The constructor creates the concrete completion handler and
|
||||
makes the link between the handler and the asynchronous
|
||||
result.
|
||||
|
||||
@param token The completion token. If this is a regular completion
|
||||
handler, copies may be made as needed. If the handler is movable,
|
||||
it may also be moved.
|
||||
*/
|
||||
explicit
|
||||
async_completion(CompletionToken& token)
|
||||
: completion_handler(static_cast<typename std::conditional<
|
||||
std::is_same<CompletionToken, completion_handler_type>::value,
|
||||
completion_handler_type&, CompletionToken&&>::type>(token))
|
||||
, result(completion_handler)
|
||||
{
|
||||
// CompletionToken is not invokable with the given signature
|
||||
static_assert(is_completion_handler<
|
||||
completion_handler_type, Signature>::value,
|
||||
"CompletionToken requirements not met: signature mismatch");
|
||||
}
|
||||
|
||||
/// The final completion handler, callable with the specified signature.
|
||||
typename std::conditional<std::is_same<
|
||||
CompletionToken, completion_handler_type>::value,
|
||||
completion_handler_type&,
|
||||
completion_handler_type
|
||||
>::type completion_handler;
|
||||
|
||||
/// The return value of the asynchronous initiation function.
|
||||
async_result<typename std::decay<
|
||||
CompletionToken>::type, Signature> result;
|
||||
};
|
||||
|
||||
template<class CompletionToken, typename Signature>
|
||||
using handler_type = typename beast::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
Signature>::completion_handler_type;
|
||||
|
||||
template<class CompletionToken, typename Signature>
|
||||
using async_return_type = typename beast::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
Signature>::return_type;
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_BIND_HANDLER_HPP
|
||||
#define BOOST_BEAST_BIND_HANDLER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/bind_handler.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** Bind parameters to a completion handler, creating a new handler.
|
||||
|
||||
This function creates a new handler which, when invoked, calls
|
||||
the original handler with the list of bound arguments. Any
|
||||
parameters passed in the invocation will be subtituted for
|
||||
placeholders present in the list of bound arguments. Parameters
|
||||
which are not matched to placeholders are silently discarded.
|
||||
The passed handler and arguments are forwarded into the returned
|
||||
handler, which provides the same `io_service` execution guarantees
|
||||
as the original handler.
|
||||
|
||||
Unlike `boost::asio::io_service::wrap`, the returned handler can
|
||||
be used in a subsequent call to `boost::asio::io_service::post`
|
||||
instead of `boost::asio::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
|
||||
signal_aborted(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 BOOST_BEAST_DOXYGEN
|
||||
implementation_defined
|
||||
#else
|
||||
detail::bound_handler<
|
||||
typename std::decay<Handler>::type, Args...>
|
||||
#endif
|
||||
bind_handler(Handler&& handler, Args&&... args)
|
||||
{
|
||||
#if 0
|
||||
static_assert(is_completion_handler<
|
||||
Handler, void(Args...)>::value,
|
||||
"Handler requirements not met");
|
||||
#endif
|
||||
return detail::bound_handler<typename std::decay<
|
||||
Handler>::type, Args...>(std::forward<
|
||||
Handler>(handler), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_BUFFER_CAT_HPP
|
||||
#define BOOST_BEAST_BUFFER_CAT_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <tuple>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A buffer sequence representing a concatenation of buffer sequences.
|
||||
|
||||
@see @ref buffer_cat
|
||||
*/
|
||||
template<class... Buffers>
|
||||
class buffer_cat_view
|
||||
{
|
||||
std::tuple<Buffers...> bn_;
|
||||
|
||||
public:
|
||||
/** The type of buffer returned when dereferencing an iterator.
|
||||
|
||||
If every buffer sequence in the view is a @b MutableBufferSequence,
|
||||
then `value_type` will be `boost::asio::mutable_buffer`.
|
||||
Otherwise, `value_type` will be `boost::asio::const_buffer`.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = implementation_defined;
|
||||
#else
|
||||
using value_type = typename
|
||||
detail::common_buffers_type<Buffers...>::type;
|
||||
#endif
|
||||
|
||||
/// The type of iterator used by the concatenated sequence
|
||||
class const_iterator;
|
||||
|
||||
/// Constructor
|
||||
buffer_cat_view(buffer_cat_view&&) = default;
|
||||
|
||||
/// Assignment
|
||||
buffer_cat_view& operator=(buffer_cat_view&&) = default;
|
||||
|
||||
/// Assignment
|
||||
buffer_cat_view& operator=(buffer_cat_view const&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param buffers The list of buffer sequences to concatenate.
|
||||
Copies of the arguments will be made; however, the ownership
|
||||
of memory is not transferred.
|
||||
*/
|
||||
explicit
|
||||
buffer_cat_view(Buffers const&... buffers);
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for @b BufferSequence
|
||||
buffer_cat_view(buffer_cat_view const&) = default;
|
||||
|
||||
/// Required for @b BufferSequence
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Required for @b BufferSequence
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
/** Concatenate 2 or more buffer sequences.
|
||||
|
||||
This function returns a constant or mutable buffer sequence which,
|
||||
when iterated, efficiently concatenates the input buffer sequences.
|
||||
Copies of the arguments passed will be made; however, the returned
|
||||
object does not take ownership of the underlying memory. The
|
||||
application is still responsible for managing the lifetime of the
|
||||
referenced memory.
|
||||
|
||||
@param buffers The list of buffer sequences to concatenate.
|
||||
|
||||
@return A new buffer sequence that represents the concatenation of
|
||||
the input buffer sequences. This buffer sequence will be a
|
||||
@b MutableBufferSequence if each of the passed buffer sequences is
|
||||
also a @b MutableBufferSequence; otherwise the returned buffer
|
||||
sequence will be a @b ConstBufferSequence.
|
||||
|
||||
@see @ref buffer_cat_view
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class... BufferSequence>
|
||||
buffer_cat_view<BufferSequence...>
|
||||
buffer_cat(BufferSequence const&... buffers)
|
||||
#else
|
||||
template<class B1, class B2, class... Bn>
|
||||
inline
|
||||
buffer_cat_view<B1, B2, Bn...>
|
||||
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
|
||||
#endif
|
||||
{
|
||||
static_assert(
|
||||
detail::is_all_const_buffer_sequence<B1, B2, Bn...>::value,
|
||||
"BufferSequence requirements not met");
|
||||
return buffer_cat_view<B1, B2, Bn...>{b1, b2, bn...};
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/buffer_cat.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,240 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_BUFFER_PREFIX_HPP
|
||||
#define BOOST_BEAST_BUFFER_PREFIX_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/in_place_init.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A buffer sequence adapter that shortens the sequence size.
|
||||
|
||||
The class adapts a buffer sequence to efficiently represent
|
||||
a shorter subset of the original list of buffers starting
|
||||
with the first byte of the original sequence.
|
||||
|
||||
@tparam BufferSequence The buffer sequence to adapt.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
class buffer_prefix_view
|
||||
{
|
||||
using buffers_type = typename
|
||||
std::decay<BufferSequence>::type;
|
||||
|
||||
using iter_type =
|
||||
typename buffers_type::const_iterator;
|
||||
|
||||
BufferSequence bs_;
|
||||
std::size_t size_;
|
||||
iter_type end_;
|
||||
|
||||
template<class Deduced>
|
||||
buffer_prefix_view(
|
||||
Deduced&& other, std::size_t dist)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, size_(other.size_)
|
||||
, end_(std::next(bs_.begin(), dist))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
setup(std::size_t size);
|
||||
|
||||
public:
|
||||
/// The type for each element in the list of buffers.
|
||||
using value_type = typename std::conditional<
|
||||
std::is_convertible<typename
|
||||
std::iterator_traits<iter_type>::value_type,
|
||||
boost::asio::mutable_buffer>::value,
|
||||
boost::asio::mutable_buffer,
|
||||
boost::asio::const_buffer>::type;
|
||||
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_iterator;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
buffer_prefix_view(buffer_prefix_view&&);
|
||||
|
||||
/// Copy constructor.
|
||||
buffer_prefix_view(buffer_prefix_view const&);
|
||||
|
||||
/// Move assignment.
|
||||
buffer_prefix_view& operator=(buffer_prefix_view&&);
|
||||
|
||||
/// Copy assignment.
|
||||
buffer_prefix_view& operator=(buffer_prefix_view const&);
|
||||
|
||||
/** Construct a buffer sequence prefix.
|
||||
|
||||
@param size The maximum number of bytes in the prefix.
|
||||
If this is larger than the size of passed, buffers,
|
||||
the resulting sequence will represent the entire
|
||||
input sequence.
|
||||
|
||||
@param buffers The buffer sequence to adapt. A copy of
|
||||
the sequence will be made, but ownership of the underlying
|
||||
memory is not transferred.
|
||||
*/
|
||||
buffer_prefix_view(
|
||||
std::size_t size,
|
||||
BufferSequence const& buffers);
|
||||
|
||||
/** Construct a buffer sequence prefix in-place.
|
||||
|
||||
@param size The maximum number of bytes in the prefix.
|
||||
If this is larger than the size of passed, buffers,
|
||||
the resulting sequence will represent the entire
|
||||
input sequence.
|
||||
|
||||
@param args Arguments forwarded to the contained buffers constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
buffer_prefix_view(
|
||||
std::size_t size,
|
||||
boost::in_place_init_t,
|
||||
Args&&... args);
|
||||
|
||||
/// Get a bidirectional iterator to the first element.
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Get a bidirectional iterator to one past the last element.
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
/** Returns a prefix of a constant buffer.
|
||||
|
||||
The returned buffer points to the same memory as the
|
||||
passed buffer, but with a size that is equal to or less
|
||||
than the size of the original buffer.
|
||||
|
||||
@param size The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. The underlying
|
||||
memory is not modified.
|
||||
|
||||
@return A new buffer that points to the first `size`
|
||||
bytes of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::const_buffer
|
||||
buffer_prefix(std::size_t size,
|
||||
boost::asio::const_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void const*>(buffer),
|
||||
(std::min)(size, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
/** Returns a prefix of a mutable buffer.
|
||||
|
||||
The returned buffer points to the same memory as the
|
||||
passed buffer, but with a size that is equal to or less
|
||||
than the size of the original buffer.
|
||||
|
||||
@param size The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. The underlying
|
||||
memory is not modified.
|
||||
|
||||
@return A new buffer that points to the first `size` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::mutable_buffer
|
||||
buffer_prefix(std::size_t size,
|
||||
boost::asio::mutable_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return {buffer_cast<void*>(buffer),
|
||||
(std::min)(size, buffer_size(buffer))};
|
||||
}
|
||||
|
||||
/** Returns a prefix of a buffer sequence.
|
||||
|
||||
This function returns a new buffer sequence which when iterated,
|
||||
presents a shorter subset of the original list of buffers starting
|
||||
with the first byte of the original sequence.
|
||||
|
||||
@param size The maximum number of bytes in the wrapped
|
||||
sequence. If this is larger than the size of passed,
|
||||
buffers, the resulting sequence will represent the
|
||||
entire input sequence.
|
||||
|
||||
@param buffers An instance of @b ConstBufferSequence or
|
||||
@b MutableBufferSequence to adapt. A copy of the sequence
|
||||
will be made, but ownership of the underlying memory is
|
||||
not transferred.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
buffer_prefix_view<BufferSequence>
|
||||
#else
|
||||
inline
|
||||
typename std::enable_if<
|
||||
! std::is_same<BufferSequence,
|
||||
boost::asio::const_buffer>::value &&
|
||||
! std::is_same<BufferSequence,
|
||||
boost::asio::mutable_buffer>::value,
|
||||
buffer_prefix_view<BufferSequence>>::type
|
||||
#endif
|
||||
buffer_prefix(std::size_t size, BufferSequence const& buffers)
|
||||
{
|
||||
static_assert(
|
||||
is_const_buffer_sequence<BufferSequence>::value ||
|
||||
is_mutable_buffer_sequence<BufferSequence>::value,
|
||||
"BufferSequence requirements not met");
|
||||
return buffer_prefix_view<BufferSequence>(size, buffers);
|
||||
}
|
||||
|
||||
/** Returns the first buffer in a buffer sequence
|
||||
|
||||
This returns the first buffer in the buffer sequence.
|
||||
If the buffer sequence is an empty range, the returned
|
||||
buffer will have a zero buffer size.
|
||||
|
||||
@param buffers The buffer sequence. If the sequence is
|
||||
mutable, the returned buffer sequence will also be mutable.
|
||||
Otherwise, the returned buffer sequence will be constant.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
typename std::conditional<
|
||||
is_mutable_buffer_sequence<BufferSequence>::value,
|
||||
boost::asio::mutable_buffer,
|
||||
boost::asio::const_buffer>::type
|
||||
buffer_front(BufferSequence const& buffers)
|
||||
{
|
||||
auto const first = buffers.begin();
|
||||
if(first == buffers.end())
|
||||
return {};
|
||||
return *first;
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/buffer_prefix.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,360 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_BUFFERED_READ_STREAM_HPP
|
||||
#define BOOST_BEAST_BUFFERED_READ_STREAM_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/async_result.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A @b Stream with attached @b DynamicBuffer to buffer reads.
|
||||
|
||||
This wraps a @b Stream implementation so that calls to write are
|
||||
passed through to the underlying stream, while calls to read will
|
||||
first consume the input sequence stored in a @b DynamicBuffer which
|
||||
is part of the object.
|
||||
|
||||
The use-case for this class is different than that of the
|
||||
`boost::asio::buffered_readstream`. It is designed to facilitate
|
||||
the use of `boost::asio::read_until`, and to allow buffers
|
||||
acquired during detection of handshakes to be made transparently
|
||||
available to callers. A hypothetical implementation of the
|
||||
buffered version of `boost::asio::ssl::stream::async_handshake`
|
||||
could make use of this wrapper.
|
||||
|
||||
Uses:
|
||||
|
||||
@li Transparently leave untouched input acquired in calls
|
||||
to `boost::asio::read_until` behind for subsequent callers.
|
||||
|
||||
@li "Preload" a stream with handshake input data acquired
|
||||
from other sources.
|
||||
|
||||
Example:
|
||||
@code
|
||||
// Process the next HTTP header on the stream,
|
||||
// leaving excess bytes behind for the next call.
|
||||
//
|
||||
template<class DynamicBuffer>
|
||||
void process_http_message(
|
||||
buffered_read_stream<DynamicBuffer>& stream)
|
||||
{
|
||||
// Read up to and including the end of the HTTP
|
||||
// header, leaving the sequence in the stream's
|
||||
// buffer. read_until may read past the end of the
|
||||
// headers; the return value will include only the
|
||||
// part up to the end of the delimiter.
|
||||
//
|
||||
std::size_t bytes_transferred =
|
||||
boost::asio::read_until(
|
||||
stream.next_layer(), stream.buffer(), "\r\n\r\n");
|
||||
|
||||
// Use buffer_prefix() to limit the input
|
||||
// sequence to only the data up to and including
|
||||
// the trailing "\r\n\r\n".
|
||||
//
|
||||
auto header_buffers = buffer_prefix(
|
||||
bytes_transferred, stream.buffer().data());
|
||||
|
||||
...
|
||||
|
||||
// Discard the portion of the input corresponding
|
||||
// to the HTTP headers.
|
||||
//
|
||||
stream.buffer().consume(bytes_transferred);
|
||||
|
||||
// Everything we read from the stream
|
||||
// is part of the content-body.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@tparam Stream The type of stream to wrap.
|
||||
|
||||
@tparam DynamicBuffer The type of stream buffer to use.
|
||||
*/
|
||||
template<class Stream, class DynamicBuffer>
|
||||
class buffered_read_stream
|
||||
{
|
||||
static_assert(is_dynamic_buffer<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 buffer_type = DynamicBuffer;
|
||||
|
||||
/// The type of the next layer.
|
||||
using next_layer_type =
|
||||
typename std::remove_reference<Stream>::type;
|
||||
|
||||
/// The type of the lowest layer.
|
||||
using lowest_layer_type =
|
||||
typename get_lowest_layer<next_layer_type>::type;
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
@note The behavior of move assignment on or from streams
|
||||
with active or pending operations is undefined.
|
||||
*/
|
||||
buffered_read_stream(buffered_read_stream&&) = default;
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
@note The behavior of move assignment on or from streams
|
||||
with active or pending operations is undefined.
|
||||
*/
|
||||
buffered_read_stream& operator=(buffered_read_stream&&) = default;
|
||||
|
||||
/** Construct the wrapping stream.
|
||||
|
||||
@param args Parameters forwarded to the `Stream` constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
buffered_read_stream(Args&&... args);
|
||||
|
||||
/// Get a reference to the next layer.
|
||||
next_layer_type&
|
||||
next_layer()
|
||||
{
|
||||
return next_layer_;
|
||||
}
|
||||
|
||||
/// Get a const reference to the next layer.
|
||||
next_layer_type const&
|
||||
next_layer() const
|
||||
{
|
||||
return next_layer_;
|
||||
}
|
||||
|
||||
/// Get a reference to the lowest layer.
|
||||
lowest_layer_type&
|
||||
lowest_layer()
|
||||
{
|
||||
return next_layer_.lowest_layer();
|
||||
}
|
||||
|
||||
/// Get a const reference to the lowest layer.
|
||||
lowest_layer_type const&
|
||||
lowest_layer() const
|
||||
{
|
||||
return next_layer_.lowest_layer();
|
||||
}
|
||||
|
||||
/// Get the 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 BOOST_BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
#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_sync_write_stream<next_layer_type>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
return next_layer_.write_some(buffers);
|
||||
}
|
||||
|
||||
/** Write some data to the stream.
|
||||
|
||||
This function is used to write data to the stream.
|
||||
The function call will block until one or more bytes of the
|
||||
data has been written successfully, or until an error occurs.
|
||||
|
||||
@param buffers One or more data buffers to be written to the stream.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes written.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<next_layer_type>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
return next_layer_.write_some(buffers, ec);
|
||||
}
|
||||
|
||||
/** Start an asynchronous write.
|
||||
|
||||
This function is used to asynchronously write data from
|
||||
the stream. The function call always returns immediately.
|
||||
|
||||
@param buffers One or more data buffers to be written to
|
||||
the stream. Although the buffers object may be copied as
|
||||
necessary, ownership of the underlying memory blocks is
|
||||
retained by the caller, which must guarantee that they
|
||||
remain valid until the handler is called.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // number of bytes transferred
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
#endif
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler);
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/buffered_read_stream.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_BUFFERS_ADAPTER_HPP
|
||||
#define BOOST_BEAST_BUFFERS_ADAPTER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** Adapts a @b MutableBufferSequence into a @b DynamicBuffer.
|
||||
|
||||
This class wraps a @b MutableBufferSequence to meet the requirements
|
||||
of @b DynamicBuffer. Upon construction the input and output sequences are
|
||||
empty. A copy of the mutable buffer sequence object is stored; however,
|
||||
ownership of the underlying memory is not transferred. The caller is
|
||||
responsible for making sure that referenced memory remains valid
|
||||
for the duration of any operations.
|
||||
|
||||
The size of the mutable buffer sequence determines the maximum
|
||||
number of bytes which may be prepared and committed.
|
||||
|
||||
@tparam MutableBufferSequence The type of mutable buffer sequence to wrap.
|
||||
*/
|
||||
template<class MutableBufferSequence>
|
||||
class buffers_adapter
|
||||
{
|
||||
static_assert(is_mutable_buffer_sequence<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 BOOST_BEAST_DOXYGEN
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_buffers_type;
|
||||
|
||||
class mutable_buffers_type;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
buffers_adapter(buffers_adapter&& other);
|
||||
|
||||
/// Copy constructor.
|
||||
buffers_adapter(buffers_adapter const& other);
|
||||
|
||||
/// Move assignment.
|
||||
buffers_adapter& operator=(buffers_adapter&& other);
|
||||
|
||||
/// Copy assignment.
|
||||
buffers_adapter& operator=(buffers_adapter const&);
|
||||
|
||||
/** Construct a buffers adapter.
|
||||
|
||||
@param buffers The mutable buffer sequence to wrap. A copy of
|
||||
the object will be made, but ownership of the memory is not
|
||||
transferred.
|
||||
*/
|
||||
explicit
|
||||
buffers_adapter(MutableBufferSequence const& buffers);
|
||||
|
||||
/// Returns the largest size output sequence possible.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return max_size_;
|
||||
}
|
||||
|
||||
/// Get the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return in_size_;
|
||||
}
|
||||
|
||||
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return max_size_;
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represents the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if the size would exceed the limit
|
||||
imposed by the underlying mutable buffer sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t n);
|
||||
|
||||
/** Get a list of buffers that represents the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(std::size_t n);
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/buffers_adapter.ipp>
|
||||
|
||||
#endif
|
||||
@@ -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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CONSUMING_BUFFERS_HPP
|
||||
#define BOOST_BEAST_CONSUMING_BUFFERS_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/detail/in_place_init.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** Adapter to trim the front of a `BufferSequence`.
|
||||
|
||||
This adapter wraps a buffer sequence to create a new sequence
|
||||
which may be incrementally consumed. Bytes consumed are removed
|
||||
from the front of the buffer. The underlying memory is not changed,
|
||||
instead the adapter efficiently iterates through a subset of
|
||||
the buffers wrapped.
|
||||
|
||||
The wrapped buffer is not modified, a copy is made instead.
|
||||
Ownership of the underlying memory is not transferred, the application
|
||||
is still responsible for managing its lifetime.
|
||||
|
||||
@tparam BufferSequence The buffer sequence to wrap.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
class consuming_buffers
|
||||
{
|
||||
using buffers_type =
|
||||
typename std::decay<BufferSequence>::type;
|
||||
|
||||
using iter_type =
|
||||
typename buffers_type::const_iterator;
|
||||
|
||||
BufferSequence bs_;
|
||||
iter_type begin_;
|
||||
std::size_t skip_ = 0;
|
||||
|
||||
template<class Deduced>
|
||||
consuming_buffers(Deduced&& other, std::size_t dist)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, begin_(std::next(bs_.begin(), dist))
|
||||
, skip_(other.skip_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/** The type for each element in the list of buffers.
|
||||
|
||||
If the buffers in the underlying sequence are convertible to
|
||||
`boost::asio::mutable_buffer`, then this type will be
|
||||
`boost::asio::mutable_buffer`, else this type will be
|
||||
`boost::asio::const_buffer`.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = implementation_defined;
|
||||
#else
|
||||
using value_type = typename std::conditional<
|
||||
std::is_convertible<typename
|
||||
std::iterator_traits<iter_type>::value_type,
|
||||
boost::asio::mutable_buffer>::value,
|
||||
boost::asio::mutable_buffer,
|
||||
boost::asio::const_buffer>::type;
|
||||
#endif
|
||||
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_iterator;
|
||||
|
||||
#endif
|
||||
|
||||
/// Constructor
|
||||
consuming_buffers();
|
||||
|
||||
/// Constructor
|
||||
consuming_buffers(consuming_buffers&&);
|
||||
|
||||
/// Constructor
|
||||
consuming_buffers(consuming_buffers const&);
|
||||
|
||||
/** Constructor
|
||||
|
||||
A copy of the buffer sequence is made. Ownership of the
|
||||
underlying memory is not transferred or copied.
|
||||
*/
|
||||
explicit
|
||||
consuming_buffers(BufferSequence const& buffers);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs the buffer sequence in-place from
|
||||
a list of arguments.
|
||||
|
||||
@param args Arguments forwarded to the buffers constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
consuming_buffers(boost::in_place_init_t, Args&&... args);
|
||||
|
||||
/// Assignment
|
||||
consuming_buffers& operator=(consuming_buffers&&);
|
||||
|
||||
/// Assignment
|
||||
consuming_buffers& operator=(consuming_buffers const&);
|
||||
|
||||
/// Returns the underlying buffers, without modification
|
||||
BufferSequence const&
|
||||
get() const
|
||||
{
|
||||
return bs_;
|
||||
}
|
||||
|
||||
/// Get a bidirectional iterator to the first element.
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Get a bidirectional iterator to one past the last element.
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
/** Remove bytes from the beginning of the sequence.
|
||||
|
||||
@param amount The number of bytes to remove. If this is
|
||||
larger than the number of bytes remaining, all the
|
||||
bytes remaining are removed.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t amount);
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/consuming_buffers.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,251 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
/*
|
||||
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
|
||||
Copyright notice:
|
||||
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 René Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
*/
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_BASE64_HPP
|
||||
#define BOOST_BEAST_DETAIL_BASE64_HPP
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
namespace base64 {
|
||||
|
||||
inline
|
||||
char const*
|
||||
get_alphabet()
|
||||
{
|
||||
static char constexpr tab[] = {
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
};
|
||||
return &tab[0];
|
||||
}
|
||||
|
||||
inline
|
||||
signed char const*
|
||||
get_inverse()
|
||||
{
|
||||
static signed char constexpr tab[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255
|
||||
};
|
||||
return &tab[0];
|
||||
}
|
||||
|
||||
|
||||
/// Returns max chars needed to encode a base64 string
|
||||
inline
|
||||
std::size_t constexpr
|
||||
encoded_size(std::size_t n)
|
||||
{
|
||||
return 4 * ((n + 2) / 3);
|
||||
}
|
||||
|
||||
/// Returns max bytes needed to decode a base64 string
|
||||
inline
|
||||
std::size_t constexpr
|
||||
decoded_size(std::size_t n)
|
||||
{
|
||||
return n / 4 * 3; // requires n&3==0, smaller
|
||||
//return 3 * n / 4;
|
||||
}
|
||||
|
||||
/** Encode a series of octets as a padded, base64 string.
|
||||
|
||||
The resulting string will not be null terminated.
|
||||
|
||||
@par Requires
|
||||
|
||||
The memory pointed to by `out` points to valid memory
|
||||
of at least `encoded_size(len)` bytes.
|
||||
|
||||
@return The number of characters written to `out`. This
|
||||
will exclude any null termination.
|
||||
*/
|
||||
template<class = void>
|
||||
std::size_t
|
||||
encode(void* dest, void const* src, std::size_t len)
|
||||
{
|
||||
char* out = static_cast<char*>(dest);
|
||||
char const* in = static_cast<char const*>(src);
|
||||
auto const tab = base64::get_alphabet();
|
||||
|
||||
for(auto n = len / 3; n--;)
|
||||
{
|
||||
*out++ = tab[ (in[0] & 0xfc) >> 2];
|
||||
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
|
||||
*out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
|
||||
*out++ = tab[ in[2] & 0x3f];
|
||||
in += 3;
|
||||
}
|
||||
|
||||
switch(len % 3)
|
||||
{
|
||||
case 2:
|
||||
*out++ = tab[ (in[0] & 0xfc) >> 2];
|
||||
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
|
||||
*out++ = tab[ (in[1] & 0x0f) << 2];
|
||||
*out++ = '=';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*out++ = tab[ (in[0] & 0xfc) >> 2];
|
||||
*out++ = tab[((in[0] & 0x03) << 4)];
|
||||
*out++ = '=';
|
||||
*out++ = '=';
|
||||
break;
|
||||
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
return out - static_cast<char*>(dest);
|
||||
}
|
||||
|
||||
/** Decode a padded base64 string into a series of octets.
|
||||
|
||||
@par Requires
|
||||
|
||||
The memory pointed to by `out` points to valid memory
|
||||
of at least `decoded_size(len)` bytes.
|
||||
|
||||
@return The number of octets written to `out`, and
|
||||
the number of characters read from the input string,
|
||||
expressed as a pair.
|
||||
*/
|
||||
template<class = void>
|
||||
std::pair<std::size_t, std::size_t>
|
||||
decode(void* dest, char const* src, std::size_t len)
|
||||
{
|
||||
char* out = static_cast<char*>(dest);
|
||||
auto in = reinterpret_cast<unsigned char const*>(src);
|
||||
unsigned char c3[3], c4[4];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
auto const inverse = base64::get_inverse();
|
||||
|
||||
while(len-- && *in != '=')
|
||||
{
|
||||
auto const v = inverse[*in];
|
||||
if(v == -1)
|
||||
break;
|
||||
++in;
|
||||
c4[i] = v;
|
||||
if(++i == 4)
|
||||
{
|
||||
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
|
||||
|
||||
for(i = 0; i < 3; i++)
|
||||
*out++ = c3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
|
||||
|
||||
for(j = 0; j < i - 1; j++)
|
||||
*out++ = c3[j];
|
||||
}
|
||||
|
||||
return {out - static_cast<char*>(dest),
|
||||
in - reinterpret_cast<unsigned char const*>(src)};
|
||||
}
|
||||
|
||||
} // base64
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
base64_encode (std::uint8_t const* data,
|
||||
std::size_t len)
|
||||
{
|
||||
std::string dest;
|
||||
dest.resize(base64::encoded_size(len));
|
||||
dest.resize(base64::encode(&dest[0], data, len));
|
||||
return dest;
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
base64_encode(std::string const& s)
|
||||
{
|
||||
return base64_encode (reinterpret_cast <
|
||||
std::uint8_t const*> (s.data()), s.size());
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
base64_decode(std::string const& data)
|
||||
{
|
||||
std::string dest;
|
||||
dest.resize(base64::decoded_size(data.size()));
|
||||
auto const result = base64::decode(
|
||||
&dest[0], data.data(), data.size());
|
||||
dest.resize(result.first);
|
||||
return dest;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_BIND_DETAIL_HANDLER_HPP
|
||||
#define BOOST_BEAST_BIND_DETAIL_HANDLER_HPP
|
||||
|
||||
#include <boost/beast/core/detail/integer_sequence.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/* Nullary handler that calls Handler with bound arguments.
|
||||
|
||||
The bound handler provides the same io_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 Arg, class Vals>
|
||||
static
|
||||
typename std::enable_if<
|
||||
std::is_placeholder<typename
|
||||
std::decay<Arg>::type>::value == 0,
|
||||
Arg&&>::type
|
||||
extract(Arg&& arg, Vals& vals)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
template<class Arg, class Vals>
|
||||
static
|
||||
typename std::enable_if<
|
||||
std::is_placeholder<typename
|
||||
std::decay<Arg>::type>::value != 0,
|
||||
typename std::tuple_element<
|
||||
std::is_placeholder<
|
||||
typename std::decay<Arg>::type>::value - 1,
|
||||
Vals>::type&&>::type
|
||||
extract(Arg&&, Vals&& vals)
|
||||
{
|
||||
return std::get<std::is_placeholder<
|
||||
typename std::decay<Arg>::type>::value - 1>(
|
||||
std::forward<Vals>(vals));
|
||||
}
|
||||
|
||||
template<
|
||||
class ArgsTuple,
|
||||
std::size_t... S>
|
||||
static
|
||||
void
|
||||
invoke(
|
||||
Handler& h,
|
||||
ArgsTuple& args,
|
||||
std::tuple<>&&,
|
||||
index_sequence<S...>)
|
||||
{
|
||||
boost::ignore_unused(args);
|
||||
h(std::get<S>(args)...);
|
||||
}
|
||||
|
||||
template<
|
||||
class ArgsTuple,
|
||||
class ValsTuple,
|
||||
std::size_t... S>
|
||||
static
|
||||
void
|
||||
invoke(
|
||||
Handler& h,
|
||||
ArgsTuple& args,
|
||||
ValsTuple&& vals,
|
||||
index_sequence<S...>)
|
||||
{
|
||||
boost::ignore_unused(args);
|
||||
boost::ignore_unused(vals);
|
||||
h(extract(std::get<S>(args),
|
||||
std::forward<ValsTuple>(vals))...);
|
||||
}
|
||||
|
||||
public:
|
||||
using result_type = void;
|
||||
|
||||
bound_handler(bound_handler&&) = default;
|
||||
bound_handler(bound_handler const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
explicit
|
||||
bound_handler(
|
||||
DeducedHandler&& handler, Args&&... args)
|
||||
: h_(std::forward<DeducedHandler>(handler))
|
||||
, args_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Values>
|
||||
void
|
||||
operator()(Values&&... values)
|
||||
{
|
||||
invoke(h_, args_,
|
||||
std::forward_as_tuple(
|
||||
std::forward<Values>(values)...),
|
||||
index_sequence_for<Args...>());
|
||||
}
|
||||
|
||||
template<class... Values>
|
||||
void
|
||||
operator()(Values&&... values) const
|
||||
{
|
||||
invoke(h_, args_,
|
||||
std::forward_as_tuple(
|
||||
std::forward<Values>(values)...),
|
||||
index_sequence_for<Args...>());
|
||||
}
|
||||
|
||||
friend
|
||||
void*
|
||||
asio_handler_allocate(
|
||||
std::size_t size, bound_handler* h)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(h->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size, bound_handler* h)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(h->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
asio_handler_is_continuation(bound_handler* h)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(std::addressof(h->h_));
|
||||
}
|
||||
|
||||
template<class F>
|
||||
friend
|
||||
void
|
||||
asio_handler_invoke(F&& f, bound_handler* h)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(h->h_));
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
namespace std {
|
||||
template<class Handler, class... Args>
|
||||
void
|
||||
bind(boost::beast::detail::bound_handler<
|
||||
Handler, Args...>, ...) = delete;
|
||||
} // std
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_BUFFERS_REF_HPP
|
||||
#define BOOST_BEAST_DETAIL_BUFFERS_REF_HPP
|
||||
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// A very lightweight reference to a buffer sequence
|
||||
template<class BufferSequence>
|
||||
class buffers_ref
|
||||
{
|
||||
BufferSequence const* buffers_;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename BufferSequence::value_type;
|
||||
|
||||
using const_iterator =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
buffers_ref(buffers_ref const&) = default;
|
||||
buffers_ref& operator=(buffers_ref const&) = default;
|
||||
|
||||
explicit
|
||||
buffers_ref(BufferSequence const& buffers)
|
||||
: buffers_(std::addressof(buffers))
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return buffers_->begin();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return buffers_->end();
|
||||
}
|
||||
};
|
||||
|
||||
// Return a reference to a buffer sequence
|
||||
template<class BufferSequence>
|
||||
buffers_ref<BufferSequence>
|
||||
make_buffers_ref(BufferSequence const& buffers)
|
||||
{
|
||||
return buffers_ref<BufferSequence>(buffers);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_DETAIL_CLAMP_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_CLAMP_HPP
|
||||
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class UInt>
|
||||
static
|
||||
std::size_t
|
||||
clamp(UInt x)
|
||||
{
|
||||
if(x >= (std::numeric_limits<std::size_t>::max)())
|
||||
return (std::numeric_limits<std::size_t>::max)();
|
||||
return static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
template<class UInt>
|
||||
static
|
||||
std::size_t
|
||||
clamp(UInt x, std::size_t limit)
|
||||
{
|
||||
if(x >= limit)
|
||||
return limit;
|
||||
return static_cast<std::size_t>(x);
|
||||
}
|
||||
|
||||
// return `true` if x + y > z, which are unsigned
|
||||
template<
|
||||
class U1, class U2, class U3>
|
||||
constexpr
|
||||
bool
|
||||
sum_exceeds(U1 x, U2 y, U3 z)
|
||||
{
|
||||
static_assert(
|
||||
std::is_unsigned<U1>::value &&
|
||||
std::is_unsigned<U2>::value &&
|
||||
std::is_unsigned<U3>::value, "");
|
||||
return y > z || x > z - y;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_DETAIL_CONFIG_HPP
|
||||
#define BOOST_BEAST_CORE_DETAIL_CONFIG_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#if BOOST_VERSION >= 106500 || ! defined(BOOST_GCC) || BOOST_GCC < 70000
|
||||
# define BOOST_BEAST_FALLTHROUGH BOOST_FALLTHROUGH
|
||||
#else
|
||||
# define BOOST_BEAST_FALLTHROUGH __attribute__((fallthrough))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Copyright (c) 2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_CPU_INFO_HPP
|
||||
#define BOOST_BEAST_DETAIL_CPU_INFO_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#ifndef BOOST_BEAST_NO_INTRINSICS
|
||||
# if defined(BOOST_MSVC) || ((defined(BOOST_GCC) || defined(BOOST_CLANG)) && defined(__SSE4_2__))
|
||||
# define BOOST_BEAST_NO_INTRINSICS 0
|
||||
# else
|
||||
# define BOOST_BEAST_NO_INTRINSICS 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ! BOOST_BEAST_NO_INTRINSICS
|
||||
|
||||
#ifdef BOOST_MSVC
|
||||
#include <intrin.h> // __cpuid
|
||||
#else
|
||||
#include <cpuid.h> // __get_cpuid
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/* Portions from Boost,
|
||||
Copyright Andrey Semashev 2007 - 2015.
|
||||
*/
|
||||
template<class = void>
|
||||
void
|
||||
cpuid(
|
||||
std::uint32_t id,
|
||||
std::uint32_t& eax,
|
||||
std::uint32_t& ebx,
|
||||
std::uint32_t& ecx,
|
||||
std::uint32_t& edx)
|
||||
{
|
||||
#ifdef BOOST_MSVC
|
||||
int regs[4];
|
||||
__cpuid(regs, id);
|
||||
eax = regs[0];
|
||||
ebx = regs[1];
|
||||
ecx = regs[2];
|
||||
edx = regs[3];
|
||||
#else
|
||||
__get_cpuid(id, &eax, &ebx, &ecx, &edx);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct cpu_info
|
||||
{
|
||||
bool sse42 = false;
|
||||
|
||||
cpu_info();
|
||||
};
|
||||
|
||||
inline
|
||||
cpu_info::
|
||||
cpu_info()
|
||||
{
|
||||
constexpr std::uint32_t SSE42 = 1 << 20;
|
||||
|
||||
std::uint32_t eax = 0;
|
||||
std::uint32_t ebx = 0;
|
||||
std::uint32_t ecx = 0;
|
||||
std::uint32_t edx = 0;
|
||||
|
||||
cpuid(0, eax, ebx, ecx, edx);
|
||||
if(eax >= 1)
|
||||
{
|
||||
cpuid(1, eax, ebx, ecx, edx);
|
||||
sse42 = (ecx & SSE42) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
cpu_info const&
|
||||
get_cpu_info()
|
||||
{
|
||||
static cpu_info const ci;
|
||||
return ci;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
|
||||
#define BOOST_BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
|
||||
|
||||
#include <boost/type_traits/is_final.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
struct empty_base_optimization_decide
|
||||
: std::integral_constant<bool,
|
||||
std::is_empty<T>::value &&
|
||||
! boost::is_final<T>::value>
|
||||
{
|
||||
};
|
||||
|
||||
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
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_IN_PLACE_INIT_HPP
|
||||
#define BOOST_BEAST_DETAIL_IN_PLACE_INIT_HPP
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
// Provide boost::in_place_init_t and boost::in_place_init
|
||||
// for Boost versions earlier than 1.63.0.
|
||||
|
||||
#if BOOST_VERSION < 106300
|
||||
|
||||
namespace boost {
|
||||
|
||||
namespace optional_ns {
|
||||
|
||||
// a tag for in-place initialization of contained value
|
||||
struct in_place_init_t
|
||||
{
|
||||
struct init_tag{};
|
||||
explicit in_place_init_t(init_tag){}
|
||||
};
|
||||
const in_place_init_t in_place_init ((in_place_init_t::init_tag()));
|
||||
|
||||
} // namespace optional_ns
|
||||
|
||||
using optional_ns::in_place_init_t;
|
||||
using optional_ns::in_place_init;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_INTEGER_SEQUENCE_HPP
|
||||
#define BOOST_BEAST_DETAIL_INTEGER_SEQUENCE_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class T, T... Ints>
|
||||
struct integer_sequence
|
||||
{
|
||||
using value_type = T;
|
||||
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
|
||||
|
||||
static std::size_t constexpr static_size = sizeof...(Ints);
|
||||
|
||||
static std::size_t constexpr size()
|
||||
{
|
||||
return sizeof...(Ints);
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t... Ints>
|
||||
using index_sequence = integer_sequence<std::size_t, Ints...>;
|
||||
|
||||
// This workaround is needed for broken sizeof...
|
||||
template<class... Args>
|
||||
struct sizeof_workaround
|
||||
{
|
||||
static std::size_t constexpr size = sizeof... (Args);
|
||||
};
|
||||
|
||||
#ifdef BOOST_MSVC
|
||||
|
||||
// This implementation compiles on real MSVC and clang but not gcc
|
||||
|
||||
template<class T, unsigned long long N, class Seq>
|
||||
struct make_integer_sequence_unchecked;
|
||||
|
||||
template<class T, unsigned long long N, unsigned long long ...Indices>
|
||||
struct make_integer_sequence_unchecked<
|
||||
T, N, integer_sequence<T, Indices...>>
|
||||
{
|
||||
using type = typename make_integer_sequence_unchecked<
|
||||
T, N-1, integer_sequence<T, N-1, Indices...>>::type;
|
||||
};
|
||||
|
||||
template<class T, unsigned long long ...Indices>
|
||||
struct make_integer_sequence_unchecked<
|
||||
T, 0, integer_sequence<T, Indices...>>
|
||||
{
|
||||
using type = integer_sequence<T, Indices...>;
|
||||
};
|
||||
|
||||
template<class T, T N>
|
||||
struct make_integer_sequence_checked
|
||||
{
|
||||
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
|
||||
BOOST_STATIC_ASSERT(N >= 0);
|
||||
|
||||
using type = typename make_integer_sequence_unchecked<
|
||||
T, N, integer_sequence<T>>::type;
|
||||
};
|
||||
|
||||
template<class T, T N>
|
||||
using make_integer_sequence =
|
||||
typename make_integer_sequence_checked<T, N>::type;
|
||||
|
||||
template<std::size_t N>
|
||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||
|
||||
template<class... Args>
|
||||
using index_sequence_for =
|
||||
make_index_sequence<sizeof_workaround<Args...>::size>;
|
||||
|
||||
#else
|
||||
|
||||
// This implementation compiles on gcc but not MSVC
|
||||
|
||||
template<std::size_t... Ints>
|
||||
struct index_tuple
|
||||
{
|
||||
using next = index_tuple<Ints..., sizeof... (Ints)>;
|
||||
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
struct build_index_tuple
|
||||
{
|
||||
using type = typename build_index_tuple<N-1>::type::next;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct build_index_tuple<0>
|
||||
{
|
||||
using type = index_tuple<>;
|
||||
};
|
||||
|
||||
template<class T, T N,
|
||||
class Seq = typename build_index_tuple<N>::type
|
||||
>
|
||||
struct integer_sequence_helper;
|
||||
|
||||
template<class T, T N, std::size_t... Ints>
|
||||
struct integer_sequence_helper<T, N, index_tuple<Ints...>>
|
||||
{
|
||||
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
|
||||
BOOST_STATIC_ASSERT(N >= 0);
|
||||
|
||||
using type = integer_sequence<T, static_cast<T> (Ints)...>;
|
||||
};
|
||||
|
||||
template<class T, T N>
|
||||
using make_integer_sequence =
|
||||
typename integer_sequence_helper<T, N>::type;
|
||||
|
||||
template<std::size_t N>
|
||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||
|
||||
template<class... Args>
|
||||
using index_sequence_for =
|
||||
make_index_sequence<sizeof_workaround<Args...>::size>;
|
||||
|
||||
#endif
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,322 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_OSTREAM_HPP
|
||||
#define BOOST_BEAST_DETAIL_OSTREAM_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/beast/core/read_size.hpp>
|
||||
#include <memory>
|
||||
#include <iosfwd>
|
||||
#include <streambuf>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class Buffers>
|
||||
class buffers_helper
|
||||
{
|
||||
Buffers b_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
buffers_helper(Buffers const& b)
|
||||
: b_(b)
|
||||
{
|
||||
}
|
||||
|
||||
template<class B>
|
||||
friend
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
buffers_helper<B> const& v);
|
||||
};
|
||||
|
||||
template<class Buffers>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
buffers_helper<Buffers> const& v)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for(boost::asio::const_buffer b : v.b_)
|
||||
os.write(buffer_cast<char const*>(b),
|
||||
buffer_size(b));
|
||||
return os;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct basic_streambuf_movable_helper :
|
||||
std::basic_streambuf<char, std::char_traits<char>>
|
||||
{
|
||||
basic_streambuf_movable_helper(
|
||||
basic_streambuf_movable_helper&&) = default;
|
||||
};
|
||||
|
||||
using basic_streambuf_movable =
|
||||
std::is_move_constructible<basic_streambuf_movable_helper>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class DynamicBuffer,
|
||||
class CharT, class Traits, bool isMovable>
|
||||
class ostream_buffer;
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_buffer
|
||||
<DynamicBuffer, CharT, Traits, true>
|
||||
: public std::basic_streambuf<CharT, Traits>
|
||||
{
|
||||
using int_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::int_type;
|
||||
|
||||
using traits_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::traits_type;
|
||||
|
||||
static std::size_t constexpr max_size = 512;
|
||||
|
||||
DynamicBuffer& buf_;
|
||||
|
||||
public:
|
||||
ostream_buffer(ostream_buffer&&) = default;
|
||||
ostream_buffer(ostream_buffer const&) = delete;
|
||||
|
||||
~ostream_buffer() noexcept
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
explicit
|
||||
ostream_buffer(DynamicBuffer& buf)
|
||||
: buf_(buf)
|
||||
{
|
||||
prepare();
|
||||
}
|
||||
|
||||
int_type
|
||||
overflow(int_type ch) override
|
||||
{
|
||||
if(! Traits::eq_int_type(ch, Traits::eof()))
|
||||
{
|
||||
Traits::assign(*this->pptr(),
|
||||
static_cast<CharT>(ch));
|
||||
flush(1);
|
||||
prepare();
|
||||
return ch;
|
||||
}
|
||||
flush();
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
int
|
||||
sync() override
|
||||
{
|
||||
flush();
|
||||
prepare();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
prepare()
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto mbs = buf_.prepare(
|
||||
read_size_or_throw(buf_, max_size));
|
||||
auto const mb = *mbs.begin();
|
||||
auto const p = buffer_cast<CharT*>(mb);
|
||||
this->setp(p,
|
||||
p + buffer_size(mb) / sizeof(CharT) - 1);
|
||||
}
|
||||
|
||||
void
|
||||
flush(int extra = 0)
|
||||
{
|
||||
buf_.commit(
|
||||
(this->pptr() - this->pbase() + extra) *
|
||||
sizeof(CharT));
|
||||
}
|
||||
};
|
||||
|
||||
// This nonsense is all to work around a glitch in libstdc++
|
||||
// where std::basic_streambuf copy constructor is private:
|
||||
// https://github.com/gcc-mirror/gcc/blob/gcc-4_8-branch/libstdc%2B%2B-v3/include/std/streambuf#L799
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_buffer
|
||||
<DynamicBuffer, CharT, Traits, false>
|
||||
: public std::basic_streambuf<CharT, Traits>
|
||||
{
|
||||
using int_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::int_type;
|
||||
|
||||
using traits_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::traits_type;
|
||||
|
||||
static std::size_t constexpr max_size = 512;
|
||||
|
||||
DynamicBuffer& buf_;
|
||||
|
||||
public:
|
||||
ostream_buffer(ostream_buffer&&) = delete;
|
||||
ostream_buffer(ostream_buffer const&) = delete;
|
||||
|
||||
~ostream_buffer() noexcept
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
explicit
|
||||
ostream_buffer(DynamicBuffer& buf)
|
||||
: buf_(buf)
|
||||
{
|
||||
prepare();
|
||||
}
|
||||
|
||||
int_type
|
||||
overflow(int_type ch) override
|
||||
{
|
||||
if(! Traits::eq_int_type(ch, Traits::eof()))
|
||||
{
|
||||
Traits::assign(*this->pptr(),
|
||||
static_cast<CharT>(ch));
|
||||
flush(1);
|
||||
prepare();
|
||||
return ch;
|
||||
}
|
||||
flush();
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
int
|
||||
sync() override
|
||||
{
|
||||
flush();
|
||||
prepare();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
prepare()
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto mbs = buf_.prepare(
|
||||
read_size_or_throw(buf_, max_size));
|
||||
auto const mb = *mbs.begin();
|
||||
auto const p = buffer_cast<CharT*>(mb);
|
||||
this->setp(p,
|
||||
p + buffer_size(mb) / sizeof(CharT) - 1);
|
||||
}
|
||||
|
||||
void
|
||||
flush(int extra = 0)
|
||||
{
|
||||
buf_.commit(
|
||||
(this->pptr() - this->pbase() + extra) *
|
||||
sizeof(CharT));
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class DynamicBuffer,
|
||||
class CharT, class Traits, bool isMovable>
|
||||
class ostream_helper;
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_helper<
|
||||
DynamicBuffer, CharT, Traits, true>
|
||||
: public std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits, true> osb_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
ostream_helper(DynamicBuffer& buf);
|
||||
|
||||
ostream_helper(ostream_helper&& other);
|
||||
};
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_helper<DynamicBuffer, CharT, Traits, true>::
|
||||
ostream_helper(DynamicBuffer& buf)
|
||||
: std::basic_ostream<CharT, Traits>(
|
||||
&this->osb_)
|
||||
, osb_(buf)
|
||||
{
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_helper<DynamicBuffer, CharT, Traits, true>::
|
||||
ostream_helper(
|
||||
ostream_helper&& other)
|
||||
: std::basic_ostream<CharT, Traits>(&osb_)
|
||||
, osb_(std::move(other.osb_))
|
||||
{
|
||||
}
|
||||
|
||||
// This work-around is for libstdc++ versions that
|
||||
// don't have a movable std::basic_streambuf
|
||||
|
||||
template<class T>
|
||||
class ostream_helper_base
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<T> member;
|
||||
|
||||
ostream_helper_base(
|
||||
ostream_helper_base&&) = default;
|
||||
|
||||
explicit
|
||||
ostream_helper_base(T* t)
|
||||
: member(t)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_helper<
|
||||
DynamicBuffer, CharT, Traits, false>
|
||||
: private ostream_helper_base<ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits, false>>
|
||||
, public std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
ostream_helper(DynamicBuffer& buf)
|
||||
: ostream_helper_base<ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits, false>>(
|
||||
new ostream_buffer<DynamicBuffer,
|
||||
CharT, Traits, false>(buf))
|
||||
, std::basic_ostream<CharT, Traits>(this->member.get())
|
||||
{
|
||||
}
|
||||
|
||||
ostream_helper(ostream_helper&& other)
|
||||
: ostream_helper_base<ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits, false>>(
|
||||
std::move(other))
|
||||
, std::basic_ostream<CharT, Traits>(this->member.get())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,313 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_SHA1_HPP
|
||||
#define BOOST_BEAST_DETAIL_SHA1_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
// Based on https://github.com/vog/sha1
|
||||
/*
|
||||
Original authors:
|
||||
Steve Reid (Original C Code)
|
||||
Bruce Guenter (Small changes to fit into bglibs)
|
||||
Volker Grabsch (Translation to simpler C++ Code)
|
||||
Eugene Hopkinson (Safety improvements)
|
||||
Vincent Falco (beast adaptation)
|
||||
*/
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
namespace sha1 {
|
||||
|
||||
static std::size_t constexpr BLOCK_INTS = 16;
|
||||
static std::size_t constexpr BLOCK_BYTES = 64;
|
||||
static std::size_t constexpr DIGEST_BYTES = 20;
|
||||
|
||||
inline
|
||||
std::uint32_t
|
||||
rol(std::uint32_t value, std::size_t bits)
|
||||
{
|
||||
return (value << bits) | (value >> (32 - bits));
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint32_t
|
||||
blk(std::uint32_t block[BLOCK_INTS], std::size_t i)
|
||||
{
|
||||
return rol(
|
||||
block[(i+13)&15] ^ block[(i+8)&15] ^
|
||||
block[(i+2)&15] ^ block[i], 1);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
R0(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void
|
||||
R1(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
block[i] = blk(block, i);
|
||||
z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
R2(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
block[i] = blk(block, i);
|
||||
z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
R3(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
block[i] = blk(block, i);
|
||||
z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
R4(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
block[i] = blk(block, i);
|
||||
z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
make_block(std::uint8_t const* p,
|
||||
std::uint32_t block[BLOCK_INTS])
|
||||
{
|
||||
for(std::size_t i = 0; i < BLOCK_INTS; i++)
|
||||
block[i] =
|
||||
(static_cast<std::uint32_t>(p[4*i+3])) |
|
||||
(static_cast<std::uint32_t>(p[4*i+2]))<< 8 |
|
||||
(static_cast<std::uint32_t>(p[4*i+1]))<<16 |
|
||||
(static_cast<std::uint32_t>(p[4*i+0]))<<24;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
transform(
|
||||
std::uint32_t digest[], std::uint32_t block[BLOCK_INTS])
|
||||
{
|
||||
std::uint32_t a = digest[0];
|
||||
std::uint32_t b = digest[1];
|
||||
std::uint32_t c = digest[2];
|
||||
std::uint32_t d = digest[3];
|
||||
std::uint32_t e = digest[4];
|
||||
|
||||
R0(block, a, b, c, d, e, 0);
|
||||
R0(block, e, a, b, c, d, 1);
|
||||
R0(block, d, e, a, b, c, 2);
|
||||
R0(block, c, d, e, a, b, 3);
|
||||
R0(block, b, c, d, e, a, 4);
|
||||
R0(block, a, b, c, d, e, 5);
|
||||
R0(block, e, a, b, c, d, 6);
|
||||
R0(block, d, e, a, b, c, 7);
|
||||
R0(block, c, d, e, a, b, 8);
|
||||
R0(block, b, c, d, e, a, 9);
|
||||
R0(block, a, b, c, d, e, 10);
|
||||
R0(block, e, a, b, c, d, 11);
|
||||
R0(block, d, e, a, b, c, 12);
|
||||
R0(block, c, d, e, a, b, 13);
|
||||
R0(block, b, c, d, e, a, 14);
|
||||
R0(block, a, b, c, d, e, 15);
|
||||
R1(block, e, a, b, c, d, 0);
|
||||
R1(block, d, e, a, b, c, 1);
|
||||
R1(block, c, d, e, a, b, 2);
|
||||
R1(block, b, c, d, e, a, 3);
|
||||
R2(block, a, b, c, d, e, 4);
|
||||
R2(block, e, a, b, c, d, 5);
|
||||
R2(block, d, e, a, b, c, 6);
|
||||
R2(block, c, d, e, a, b, 7);
|
||||
R2(block, b, c, d, e, a, 8);
|
||||
R2(block, a, b, c, d, e, 9);
|
||||
R2(block, e, a, b, c, d, 10);
|
||||
R2(block, d, e, a, b, c, 11);
|
||||
R2(block, c, d, e, a, b, 12);
|
||||
R2(block, b, c, d, e, a, 13);
|
||||
R2(block, a, b, c, d, e, 14);
|
||||
R2(block, e, a, b, c, d, 15);
|
||||
R2(block, d, e, a, b, c, 0);
|
||||
R2(block, c, d, e, a, b, 1);
|
||||
R2(block, b, c, d, e, a, 2);
|
||||
R2(block, a, b, c, d, e, 3);
|
||||
R2(block, e, a, b, c, d, 4);
|
||||
R2(block, d, e, a, b, c, 5);
|
||||
R2(block, c, d, e, a, b, 6);
|
||||
R2(block, b, c, d, e, a, 7);
|
||||
R3(block, a, b, c, d, e, 8);
|
||||
R3(block, e, a, b, c, d, 9);
|
||||
R3(block, d, e, a, b, c, 10);
|
||||
R3(block, c, d, e, a, b, 11);
|
||||
R3(block, b, c, d, e, a, 12);
|
||||
R3(block, a, b, c, d, e, 13);
|
||||
R3(block, e, a, b, c, d, 14);
|
||||
R3(block, d, e, a, b, c, 15);
|
||||
R3(block, c, d, e, a, b, 0);
|
||||
R3(block, b, c, d, e, a, 1);
|
||||
R3(block, a, b, c, d, e, 2);
|
||||
R3(block, e, a, b, c, d, 3);
|
||||
R3(block, d, e, a, b, c, 4);
|
||||
R3(block, c, d, e, a, b, 5);
|
||||
R3(block, b, c, d, e, a, 6);
|
||||
R3(block, a, b, c, d, e, 7);
|
||||
R3(block, e, a, b, c, d, 8);
|
||||
R3(block, d, e, a, b, c, 9);
|
||||
R3(block, c, d, e, a, b, 10);
|
||||
R3(block, b, c, d, e, a, 11);
|
||||
R4(block, a, b, c, d, e, 12);
|
||||
R4(block, e, a, b, c, d, 13);
|
||||
R4(block, d, e, a, b, c, 14);
|
||||
R4(block, c, d, e, a, b, 15);
|
||||
R4(block, b, c, d, e, a, 0);
|
||||
R4(block, a, b, c, d, e, 1);
|
||||
R4(block, e, a, b, c, d, 2);
|
||||
R4(block, d, e, a, b, c, 3);
|
||||
R4(block, c, d, e, a, b, 4);
|
||||
R4(block, b, c, d, e, a, 5);
|
||||
R4(block, a, b, c, d, e, 6);
|
||||
R4(block, e, a, b, c, d, 7);
|
||||
R4(block, d, e, a, b, c, 8);
|
||||
R4(block, c, d, e, a, b, 9);
|
||||
R4(block, b, c, d, e, a, 10);
|
||||
R4(block, a, b, c, d, e, 11);
|
||||
R4(block, e, a, b, c, d, 12);
|
||||
R4(block, d, e, a, b, c, 13);
|
||||
R4(block, c, d, e, a, b, 14);
|
||||
R4(block, b, c, d, e, a, 15);
|
||||
|
||||
digest[0] += a;
|
||||
digest[1] += b;
|
||||
digest[2] += c;
|
||||
digest[3] += d;
|
||||
digest[4] += e;
|
||||
}
|
||||
|
||||
} // sha1
|
||||
|
||||
struct sha1_context
|
||||
{
|
||||
static unsigned int constexpr block_size = sha1::BLOCK_BYTES;
|
||||
static unsigned int constexpr digest_size = 20;
|
||||
|
||||
std::size_t buflen;
|
||||
std::size_t blocks;
|
||||
std::uint32_t digest[5];
|
||||
std::uint8_t buf[block_size];
|
||||
};
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
init(sha1_context& ctx) noexcept
|
||||
{
|
||||
ctx.buflen = 0;
|
||||
ctx.blocks = 0;
|
||||
ctx.digest[0] = 0x67452301;
|
||||
ctx.digest[1] = 0xefcdab89;
|
||||
ctx.digest[2] = 0x98badcfe;
|
||||
ctx.digest[3] = 0x10325476;
|
||||
ctx.digest[4] = 0xc3d2e1f0;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
update(sha1_context& ctx,
|
||||
void const* message, std::size_t size) noexcept
|
||||
{
|
||||
auto p = reinterpret_cast<
|
||||
std::uint8_t const*>(message);
|
||||
for(;;)
|
||||
{
|
||||
auto const n = (std::min)(
|
||||
size, sizeof(ctx.buf) - ctx.buflen);
|
||||
std::memcpy(ctx.buf + ctx.buflen, p, n);
|
||||
ctx.buflen += n;
|
||||
if(ctx.buflen != 64)
|
||||
return;
|
||||
p += n;
|
||||
size -= n;
|
||||
ctx.buflen = 0;
|
||||
std::uint32_t block[sha1::BLOCK_INTS];
|
||||
sha1::make_block(ctx.buf, block);
|
||||
sha1::transform(ctx.digest, block);
|
||||
++ctx.blocks;
|
||||
}
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
finish(sha1_context& ctx, void* digest) noexcept
|
||||
{
|
||||
using sha1::BLOCK_INTS;
|
||||
using sha1::BLOCK_BYTES;
|
||||
|
||||
std::uint64_t total_bits =
|
||||
(ctx.blocks*64 + ctx.buflen) * 8;
|
||||
// pad
|
||||
ctx.buf[ctx.buflen++] = 0x80;
|
||||
auto const buflen = ctx.buflen;
|
||||
while(ctx.buflen < 64)
|
||||
ctx.buf[ctx.buflen++] = 0x00;
|
||||
std::uint32_t block[BLOCK_INTS];
|
||||
sha1::make_block(ctx.buf, block);
|
||||
if(buflen > BLOCK_BYTES - 8)
|
||||
{
|
||||
sha1::transform(ctx.digest, block);
|
||||
for(size_t i = 0; i < BLOCK_INTS - 2; i++)
|
||||
block[i] = 0;
|
||||
}
|
||||
|
||||
/* Append total_bits, split this uint64_t into two uint32_t */
|
||||
block[BLOCK_INTS - 1] = total_bits & 0xffffffff;
|
||||
block[BLOCK_INTS - 2] = (total_bits >> 32);
|
||||
sha1::transform(ctx.digest, block);
|
||||
for(std::size_t i = 0; i < sha1::DIGEST_BYTES/4; i++)
|
||||
{
|
||||
std::uint8_t* d =
|
||||
reinterpret_cast<std::uint8_t*>(digest) + 4 * i;
|
||||
d[3] = ctx.digest[i] & 0xff;
|
||||
d[2] = (ctx.digest[i] >> 8) & 0xff;
|
||||
d[1] = (ctx.digest[i] >> 16) & 0xff;
|
||||
d[0] = (ctx.digest[i] >> 24) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_STATIC_OSTREAM_HPP
|
||||
#define BOOST_BEAST_DETAIL_STATIC_OSTREAM_HPP
|
||||
|
||||
#include <locale>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
|
||||
|
||||
class static_ostream_buffer
|
||||
: public std::basic_streambuf<char>
|
||||
{
|
||||
using CharT = char;
|
||||
using Traits = std::char_traits<CharT>;
|
||||
using int_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::int_type;
|
||||
using traits_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::traits_type;
|
||||
|
||||
char* data_;
|
||||
std::size_t size_;
|
||||
std::size_t len_ = 0;
|
||||
std::string s_;
|
||||
|
||||
public:
|
||||
static_ostream_buffer(static_ostream_buffer&&) = delete;
|
||||
static_ostream_buffer(static_ostream_buffer const&) = delete;
|
||||
|
||||
static_ostream_buffer(char* data, std::size_t size)
|
||||
: data_(data)
|
||||
, size_(size)
|
||||
{
|
||||
this->setp(data_, data_ + size - 1);
|
||||
}
|
||||
|
||||
~static_ostream_buffer() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
string_view
|
||||
str() const
|
||||
{
|
||||
if(! s_.empty())
|
||||
return {s_.data(), len_};
|
||||
return {data_, len_};
|
||||
}
|
||||
|
||||
int_type
|
||||
overflow(int_type ch) override
|
||||
{
|
||||
if(! Traits::eq_int_type(ch, Traits::eof()))
|
||||
{
|
||||
Traits::assign(*this->pptr(),
|
||||
static_cast<CharT>(ch));
|
||||
flush(1);
|
||||
prepare();
|
||||
return ch;
|
||||
}
|
||||
flush();
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
int
|
||||
sync() override
|
||||
{
|
||||
flush();
|
||||
prepare();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
prepare()
|
||||
{
|
||||
static auto const growth_factor = 1.5;
|
||||
|
||||
if(len_ < size_ - 1)
|
||||
{
|
||||
this->setp(
|
||||
data_ + len_, data_ + size_ - 2);
|
||||
return;
|
||||
}
|
||||
if(s_.empty())
|
||||
{
|
||||
s_.resize(static_cast<std::size_t>(
|
||||
growth_factor * len_));
|
||||
Traits::copy(&s_[0], data_, len_);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_.resize(static_cast<std::size_t>(
|
||||
growth_factor * len_));
|
||||
}
|
||||
this->setp(&s_[len_], &s_[len_] +
|
||||
s_.size() - len_ - 1);
|
||||
}
|
||||
|
||||
void
|
||||
flush(int extra = 0)
|
||||
{
|
||||
len_ += static_cast<std::size_t>(
|
||||
this->pptr() - this->pbase() + extra);
|
||||
}
|
||||
};
|
||||
|
||||
class static_ostream : public std::basic_ostream<char>
|
||||
{
|
||||
static_ostream_buffer osb_;
|
||||
|
||||
public:
|
||||
static_ostream(char* data, std::size_t size)
|
||||
: std::basic_ostream<char>(&this->osb_)
|
||||
, osb_(data, size)
|
||||
{
|
||||
imbue(std::locale::classic());
|
||||
}
|
||||
|
||||
string_view
|
||||
str() const
|
||||
{
|
||||
return osb_.str();
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_STATIC_STRING_HPP
|
||||
#define BOOST_BEAST_DETAIL_STATIC_STRING_HPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// Because k-ballo said so
|
||||
template<class T>
|
||||
using is_input_iterator =
|
||||
std::integral_constant<bool,
|
||||
! std::is_integral<T>::value>;
|
||||
|
||||
template<class CharT, class Traits>
|
||||
int
|
||||
lexicographical_compare(
|
||||
CharT const* s1, std::size_t n1,
|
||||
CharT const* s2, std::size_t n2)
|
||||
{
|
||||
if(n1 < n2)
|
||||
return Traits::compare(
|
||||
s1, s2, n1) <= 0 ? -1 : 1;
|
||||
if(n1 > n2)
|
||||
return Traits::compare(
|
||||
s1, s2, n2) >= 0 ? 1 : -1;
|
||||
return Traits::compare(s1, s2, n1);
|
||||
}
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
int
|
||||
lexicographical_compare(
|
||||
basic_string_view<CharT, Traits> s1,
|
||||
CharT const* s2, std::size_t n2)
|
||||
{
|
||||
return lexicographical_compare<CharT, Traits>(
|
||||
s1.data(), s1.size(), s2, n2);
|
||||
}
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
int
|
||||
lexicographical_compare(
|
||||
basic_string_view<CharT, Traits> s1,
|
||||
basic_string_view<CharT, Traits> s2)
|
||||
{
|
||||
return lexicographical_compare<CharT, Traits>(
|
||||
s1.data(), s1.size(), s2.data(), s2.size());
|
||||
}
|
||||
|
||||
// Maximum number of characters in the decimal
|
||||
// representation of a binary number. This includes
|
||||
// the potential minus sign.
|
||||
//
|
||||
inline
|
||||
std::size_t constexpr
|
||||
max_digits(std::size_t bytes)
|
||||
{
|
||||
return static_cast<std::size_t>(
|
||||
bytes * 2.41) + 1 + 1;
|
||||
}
|
||||
|
||||
template<class CharT, class Integer, class Traits>
|
||||
CharT*
|
||||
raw_to_string(
|
||||
CharT* buf, Integer x, std::true_type)
|
||||
{
|
||||
if(x == 0)
|
||||
{
|
||||
Traits::assign(*--buf, '0');
|
||||
return buf;
|
||||
}
|
||||
if(x < 0)
|
||||
{
|
||||
x = -x;
|
||||
for(;x > 0; x /= 10)
|
||||
Traits::assign(*--buf ,
|
||||
"0123456789"[x % 10]);
|
||||
Traits::assign(*--buf, '-');
|
||||
return buf;
|
||||
}
|
||||
for(;x > 0; x /= 10)
|
||||
Traits::assign(*--buf ,
|
||||
"0123456789"[x % 10]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<class CharT, class Integer, class Traits>
|
||||
CharT*
|
||||
raw_to_string(
|
||||
CharT* buf, Integer x, std::false_type)
|
||||
{
|
||||
if(x == 0)
|
||||
{
|
||||
*--buf = '0';
|
||||
return buf;
|
||||
}
|
||||
for(;x > 0; x /= 10)
|
||||
Traits::assign(*--buf ,
|
||||
"0123456789"[x % 10]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<
|
||||
class CharT,
|
||||
class Integer,
|
||||
class Traits = std::char_traits<CharT>>
|
||||
CharT*
|
||||
raw_to_string(CharT* last, std::size_t size, Integer i)
|
||||
{
|
||||
boost::ignore_unused(size);
|
||||
BOOST_ASSERT(size >= max_digits(sizeof(Integer)));
|
||||
return raw_to_string<CharT, Integer, Traits>(
|
||||
last, i, std::is_signed<Integer>{});
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,269 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||
#define BOOST_BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
// A few workarounds to keep things working
|
||||
|
||||
namespace boost {
|
||||
namespace asio {
|
||||
|
||||
// for has_get_io_service
|
||||
class io_service;
|
||||
|
||||
// for is_dynamic_buffer
|
||||
template<class Allocator>
|
||||
class basic_streambuf;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// for is_buffer_sequence
|
||||
template<class Buffer, class Buffers>
|
||||
class consuming_buffers;
|
||||
|
||||
} // detail
|
||||
|
||||
} // asio
|
||||
} // boost
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
//
|
||||
// utilities
|
||||
//
|
||||
|
||||
template<class... Ts>
|
||||
struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template<class... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template<class T>
|
||||
inline
|
||||
void
|
||||
accept_rv(T){}
|
||||
|
||||
template<class U>
|
||||
std::size_t constexpr
|
||||
max_sizeof()
|
||||
{
|
||||
return sizeof(U);
|
||||
}
|
||||
|
||||
template<class U0, class U1, class... Us>
|
||||
std::size_t constexpr
|
||||
max_sizeof()
|
||||
{
|
||||
return
|
||||
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
|
||||
max_sizeof<U0>() : max_sizeof<U1, Us...>();
|
||||
}
|
||||
|
||||
template<unsigned N, class T, class... Tn>
|
||||
struct repeat_tuple_impl
|
||||
{
|
||||
using type = typename repeat_tuple_impl<
|
||||
N - 1, T, T, Tn...>::type;
|
||||
};
|
||||
|
||||
template<class T, class... Tn>
|
||||
struct repeat_tuple_impl<0, T, Tn...>
|
||||
{
|
||||
using type = std::tuple<T, Tn...>;
|
||||
};
|
||||
|
||||
template<unsigned N, class T>
|
||||
struct repeat_tuple
|
||||
{
|
||||
using type =
|
||||
typename repeat_tuple_impl<N-1, T>::type;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct repeat_tuple<0, T>
|
||||
{
|
||||
using type = std::tuple<>;
|
||||
};
|
||||
|
||||
template<class R, class C, class ...A>
|
||||
auto
|
||||
is_invocable_test(C&& c, int, A&& ...a)
|
||||
-> decltype(std::is_convertible<
|
||||
decltype(c(std::forward<A>(a)...)), R>::value ||
|
||||
std::is_same<R, void>::value,
|
||||
std::true_type());
|
||||
|
||||
template<class R, class C, class ...A>
|
||||
std::false_type
|
||||
is_invocable_test(C&& c, long, A&& ...a);
|
||||
|
||||
/** Metafunction returns `true` if F callable as R(A...)
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
is_invocable<T, void(std::string)>
|
||||
@endcode
|
||||
*/
|
||||
/** @{ */
|
||||
template<class C, class F>
|
||||
struct is_invocable : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class C, class R, class ...A>
|
||||
struct is_invocable<C, R(A...)>
|
||||
: decltype(is_invocable_test<R>(
|
||||
std::declval<C>(), 1, std::declval<A>()...))
|
||||
{
|
||||
};
|
||||
/** @} */
|
||||
|
||||
// for span
|
||||
template<class T, class E, class = void>
|
||||
struct is_contiguous_container: std::false_type {};
|
||||
|
||||
template<class T, class E>
|
||||
struct is_contiguous_container<T, E, void_t<
|
||||
decltype(
|
||||
std::declval<std::size_t&>() = std::declval<T const&>().size(),
|
||||
std::declval<E*&>() = std::declval<T&>().data(),
|
||||
(void)0),
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename std::remove_cv<E>::type,
|
||||
typename std::remove_cv<
|
||||
typename std::remove_pointer<
|
||||
decltype(std::declval<T&>().data())
|
||||
>::type
|
||||
>::type
|
||||
>::value
|
||||
>::type>>: std::true_type
|
||||
{};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// buffer concepts
|
||||
//
|
||||
|
||||
// Types that meet the requirements,
|
||||
// for use with std::declval only.
|
||||
template<class BufferType>
|
||||
struct BufferSequence
|
||||
{
|
||||
using value_type = BufferType;
|
||||
using const_iterator = BufferType const*;
|
||||
~BufferSequence();
|
||||
BufferSequence(BufferSequence const&) = default;
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
};
|
||||
using ConstBufferSequence =
|
||||
BufferSequence<boost::asio::const_buffer>;
|
||||
using MutableBufferSequence =
|
||||
BufferSequence<boost::asio::mutable_buffer>;
|
||||
|
||||
template<class T, class B, class = void>
|
||||
struct is_buffer_sequence : std::false_type {};
|
||||
|
||||
template<class T, class B>
|
||||
struct is_buffer_sequence<T, B, void_t<decltype(
|
||||
std::declval<typename T::value_type>(),
|
||||
std::declval<typename T::const_iterator&>() =
|
||||
std::declval<T const&>().begin(),
|
||||
std::declval<typename T::const_iterator&>() =
|
||||
std::declval<T const&>().end(),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
std::is_convertible<typename T::value_type, B>::value &&
|
||||
#if 0
|
||||
std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename T::const_iterator>::iterator_category>::value
|
||||
#else
|
||||
// workaround:
|
||||
// boost::asio::detail::consuming_buffers::const_iterator
|
||||
// is not bidirectional
|
||||
std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename T::const_iterator>::iterator_category>::value
|
||||
#endif
|
||||
>
|
||||
{
|
||||
};
|
||||
|
||||
#if 0
|
||||
// workaround:
|
||||
// boost::asio::detail::consuming_buffers::const_iterator
|
||||
// is not bidirectional
|
||||
template<class Buffer, class Buffers, class B>
|
||||
struct is_buffer_sequence<
|
||||
boost::asio::detail::consuming_buffers<Buffer, Buffers>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
template<class B1, class... Bn>
|
||||
struct is_all_const_buffer_sequence
|
||||
: std::integral_constant<bool,
|
||||
is_buffer_sequence<B1, boost::asio::const_buffer>::value &&
|
||||
is_all_const_buffer_sequence<Bn...>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template<class B1>
|
||||
struct is_all_const_buffer_sequence<B1>
|
||||
: is_buffer_sequence<B1, boost::asio::const_buffer>
|
||||
{
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Copyright (c) 2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DETAIL_VARINT_HPP
|
||||
#define BOOST_BEAST_DETAIL_VARINT_HPP
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// https://developers.google.com/protocol-buffers/docs/encoding#varints
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
varint_size(std::size_t value)
|
||||
{
|
||||
std::size_t n = 1;
|
||||
while(value > 127)
|
||||
{
|
||||
++n;
|
||||
value /= 128;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
std::size_t
|
||||
varint_read(FwdIt& first)
|
||||
{
|
||||
using value_type = typename
|
||||
std::iterator_traits<FwdIt>::value_type;
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_integral<value_type>::value &&
|
||||
sizeof(value_type) == 1);
|
||||
std::size_t value = 0;
|
||||
std::size_t factor = 1;
|
||||
while((*first & 0x80) != 0)
|
||||
{
|
||||
value += (*first++ & 0x7f) * factor;
|
||||
factor *= 128;
|
||||
}
|
||||
value += *first++ * factor;
|
||||
return value;
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
void
|
||||
varint_write(FwdIt& first, std::size_t value)
|
||||
{
|
||||
using value_type = typename
|
||||
std::iterator_traits<FwdIt>::value_type;
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_integral<value_type>::value &&
|
||||
sizeof(value_type) == 1);
|
||||
while(value > 127)
|
||||
{
|
||||
*first++ = static_cast<value_type>(
|
||||
0x80 | value);
|
||||
value /= 128;
|
||||
}
|
||||
*first++ = static_cast<value_type>(value);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_DRAIN_BUFFER_HPP
|
||||
#define BOOST_BEAST_DRAIN_BUFFER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A @b DynamicBuffer which does not retain its input sequence.
|
||||
|
||||
This object implements a dynamic buffer with a fixed size
|
||||
output area, not dynamically allocated, and whose input
|
||||
sequence is always length zero. Bytes committed from the
|
||||
output area to the input area are always discarded. This
|
||||
is useful for calling interfaces that require a dynamic
|
||||
buffer for storage, but where the caller does not want
|
||||
to retain the data.
|
||||
*/
|
||||
class drain_buffer
|
||||
{
|
||||
char buf_[512];
|
||||
std::size_t n_ = 0;
|
||||
|
||||
public:
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = boost::asio::null_buffers;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = boost::asio::mutable_buffers_1;
|
||||
|
||||
/// Constructor
|
||||
drain_buffer() = default;
|
||||
|
||||
/// Copy constructor
|
||||
drain_buffer(drain_buffer const&)
|
||||
{
|
||||
// Previously returned ranges are invalidated
|
||||
}
|
||||
|
||||
/// Copy assignment
|
||||
drain_buffer&
|
||||
operator=(drain_buffer const&)
|
||||
{
|
||||
n_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Return the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return sizeof(buf_);
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return max_size();
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if the size would exceed the buffer limit
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
if(n > sizeof(buf_))
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
n_ = n;
|
||||
return {buf_, n_};
|
||||
}
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
This call always discards the output sequence.
|
||||
The size of the input sequence will remain at zero.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t)
|
||||
{
|
||||
}
|
||||
|
||||
/** Remove bytes from the input sequence.
|
||||
|
||||
This call has no effect.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_ERROR_HPP
|
||||
#define BOOST_BEAST_ERROR_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/// The type of error code used by the library
|
||||
using error_code = boost::system::error_code;
|
||||
|
||||
/// The type of system error thrown by the library
|
||||
using system_error = boost::system::system_error;
|
||||
|
||||
/// The type of error category used by the library
|
||||
using error_category = boost::system::error_category;
|
||||
|
||||
/// A function to return the generic error category used by the library
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
error_category const&
|
||||
generic_category();
|
||||
#else
|
||||
using boost::system::generic_category;
|
||||
#endif
|
||||
|
||||
/// A function to return the system error category used by the library
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
error_category const&
|
||||
system_category();
|
||||
#else
|
||||
using boost::system::system_category;
|
||||
#endif
|
||||
|
||||
/// The type of error condition used by the library
|
||||
using error_condition = boost::system::error_condition;
|
||||
|
||||
/// The set of constants used for cross-platform error codes
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
enum errc{};
|
||||
#else
|
||||
namespace errc = boost::system::errc;
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_FILE_HPP
|
||||
#define BOOST_BEAST_CORE_FILE_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/file_base.hpp>
|
||||
#include <boost/beast/core/file_posix.hpp>
|
||||
#include <boost/beast/core/file_stdio.hpp>
|
||||
#include <boost/beast/core/file_win32.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** An implementation of File.
|
||||
|
||||
This alias is set to the best available implementation
|
||||
of @b File given the platform and build settings.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
struct file : file_stdio
|
||||
{
|
||||
};
|
||||
#else
|
||||
#if BOOST_BEAST_USE_WIN32_FILE
|
||||
using file = file_win32;
|
||||
#elif BOOST_BEAST_USE_POSIX_FILE
|
||||
using file = file_posix;
|
||||
#else
|
||||
using file = file_stdio;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_FILE_BASE_HPP
|
||||
#define BOOST_BEAST_CORE_FILE_BASE_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** File open modes
|
||||
|
||||
These modes are used when opening files using
|
||||
instances of the @b File concept.
|
||||
|
||||
@see file_stdio
|
||||
*/
|
||||
enum class file_mode
|
||||
{
|
||||
/// Random reading
|
||||
read,
|
||||
|
||||
/// Sequential reading
|
||||
scan,
|
||||
|
||||
/** Random writing to a new or truncated file
|
||||
|
||||
@li If the file does not exist, it is created.
|
||||
|
||||
@li If the file exists, it is truncated to
|
||||
zero size upon opening.
|
||||
*/
|
||||
write,
|
||||
|
||||
/** Random writing to new file only
|
||||
|
||||
If the file exists, an error is generated.
|
||||
*/
|
||||
write_new,
|
||||
|
||||
/** Random writing to existing file
|
||||
|
||||
If the file does not exist, an error is generated.
|
||||
*/
|
||||
write_existing,
|
||||
|
||||
/** Appending to a new or truncated file
|
||||
|
||||
The current file position shall be set to the end of
|
||||
the file prior to each write.
|
||||
|
||||
@li If the file does not exist, it is created.
|
||||
|
||||
@li If the file exists, it is truncated to
|
||||
zero size upon opening.
|
||||
*/
|
||||
append,
|
||||
|
||||
/** Appending to a new file only
|
||||
|
||||
The current file position shall be set to the end of
|
||||
the file prior to each write.
|
||||
|
||||
If the file exists, an error is generated.
|
||||
*/
|
||||
append_new,
|
||||
|
||||
/** Appending to an existing file
|
||||
|
||||
The current file position shall be set to the end of
|
||||
the file prior to each write.
|
||||
|
||||
If the file does not exist, an error is generated.
|
||||
*/
|
||||
append_existing
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_FILE_POSIX_HPP
|
||||
#define BOOST_BEAST_CORE_FILE_POSIX_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if ! defined(BOOST_BEAST_NO_POSIX_FILE)
|
||||
# if ! defined(__APPLE__) && ! defined(__linux__)
|
||||
# define BOOST_BEAST_NO_POSIX_FILE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ! defined(BOOST_BEAST_USE_POSIX_FILE)
|
||||
# if ! defined(BOOST_BEAST_NO_POSIX_FILE)
|
||||
# define BOOST_BEAST_USE_POSIX_FILE 1
|
||||
# else
|
||||
# define BOOST_BEAST_USE_POSIX_FILE 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BOOST_BEAST_USE_POSIX_FILE
|
||||
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/file_base.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** An implementation of File for POSIX systems.
|
||||
|
||||
This class implements a @b File using POSIX interfaces.
|
||||
*/
|
||||
class file_posix
|
||||
{
|
||||
int fd_ = -1;
|
||||
|
||||
public:
|
||||
/** The type of the underlying file handle.
|
||||
|
||||
This is platform-specific.
|
||||
*/
|
||||
using native_handle_type = int;
|
||||
|
||||
/** Destructor
|
||||
|
||||
If the file is open it is first closed.
|
||||
*/
|
||||
~file_posix();
|
||||
|
||||
/** Constructor
|
||||
|
||||
There is no open file initially.
|
||||
*/
|
||||
file_posix() = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_posix(file_posix&& other);
|
||||
|
||||
/** Assignment
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_posix& operator=(file_posix&& other);
|
||||
|
||||
/// Returns the native handle associated with the file.
|
||||
native_handle_type
|
||||
native_handle() const
|
||||
{
|
||||
return fd_;
|
||||
}
|
||||
|
||||
/** Set the native handle associated with the file.
|
||||
|
||||
If the file is open it is first closed.
|
||||
|
||||
@param fd The native file handle to assign.
|
||||
*/
|
||||
void
|
||||
native_handle(native_handle_type fd);
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return fd_ != -1;
|
||||
}
|
||||
|
||||
/** Close the file if open
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
close(error_code& ec);
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Return the size of the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The size in bytes
|
||||
*/
|
||||
std::uint64_t
|
||||
size(error_code& ec) const;
|
||||
|
||||
/** Return the current position in the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The offset in bytes from the beginning of the file
|
||||
*/
|
||||
std::uint64_t
|
||||
pos(error_code& ec) const;
|
||||
|
||||
/** Adjust the current position in the open file
|
||||
|
||||
@param offset The offset in bytes from the beginning of the file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
seek(std::uint64_t offset, error_code& ec);
|
||||
|
||||
/** Read from the open file
|
||||
|
||||
@param buffer The buffer for storing the result of the read
|
||||
|
||||
@param n The number of bytes to read
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
read(void* buffer, std::size_t n, error_code& ec) const;
|
||||
|
||||
/** Write to the open file
|
||||
|
||||
@param buffer The buffer holding the data to write
|
||||
|
||||
@param n The number of bytes to write
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
write(void const* buffer, std::size_t n, error_code& ec);
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/file_posix.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_FILE_STDIO_HPP
|
||||
#define BOOST_BEAST_CORE_FILE_STDIO_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/file_base.hpp>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** An implementation of File which uses cstdio.
|
||||
|
||||
This class implements a file using the interfaces present
|
||||
in the C++ Standard Library, in `<stdio>`.
|
||||
*/
|
||||
class file_stdio
|
||||
{
|
||||
FILE* f_ = nullptr;
|
||||
|
||||
public:
|
||||
/** The type of the underlying file handle.
|
||||
|
||||
This is platform-specific.
|
||||
*/
|
||||
using native_handle_type = FILE*;
|
||||
|
||||
/** Destructor
|
||||
|
||||
If the file is open it is first closed.
|
||||
*/
|
||||
~file_stdio();
|
||||
|
||||
/** Constructor
|
||||
|
||||
There is no open file initially.
|
||||
*/
|
||||
file_stdio() = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_stdio(file_stdio&& other);
|
||||
|
||||
/** Assignment
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_stdio& operator=(file_stdio&& other);
|
||||
|
||||
/// Returns the native handle associated with the file.
|
||||
FILE*
|
||||
native_handle() const
|
||||
{
|
||||
return f_;
|
||||
}
|
||||
|
||||
/** Set the native handle associated with the file.
|
||||
|
||||
If the file is open it is first closed.
|
||||
|
||||
@param f The native file handle to assign.
|
||||
*/
|
||||
void
|
||||
native_handle(FILE* f);
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return f_ != nullptr;
|
||||
}
|
||||
|
||||
/** Close the file if open
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
close(error_code& ec);
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Return the size of the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The size in bytes
|
||||
*/
|
||||
std::uint64_t
|
||||
size(error_code& ec) const;
|
||||
|
||||
/** Return the current position in the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The offset in bytes from the beginning of the file
|
||||
*/
|
||||
std::uint64_t
|
||||
pos(error_code& ec) const;
|
||||
|
||||
/** Adjust the current position in the open file
|
||||
|
||||
@param offset The offset in bytes from the beginning of the file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
seek(std::uint64_t offset, error_code& ec);
|
||||
|
||||
/** Read from the open file
|
||||
|
||||
@param buffer The buffer for storing the result of the read
|
||||
|
||||
@param n The number of bytes to read
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
read(void* buffer, std::size_t n, error_code& ec) const;
|
||||
|
||||
/** Write to the open file
|
||||
|
||||
@param buffer The buffer holding the data to write
|
||||
|
||||
@param n The number of bytes to write
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
write(void const* buffer, std::size_t n, error_code& ec);
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/file_stdio.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_FILE_WIN32_HPP
|
||||
#define BOOST_BEAST_CORE_FILE_WIN32_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if ! defined(BOOST_BEAST_USE_WIN32_FILE)
|
||||
# ifdef BOOST_MSVC
|
||||
# define BOOST_BEAST_USE_WIN32_FILE 1
|
||||
# else
|
||||
# define BOOST_BEAST_USE_WIN32_FILE 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BOOST_BEAST_USE_WIN32_FILE
|
||||
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/file_base.hpp>
|
||||
#include <boost/detail/winapi/basic_types.hpp>
|
||||
#include <boost/detail/winapi/handles.hpp>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** An implementation of File for Win32.
|
||||
|
||||
This class implements a @b File using Win32 native interfaces.
|
||||
*/
|
||||
class file_win32
|
||||
{
|
||||
boost::detail::winapi::HANDLE_ h_ =
|
||||
boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
|
||||
public:
|
||||
/** The type of the underlying file handle.
|
||||
|
||||
This is platform-specific.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using native_handle_type = HANDLE;
|
||||
#else
|
||||
using native_handle_type = boost::detail::winapi::HANDLE_;
|
||||
#endif
|
||||
|
||||
/** Destructor
|
||||
|
||||
If the file is open it is first closed.
|
||||
*/
|
||||
~file_win32();
|
||||
|
||||
/** Constructor
|
||||
|
||||
There is no open file initially.
|
||||
*/
|
||||
file_win32() = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_win32(file_win32&& other);
|
||||
|
||||
/** Assignment
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_win32& operator=(file_win32&& other);
|
||||
|
||||
/// Returns the native handle associated with the file.
|
||||
native_handle_type
|
||||
native_handle()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Set the native handle associated with the file.
|
||||
|
||||
If the file is open it is first closed.
|
||||
|
||||
@param h The native file handle to assign.
|
||||
*/
|
||||
void
|
||||
native_handle(native_handle_type h);
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
}
|
||||
|
||||
/** Close the file if open
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
close(error_code& ec);
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Return the size of the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The size in bytes
|
||||
*/
|
||||
std::uint64_t
|
||||
size(error_code& ec) const;
|
||||
|
||||
/** Return the current position in the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The offset in bytes from the beginning of the file
|
||||
*/
|
||||
std::uint64_t
|
||||
pos(error_code& ec);
|
||||
|
||||
/** Adjust the current position in the open file
|
||||
|
||||
@param offset The offset in bytes from the beginning of the file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
seek(std::uint64_t offset, error_code& ec);
|
||||
|
||||
/** Read from the open file
|
||||
|
||||
@param buffer The buffer for storing the result of the read
|
||||
|
||||
@param n The number of bytes to read
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
read(void* buffer, std::size_t n, error_code& ec);
|
||||
|
||||
/** Write to the open file
|
||||
|
||||
@param buffer The buffer holding the data to write
|
||||
|
||||
@param n The number of bytes to write
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
write(void const* buffer, std::size_t n, error_code& ec);
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/file_win32.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,345 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_FLAT_BUFFER_HPP
|
||||
#define BOOST_BEAST_FLAT_BUFFER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A linear dynamic buffer.
|
||||
|
||||
Objects of this type meet the requirements of @b DynamicBuffer
|
||||
and offer additional invariants:
|
||||
|
||||
@li Buffer sequences returned by @ref data and @ref prepare
|
||||
will always be of length one.
|
||||
|
||||
@li A configurable maximum buffer size may be set upon
|
||||
construction. Attempts to exceed the buffer size will throw
|
||||
`std::length_error`.
|
||||
|
||||
Upon construction, a maximum size for the buffer may be
|
||||
specified. If this limit is exceeded, the `std::length_error`
|
||||
exception will be thrown.
|
||||
|
||||
@note This class is designed for use with algorithms that
|
||||
take dynamic buffers as parameters, and are optimized
|
||||
for the case where the input sequence or output sequence
|
||||
is stored in a single contiguous buffer.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_flat_buffer
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
: private detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
#else
|
||||
using allocator_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>;
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
min_size = 512
|
||||
};
|
||||
|
||||
template<class OtherAlloc>
|
||||
friend class basic_flat_buffer;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<allocator_type>;
|
||||
|
||||
static
|
||||
inline
|
||||
std::size_t
|
||||
dist(char const* first, char const* last)
|
||||
{
|
||||
return static_cast<std::size_t>(last - first);
|
||||
}
|
||||
|
||||
char* begin_;
|
||||
char* in_;
|
||||
char* out_;
|
||||
char* last_;
|
||||
char* end_;
|
||||
std::size_t max_;
|
||||
|
||||
public:
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = boost::asio::const_buffers_1;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = boost::asio::mutable_buffers_1;
|
||||
|
||||
/// Destructor
|
||||
~basic_flat_buffer();
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
*/
|
||||
basic_flat_buffer();
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
|
||||
@param limit The setting for @ref max_size.
|
||||
*/
|
||||
explicit
|
||||
basic_flat_buffer(std::size_t limit);
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
|
||||
@param alloc The allocator to construct with.
|
||||
*/
|
||||
explicit
|
||||
basic_flat_buffer(Allocator const& alloc);
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
|
||||
@param limit The setting for @ref max_size.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_flat_buffer(
|
||||
std::size_t limit, Allocator const& alloc);
|
||||
|
||||
/** Constructor
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
*/
|
||||
basic_flat_buffer(basic_flat_buffer&& other);
|
||||
|
||||
/** Constructor
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_flat_buffer(
|
||||
basic_flat_buffer&& other, Allocator const& alloc);
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
basic_flat_buffer(basic_flat_buffer const& other);
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_flat_buffer(basic_flat_buffer const& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer(
|
||||
basic_flat_buffer<OtherAlloc> const& other);
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer(
|
||||
basic_flat_buffer<OtherAlloc> const& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Assignment
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
the object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
*/
|
||||
basic_flat_buffer&
|
||||
operator=(basic_flat_buffer&& other);
|
||||
|
||||
/** Assignment
|
||||
|
||||
After the copy, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
basic_flat_buffer&
|
||||
operator=(basic_flat_buffer const& other);
|
||||
|
||||
/** Copy assignment
|
||||
|
||||
After the copy, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer&
|
||||
operator=(basic_flat_buffer<OtherAlloc> const& other);
|
||||
|
||||
/// Returns a copy of the associated allocator.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return this->member();
|
||||
}
|
||||
|
||||
/// Returns the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return dist(in_, out_);
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return max_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return dist(begin_, end_);
|
||||
}
|
||||
|
||||
/// Get a list of buffers that represent the input sequence.
|
||||
const_buffers_type
|
||||
data() const
|
||||
{
|
||||
return {in_, dist(in_, out_)};
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if `size() + n` exceeds `max_size()`.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@param n The number of bytes to move. If this is larger than
|
||||
the number of bytes in the output sequences, then the entire
|
||||
output sequences is moved.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
out_ += (std::min)(n, dist(out_, last_));
|
||||
}
|
||||
|
||||
/** Remove bytes from the input sequence.
|
||||
|
||||
If `n` is greater than the number of bytes in the input
|
||||
sequence, all bytes in the input sequence are removed.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n);
|
||||
|
||||
/** Reallocate the buffer to fit the input sequence.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
shrink_to_fit();
|
||||
|
||||
/// Exchange two flat buffers
|
||||
template<class Alloc>
|
||||
friend
|
||||
void
|
||||
swap(
|
||||
basic_flat_buffer<Alloc>& lhs,
|
||||
basic_flat_buffer<Alloc>& rhs);
|
||||
|
||||
private:
|
||||
void
|
||||
reset();
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
copy_from(DynamicBuffer const& other);
|
||||
|
||||
void
|
||||
move_assign(basic_flat_buffer&, std::true_type);
|
||||
|
||||
void
|
||||
move_assign(basic_flat_buffer&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_flat_buffer const&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_flat_buffer const&, std::false_type);
|
||||
|
||||
void
|
||||
swap(basic_flat_buffer&);
|
||||
|
||||
void
|
||||
swap(basic_flat_buffer&, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_flat_buffer&, std::false_type);
|
||||
};
|
||||
|
||||
using flat_buffer =
|
||||
basic_flat_buffer<std::allocator<char>>;
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/flat_buffer.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,245 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_FLAT_STATIC_BUFFER_HPP
|
||||
#define BOOST_BEAST_FLAT_STATIC_BUFFER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A flat @b DynamicBuffer with a fixed size internal buffer.
|
||||
|
||||
Buffer sequences returned by @ref data and @ref prepare
|
||||
will always be of length one.
|
||||
Ownership of the underlying storage belongs to the derived class.
|
||||
|
||||
@note Variables are usually declared using the template class
|
||||
@ref flat_static_buffer; however, to reduce the number of instantiations
|
||||
of template functions receiving static stream buffer arguments in a
|
||||
deduced context, the signature of the receiving function should use
|
||||
@ref flat_static_buffer_base.
|
||||
|
||||
When used with @ref flat_static_buffer this implements a dynamic
|
||||
buffer using no memory allocations.
|
||||
|
||||
@see @ref flat_static_buffer
|
||||
*/
|
||||
class flat_static_buffer_base
|
||||
{
|
||||
char* begin_;
|
||||
char* in_;
|
||||
char* out_;
|
||||
char* last_;
|
||||
char* end_;
|
||||
|
||||
flat_static_buffer_base(flat_static_buffer_base const& other) = delete;
|
||||
flat_static_buffer_base& operator=(flat_static_buffer_base const&) = delete;
|
||||
|
||||
public:
|
||||
/** The type used to represent the input sequence as a list of buffers.
|
||||
|
||||
This buffer sequence is guaranteed to have length 1.
|
||||
*/
|
||||
using const_buffers_type = boost::asio::const_buffers_1;
|
||||
|
||||
/** The type used to represent the mutable input sequence as a list of buffers.
|
||||
|
||||
This buffer sequence is guaranteed to have length 1.
|
||||
*/
|
||||
using mutable_data_type = boost::asio::mutable_buffers_1;
|
||||
|
||||
/** The type used to represent the output sequence as a list of buffers.
|
||||
|
||||
This buffer sequence is guaranteed to have length 1.
|
||||
*/
|
||||
using mutable_buffers_type = boost::asio::mutable_buffers_1;
|
||||
|
||||
/** Constructor
|
||||
|
||||
This creates a dynamic buffer using the provided storage area.
|
||||
|
||||
@param p A pointer to valid storage of at least `n` bytes.
|
||||
|
||||
@param n The number of valid bytes pointed to by `p`.
|
||||
*/
|
||||
flat_static_buffer_base(void* p, std::size_t n)
|
||||
{
|
||||
reset_impl(p, n);
|
||||
}
|
||||
|
||||
/// Return the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return out_ - in_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return dist(begin_, end_);
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return max_size();
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/** Get a list of mutable buffers that represent the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
mutable_data_type
|
||||
mutable_data();
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if the size would exceed the limit
|
||||
imposed by the underlying mutable buffer sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
out_ += std::min<std::size_t>(n, last_ - out_);
|
||||
}
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(std::size_t n)
|
||||
{
|
||||
consume_impl(n);
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Constructor
|
||||
|
||||
The buffer will be in an undefined state. It is necessary
|
||||
for the derived class to call @ref reset in order to
|
||||
initialize the object.
|
||||
*/
|
||||
flat_static_buffer_base();
|
||||
|
||||
/** Reset the pointed-to buffer.
|
||||
|
||||
This function resets the internal state to the buffer provided.
|
||||
All input and output sequences are invalidated. This function
|
||||
allows the derived class to construct its members before
|
||||
initializing the static buffer.
|
||||
|
||||
@param p A pointer to valid storage of at least `n` bytes.
|
||||
|
||||
@param n The number of valid bytes pointed to by `p`.
|
||||
*/
|
||||
void
|
||||
reset(void* p, std::size_t n);
|
||||
|
||||
private:
|
||||
static
|
||||
inline
|
||||
std::size_t
|
||||
dist(char const* first, char const* last)
|
||||
{
|
||||
return static_cast<std::size_t>(last - first);
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
reset_impl(void* p, std::size_t n);
|
||||
|
||||
template<class = void>
|
||||
mutable_buffers_type
|
||||
prepare_impl(std::size_t n);
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
consume_impl(std::size_t n);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A @b DynamicBuffer with a fixed size internal buffer.
|
||||
|
||||
Buffer sequences returned by @ref data and @ref prepare
|
||||
will always be of length one.
|
||||
This implements a dynamic buffer using no memory allocations.
|
||||
|
||||
@tparam N The number of bytes in the internal buffer.
|
||||
|
||||
@note To reduce the number of template instantiations when passing
|
||||
objects of this type in a deduced context, the signature of the
|
||||
receiving function should use @ref flat_static_buffer_base instead.
|
||||
|
||||
@see @ref flat_static_buffer_base
|
||||
*/
|
||||
template<std::size_t N>
|
||||
class flat_static_buffer : public flat_static_buffer_base
|
||||
{
|
||||
char buf_[N];
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
flat_static_buffer(flat_static_buffer const&);
|
||||
|
||||
/// Constructor
|
||||
flat_static_buffer()
|
||||
: flat_static_buffer_base(buf_, N)
|
||||
{
|
||||
}
|
||||
|
||||
/// Assignment
|
||||
flat_static_buffer& operator=(flat_static_buffer const&);
|
||||
|
||||
/// Returns the @ref flat_static_buffer_base portion of this object
|
||||
flat_static_buffer_base&
|
||||
base()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns the @ref flat_static_buffer_base portion of this object
|
||||
flat_static_buffer_base const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/flat_static_buffer.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HANDLER_ALLOC_HPP
|
||||
#define BOOST_BEAST_HANDLER_ALLOC_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
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 BOOST_BEAST_DOXYGEN
|
||||
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;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using const_pointer = T const*;
|
||||
using const_reference = T const&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
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(size_type n)
|
||||
{
|
||||
auto const size = n * sizeof(T);
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return static_cast<value_type*>(
|
||||
asio_handler_allocate(size, std::addressof(h_)));
|
||||
}
|
||||
|
||||
void
|
||||
deallocate(value_type* p, size_type n)
|
||||
{
|
||||
auto const size = n * sizeof(T);
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(p, size, std::addressof(h_));
|
||||
}
|
||||
|
||||
//#if BOOST_WORKAROUND(BOOST_GCC, < 60000) // Works, but too coarse
|
||||
|
||||
#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
|
||||
template<class U, class... Args>
|
||||
void
|
||||
construct(U* ptr, Args&&... args)
|
||||
{
|
||||
::new((void*)ptr) U(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
void
|
||||
destroy(U* ptr)
|
||||
{
|
||||
ptr->~U();
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class U>
|
||||
friend
|
||||
bool
|
||||
operator==(
|
||||
handler_alloc const&,
|
||||
handler_alloc<U, Handler> const&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
friend
|
||||
bool
|
||||
operator!=(
|
||||
handler_alloc const& lhs,
|
||||
handler_alloc<U, Handler> const& rhs)
|
||||
{
|
||||
return ! (lhs == rhs);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,213 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HANDLER_PTR_HPP
|
||||
#define BOOST_BEAST_HANDLER_PTR_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A smart pointer container with associated completion handler.
|
||||
|
||||
This is a smart pointer that retains shared ownership of an
|
||||
object through a pointer. Memory is managed using the allocation
|
||||
and deallocation functions associated with a completion handler,
|
||||
which is also stored in the object. The managed object is
|
||||
destroyed and its memory deallocated when one of the following
|
||||
happens:
|
||||
|
||||
@li The function @ref invoke is called.
|
||||
|
||||
@li The function @ref release_handler is called.
|
||||
|
||||
@li The last remaining container owning the object is destroyed.
|
||||
|
||||
Objects of this type are used in the implementation of
|
||||
composed operations. Typically the composed operation's shared
|
||||
state is managed by the @ref handler_ptr and an allocator
|
||||
associated with the final handler is used to create the managed
|
||||
object.
|
||||
|
||||
@note The reference count is stored using a 16 bit unsigned
|
||||
integer. Making more than 2^16 copies of one object results
|
||||
in undefined behavior.
|
||||
|
||||
@tparam T The type of the owned object.
|
||||
|
||||
@tparam Handler The type of the completion handler.
|
||||
*/
|
||||
template<class T, class Handler>
|
||||
class handler_ptr
|
||||
{
|
||||
struct P
|
||||
{
|
||||
T* t;
|
||||
std::atomic<std::uint16_t> n;
|
||||
|
||||
// There's no way to put the handler anywhere else
|
||||
// without exposing ourselves to race conditions
|
||||
// and all sorts of ugliness.
|
||||
// See:
|
||||
// https://github.com/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.
|
||||
|
||||
@note Care must be taken when the arguments are themselves
|
||||
stored in the owned object. Such arguments must first be
|
||||
moved to the stack or elsewhere, and then passed, or else
|
||||
undefined behavior will result.
|
||||
*/
|
||||
template<class... Args>
|
||||
void
|
||||
invoke(Args&&... args);
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/handler_ptr.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,495 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_BUFFER_CAT_IPP
|
||||
#define BOOST_BEAST_IMPL_BUFFER_CAT_IPP
|
||||
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
template<class... Bn>
|
||||
class buffer_cat_view<Bn...>::const_iterator
|
||||
{
|
||||
std::size_t n_;
|
||||
std::tuple<Bn...> const* bn_;
|
||||
std::array<char, detail::max_sizeof<
|
||||
typename Bn::const_iterator...>()> buf_;
|
||||
|
||||
friend class buffer_cat_view<Bn...>;
|
||||
|
||||
template<std::size_t I>
|
||||
using C = std::integral_constant<std::size_t, I>;
|
||||
|
||||
template<std::size_t I>
|
||||
using iter_t = typename 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
|
||||
detail::common_buffers_type<Bn...>::type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
~const_iterator();
|
||||
const_iterator();
|
||||
const_iterator(const_iterator&& other);
|
||||
const_iterator(const_iterator const& other);
|
||||
const_iterator& operator=(const_iterator&& other);
|
||||
const_iterator& operator=(const_iterator const& other);
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const;
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const;
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++();
|
||||
|
||||
const_iterator
|
||||
operator++(int);
|
||||
|
||||
const_iterator&
|
||||
operator--();
|
||||
|
||||
const_iterator
|
||||
operator--(int);
|
||||
|
||||
private:
|
||||
const_iterator(
|
||||
std::tuple<Bn...> const& bn, bool at_end);
|
||||
|
||||
void
|
||||
construct(C<sizeof...(Bn)> const&)
|
||||
{
|
||||
auto constexpr I = sizeof...(Bn);
|
||||
n_ = I;
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
construct(C<I> const&)
|
||||
{
|
||||
if(boost::asio::buffer_size(
|
||||
std::get<I>(*bn_)) != 0)
|
||||
{
|
||||
n_ = I;
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
std::get<I>(*bn_).begin()};
|
||||
return;
|
||||
}
|
||||
construct(C<I+1>{});
|
||||
}
|
||||
|
||||
void
|
||||
rconstruct(C<0> const&)
|
||||
{
|
||||
auto constexpr I = 0;
|
||||
if(boost::asio::buffer_size(
|
||||
std::get<I>(*bn_)) != 0)
|
||||
{
|
||||
n_ = I;
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
std::get<I>(*bn_).end()};
|
||||
return;
|
||||
}
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid iterator"});
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
rconstruct(C<I> const&)
|
||||
{
|
||||
if(boost::asio::buffer_size(
|
||||
std::get<I>(*bn_)) != 0)
|
||||
{
|
||||
n_ = I;
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
std::get<I>(*bn_).end()};
|
||||
return;
|
||||
}
|
||||
rconstruct(C<I-1>{});
|
||||
}
|
||||
|
||||
void
|
||||
destroy(C<sizeof...(Bn)> const&)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
destroy(C<I> const&)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
using Iter = iter_t<I>;
|
||||
iter<I>().~Iter();
|
||||
return;
|
||||
}
|
||||
destroy(C<I+1>{});
|
||||
}
|
||||
|
||||
void
|
||||
move(const_iterator&&,
|
||||
C<sizeof...(Bn)> const&)
|
||||
{
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
move(const_iterator&& other,
|
||||
C<I> const&)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
std::move(other.iter<I>())};
|
||||
return;
|
||||
}
|
||||
move(std::move(other), C<I+1>{});
|
||||
}
|
||||
|
||||
void
|
||||
copy(const_iterator const&,
|
||||
C<sizeof...(Bn)> const&)
|
||||
{
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
copy(const_iterator const& other,
|
||||
C<I> const&)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
other.iter<I>()};
|
||||
return;
|
||||
}
|
||||
copy(other, C<I+1>{});
|
||||
}
|
||||
|
||||
bool
|
||||
equal(const_iterator const&,
|
||||
C<sizeof...(Bn)> const&) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
bool
|
||||
equal(const_iterator const& other,
|
||||
C<I> const&) const
|
||||
{
|
||||
if(n_ == I)
|
||||
return iter<I>() == other.iter<I>();
|
||||
return equal(other, C<I+1>{});
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
reference
|
||||
dereference(C<sizeof...(Bn)> const&) const
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid iterator"});
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
reference
|
||||
dereference(C<I> const&) const
|
||||
{
|
||||
if(n_ == I)
|
||||
return *iter<I>();
|
||||
return dereference(C<I+1>{});
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void
|
||||
increment(C<sizeof...(Bn)> const&)
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid iterator"});
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
increment(C<I> const&)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
if(++iter<I>() !=
|
||||
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)
|
||||
rconstruct(C<I-1>{});
|
||||
decrement(C<I-1>{});
|
||||
}
|
||||
|
||||
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();
|
||||
rconstruct(C<I-1>{});
|
||||
}
|
||||
decrement(C<I-1>{});
|
||||
}
|
||||
|
||||
void
|
||||
decrement(C<0> const&)
|
||||
{
|
||||
auto constexpr I = 0;
|
||||
if(iter<I>() != std::get<I>(*bn_).begin())
|
||||
{
|
||||
--iter<I>();
|
||||
return;
|
||||
}
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid iterator"});
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::~const_iterator()
|
||||
{
|
||||
destroy(C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::const_iterator()
|
||||
: n_(sizeof...(Bn))
|
||||
, bn_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::const_iterator(
|
||||
std::tuple<Bn...> const& bn, bool at_end)
|
||||
: bn_(&bn)
|
||||
{
|
||||
if(at_end)
|
||||
n_ = sizeof...(Bn);
|
||||
else
|
||||
construct(C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::const_iterator(const_iterator&& other)
|
||||
: n_(other.n_)
|
||||
, bn_(other.bn_)
|
||||
{
|
||||
move(std::move(other), C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::const_iterator(const_iterator const& other)
|
||||
: n_(other.n_)
|
||||
, bn_(other.bn_)
|
||||
{
|
||||
copy(other, C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator=(const_iterator&& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
destroy(C<0>{});
|
||||
n_ = other.n_;
|
||||
bn_ = other.bn_;
|
||||
// VFALCO What about exceptions?
|
||||
move(std::move(other), C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator=(const_iterator const& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
destroy(C<0>{});
|
||||
n_ = other.n_;
|
||||
bn_ = other.bn_;
|
||||
// VFALCO What about exceptions?
|
||||
copy(other, C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
bool
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator==(const_iterator const& other) const
|
||||
{
|
||||
if(bn_ != other.bn_)
|
||||
return false;
|
||||
if(n_ != other.n_)
|
||||
return false;
|
||||
return equal(other, C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator*() const ->
|
||||
reference
|
||||
{
|
||||
return dereference(C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator++() ->
|
||||
const_iterator&
|
||||
{
|
||||
increment(C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator++(int) ->
|
||||
const_iterator
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator--() ->
|
||||
const_iterator&
|
||||
{
|
||||
decrement(C<sizeof...(Bn)>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator--(int) ->
|
||||
const_iterator
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_view<Bn...>::
|
||||
buffer_cat_view(Bn const&... bn)
|
||||
: bn_(bn...)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<class... Bn>
|
||||
inline
|
||||
auto
|
||||
buffer_cat_view<Bn...>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{bn_, false};
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
inline
|
||||
auto
|
||||
buffer_cat_view<Bn...>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{bn_, true};
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_BUFFER_PREFIX_IPP
|
||||
#define BOOST_BEAST_IMPL_BUFFER_PREFIX_IPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
boost::asio::const_buffer
|
||||
buffer_prefix(std::size_t size,
|
||||
boost::asio::const_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return {buffer_cast<void const*>(buffer),
|
||||
(std::min)(size, buffer_size(buffer))};
|
||||
}
|
||||
|
||||
inline
|
||||
boost::asio::mutable_buffer
|
||||
buffer_prefix(std::size_t size,
|
||||
boost::asio::mutable_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return {buffer_cast<void*>(buffer),
|
||||
(std::min)(size, buffer_size(buffer))};
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class BufferSequence>
|
||||
class buffer_prefix_view<BufferSequence>::const_iterator
|
||||
{
|
||||
friend class buffer_prefix_view<BufferSequence>;
|
||||
|
||||
buffer_prefix_view const* b_ = nullptr;
|
||||
std::size_t remain_;
|
||||
iter_type it_;
|
||||
|
||||
public:
|
||||
using value_type = typename std::conditional<
|
||||
std::is_convertible<typename
|
||||
std::iterator_traits<iter_type>::value_type,
|
||||
boost::asio::mutable_buffer>::value,
|
||||
boost::asio::mutable_buffer,
|
||||
boost::asio::const_buffer>::type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return b_ == other.b_ && it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return detail::buffer_prefix(remain_, *it_);
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
remain_ -= boost::asio::buffer_size(*it_++);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
remain_ -= boost::asio::buffer_size(*it_++);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
remain_ += boost::asio::buffer_size(*--it_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
remain_ += boost::asio::buffer_size(*--it_);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
const_iterator(buffer_prefix_view const& b,
|
||||
std::true_type)
|
||||
: b_(&b)
|
||||
, remain_(0)
|
||||
, it_(b_->end_)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator(buffer_prefix_view const& b,
|
||||
std::false_type)
|
||||
: b_(&b)
|
||||
, remain_(b_->size_)
|
||||
, it_(b_->bs_.begin())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class BufferSequence>
|
||||
void
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
setup(std::size_t size)
|
||||
{
|
||||
size_ = 0;
|
||||
end_ = bs_.begin();
|
||||
auto const last = bs_.end();
|
||||
while(end_ != last)
|
||||
{
|
||||
auto const len =
|
||||
boost::asio::buffer_size(*end_++);
|
||||
if(len >= size)
|
||||
{
|
||||
size_ += size;
|
||||
break;
|
||||
}
|
||||
size -= len;
|
||||
size_ += len;
|
||||
}
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
buffer_prefix_view(buffer_prefix_view&& other)
|
||||
: buffer_prefix_view(std::move(other),
|
||||
std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
buffer_prefix_view(buffer_prefix_view const& other)
|
||||
: buffer_prefix_view(other,
|
||||
std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
operator=(buffer_prefix_view&& other) ->
|
||||
buffer_prefix_view&
|
||||
{
|
||||
auto const dist = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_);
|
||||
bs_ = std::move(other.bs_);
|
||||
size_ = other.size_;
|
||||
end_ = std::next(bs_.begin(), dist);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
operator=(buffer_prefix_view const& other) ->
|
||||
buffer_prefix_view&
|
||||
{
|
||||
auto const dist = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_);
|
||||
bs_ = other.bs_;
|
||||
size_ = other.size_;
|
||||
end_ = std::next(bs_.begin(), dist);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
buffer_prefix_view(std::size_t size,
|
||||
BufferSequence const& bs)
|
||||
: bs_(bs)
|
||||
{
|
||||
setup(size);
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
template<class... Args>
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
buffer_prefix_view(std::size_t size,
|
||||
boost::in_place_init_t, Args&&... args)
|
||||
: bs_(std::forward<Args>(args)...)
|
||||
{
|
||||
setup(size);
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
auto
|
||||
buffer_prefix_view<BufferSequence>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, std::false_type{}};
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
auto
|
||||
buffer_prefix_view<BufferSequence>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, std::true_type{}};
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,245 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_IPP
|
||||
#define BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_IPP
|
||||
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence, class Handler>
|
||||
class buffered_read_stream<
|
||||
Stream, DynamicBuffer>::read_some_op
|
||||
{
|
||||
int step_ = 0;
|
||||
buffered_read_stream& s_;
|
||||
MutableBufferSequence b_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_some_op(read_some_op&&) = default;
|
||||
read_some_op(read_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_some_op(DeducedHandler&& h,
|
||||
buffered_read_stream& s,
|
||||
MutableBufferSequence const& b)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence, class Handler>
|
||||
void
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
read_some_op<MutableBufferSequence, Handler>::operator()(
|
||||
error_code const& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
switch(step_)
|
||||
{
|
||||
case 0:
|
||||
if(s_.sb_.size() == 0)
|
||||
{
|
||||
if(s_.capacity_ == 0)
|
||||
{
|
||||
// read (unbuffered)
|
||||
step_ = 1;
|
||||
return s_.next_layer_.async_read_some(
|
||||
b_, std::move(*this));
|
||||
}
|
||||
|
||||
// read
|
||||
step_ = 2;
|
||||
return s_.next_layer_.async_read_some(
|
||||
s_.sb_.prepare(s_.capacity_),
|
||||
std::move(*this));
|
||||
|
||||
}
|
||||
step_ = 3;
|
||||
s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// upcall
|
||||
break;
|
||||
|
||||
case 2:
|
||||
s_.sb_.commit(bytes_transferred);
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case 3:
|
||||
bytes_transferred =
|
||||
boost::asio::buffer_copy(b_, s_.sb_.data());
|
||||
s_.sb_.consume(bytes_transferred);
|
||||
break;
|
||||
}
|
||||
h_(ec, bytes_transferred);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class... Args>
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
buffered_read_stream(Args&&... args)
|
||||
: next_layer_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
auto
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler) ->
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
{
|
||||
static_assert(is_async_write_stream<next_layer_type>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(is_completion_handler<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler requirements not met");
|
||||
return next_layer_.async_write_some(buffers,
|
||||
std::forward<WriteHandler>(handler));
|
||||
}
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
read_some(
|
||||
MutableBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_sync_read_stream<next_layer_type>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_mutable_buffer_sequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
auto n = read_some(buffers, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
read_some(MutableBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<next_layer_type>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_mutable_buffer_sequence<
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
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
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
async_read_some(MutableBufferSequence const& buffers,
|
||||
ReadHandler&& handler) ->
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
{
|
||||
static_assert(is_async_read_stream<next_layer_type>::value,
|
||||
"Stream requirements not met");
|
||||
static_assert(is_mutable_buffer_sequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> init{handler};
|
||||
read_some_op<MutableBufferSequence, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, *this, buffers}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,509 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_BUFFERS_ADAPTER_IPP
|
||||
#define BOOST_BEAST_IMPL_BUFFERS_ADAPTER_IPP
|
||||
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
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)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
return mutable_buffers_type{*this};
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
void
|
||||
buffers_adapter<MutableBufferSequence>::commit(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
if(out_ == end_)
|
||||
return;
|
||||
auto const last = std::prev(end_);
|
||||
while(out_ != last)
|
||||
{
|
||||
auto const avail =
|
||||
buffer_size(*out_) - out_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
out_pos_ += n;
|
||||
in_size_ += n;
|
||||
max_size_ -= n;
|
||||
return;
|
||||
}
|
||||
++out_;
|
||||
n -= avail;
|
||||
out_pos_ = 0;
|
||||
in_size_ += avail;
|
||||
max_size_ -= avail;
|
||||
}
|
||||
|
||||
n = (std::min)(n, out_end_ - out_pos_);
|
||||
out_pos_ += n;
|
||||
in_size_ += n;
|
||||
max_size_ -= n;
|
||||
if(out_pos_ == buffer_size(*out_))
|
||||
{
|
||||
++out_;
|
||||
out_pos_ = 0;
|
||||
out_end_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
inline
|
||||
auto
|
||||
buffers_adapter<MutableBufferSequence>::data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type{*this};
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
void
|
||||
buffers_adapter<MutableBufferSequence>::consume(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
while(begin_ != out_)
|
||||
{
|
||||
auto const avail =
|
||||
buffer_size(*begin_) - in_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
in_size_ -= n;
|
||||
in_pos_ += n;
|
||||
return;
|
||||
}
|
||||
n -= avail;
|
||||
in_size_ -= avail;
|
||||
in_pos_ = 0;
|
||||
++begin_;
|
||||
}
|
||||
auto const avail = out_pos_ - in_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
in_size_ -= n;
|
||||
in_pos_ += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_size_ -= avail;
|
||||
in_pos_ = out_pos_;
|
||||
}
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,239 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_CONSUMING_BUFFERS_IPP
|
||||
#define BOOST_BEAST_IMPL_CONSUMING_BUFFERS_IPP
|
||||
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
template<class Buffers>
|
||||
class consuming_buffers<Buffers>::const_iterator
|
||||
{
|
||||
friend class consuming_buffers<Buffers>;
|
||||
|
||||
using iter_type =
|
||||
typename Buffers::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 Buffers>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers()
|
||||
: begin_(bs_.begin())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers(consuming_buffers&& other)
|
||||
: consuming_buffers(std::move(other),
|
||||
std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers(consuming_buffers const& other)
|
||||
: consuming_buffers(other,
|
||||
std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers(Buffers const& bs)
|
||||
: bs_(bs)
|
||||
, begin_(bs_.begin())
|
||||
{
|
||||
static_assert(
|
||||
is_const_buffer_sequence<Buffers>::value||
|
||||
is_mutable_buffer_sequence<Buffers>::value,
|
||||
"BufferSequence requirements not met");
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
template<class... Args>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers(boost::in_place_init_t, Args&&... args)
|
||||
: bs_(std::forward<Args>(args)...)
|
||||
, begin_(bs_.begin())
|
||||
{
|
||||
static_assert(sizeof...(Args) > 0,
|
||||
"Missing constructor arguments");
|
||||
static_assert(
|
||||
std::is_constructible<Buffers, Args...>::value,
|
||||
"Buffers not constructible from arguments");
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
auto
|
||||
consuming_buffers<Buffers>::
|
||||
operator=(consuming_buffers&& other) ->
|
||||
consuming_buffers&
|
||||
{
|
||||
auto const dist = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_);
|
||||
bs_ = std::move(other.bs_);
|
||||
begin_ = std::next(bs_.begin(), dist);
|
||||
skip_ = other.skip_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
auto
|
||||
consuming_buffers<Buffers>::
|
||||
operator=(consuming_buffers const& other) ->
|
||||
consuming_buffers&
|
||||
{
|
||||
auto const dist = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_);
|
||||
bs_ = other.bs_;
|
||||
begin_ = std::next(bs_.begin(), dist);
|
||||
skip_ = other.skip_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
inline
|
||||
auto
|
||||
consuming_buffers<Buffers>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, begin_};
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
inline
|
||||
auto
|
||||
consuming_buffers<Buffers>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, bs_.end()};
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
void
|
||||
consuming_buffers<Buffers>::
|
||||
consume(std::size_t amount)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
for(;amount > 0 && begin_ != bs_.end(); ++begin_)
|
||||
{
|
||||
auto const len =
|
||||
buffer_size(*begin_) - skip_;
|
||||
if(amount < len)
|
||||
{
|
||||
skip_ += amount;
|
||||
break;
|
||||
}
|
||||
amount -= len;
|
||||
skip_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,335 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP
|
||||
#define BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP
|
||||
|
||||
#include <limits>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
int
|
||||
file_posix_close(int fd)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(! ::close(fd))
|
||||
break;
|
||||
int const ev = errno;
|
||||
if(errno != EINTR)
|
||||
return ev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
file_posix::
|
||||
~file_posix()
|
||||
{
|
||||
if(fd_ != -1)
|
||||
detail::file_posix_close(fd_);
|
||||
}
|
||||
|
||||
inline
|
||||
file_posix::
|
||||
file_posix(file_posix&& other)
|
||||
: fd_(other.fd_)
|
||||
{
|
||||
other.fd_ = -1;
|
||||
}
|
||||
|
||||
inline
|
||||
file_posix&
|
||||
file_posix::
|
||||
operator=(file_posix&& other)
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
if(fd_ != -1)
|
||||
detail::file_posix_close(fd_);
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_posix::
|
||||
native_handle(native_handle_type fd)
|
||||
{
|
||||
if(fd_ != -1)
|
||||
detail::file_posix_close(fd_);
|
||||
fd_ = fd;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_posix::
|
||||
close(error_code& ec)
|
||||
{
|
||||
if(fd_ != -1)
|
||||
{
|
||||
auto const ev =
|
||||
detail::file_posix_close(fd_);
|
||||
if(ev)
|
||||
ec.assign(ev, generic_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
fd_ = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_posix::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
if(fd_ != -1)
|
||||
{
|
||||
auto const ev =
|
||||
detail::file_posix_close(fd_);
|
||||
if(ev)
|
||||
ec.assign(ev, generic_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
fd_ = -1;
|
||||
}
|
||||
int f = 0;
|
||||
#ifndef __APPLE__
|
||||
int advise = 0;
|
||||
#endif
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
case file_mode::read:
|
||||
f = O_RDONLY;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_RANDOM;
|
||||
#endif
|
||||
break;
|
||||
case file_mode::scan:
|
||||
f = O_RDONLY;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_SEQUENTIAL;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::write:
|
||||
f = O_RDWR | O_CREAT | O_TRUNC;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_RANDOM;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::write_new:
|
||||
f = O_RDWR | O_CREAT | O_EXCL;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_RANDOM;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::write_existing:
|
||||
f = O_RDWR | O_EXCL;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_RANDOM;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::append:
|
||||
f = O_RDWR | O_CREAT | O_TRUNC;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_SEQUENTIAL;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::append_new:
|
||||
f = O_RDWR | O_CREAT | O_EXCL;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_SEQUENTIAL;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::append_existing:
|
||||
f = O_RDWR | O_EXCL;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_SEQUENTIAL;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
fd_ = ::open(path, f, 0644);
|
||||
if(fd_ != -1)
|
||||
break;
|
||||
auto const ev = errno;
|
||||
if(ev != EINTR)
|
||||
{
|
||||
ec.assign(ev, generic_category());
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifndef __APPLE__
|
||||
if(::posix_fadvise(fd_, 0, 0, advise))
|
||||
{
|
||||
auto const ev = errno;
|
||||
detail::file_posix_close(fd_);
|
||||
fd_ = -1;
|
||||
ec.assign(ev, generic_category());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_posix::
|
||||
size(error_code& ec) const
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
struct stat st;
|
||||
if(::fstat(fd_, &st) != 0)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_posix::
|
||||
pos(error_code& ec) const
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
auto const result = ::lseek(fd_, 0, SEEK_CUR);
|
||||
if(result == (off_t)-1)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return result;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_posix::
|
||||
seek(std::uint64_t offset, error_code& ec)
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return;
|
||||
}
|
||||
auto const result = ::lseek(fd_, offset, SEEK_SET);
|
||||
if(result == static_cast<off_t>(-1))
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_posix::
|
||||
read(void* buffer, std::size_t n, error_code& ec) const
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
std::size_t nread = 0;
|
||||
while(n > 0)
|
||||
{
|
||||
auto const amount = static_cast<ssize_t>((std::min)(
|
||||
n, static_cast<std::size_t>(SSIZE_MAX)));
|
||||
auto const result = ::read(fd_, buffer, amount);
|
||||
if(result == -1)
|
||||
{
|
||||
auto const ev = errno;
|
||||
if(ev == EINTR)
|
||||
continue;
|
||||
ec.assign(ev, generic_category());
|
||||
return nread;
|
||||
}
|
||||
if(result == 0)
|
||||
{
|
||||
// short read
|
||||
return nread;
|
||||
}
|
||||
n -= result;
|
||||
nread += result;
|
||||
buffer = reinterpret_cast<char*>(buffer) + result;
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_posix::
|
||||
write(void const* buffer, std::size_t n, error_code& ec)
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
std::size_t nwritten = 0;
|
||||
while(n > 0)
|
||||
{
|
||||
auto const amount = static_cast<ssize_t>((std::min)(
|
||||
n, static_cast<std::size_t>(SSIZE_MAX)));
|
||||
auto const result = ::write(fd_, buffer, amount);
|
||||
if(result == -1)
|
||||
{
|
||||
auto const ev = errno;
|
||||
if(ev == EINTR)
|
||||
continue;
|
||||
ec.assign(ev, generic_category());
|
||||
return nwritten;
|
||||
}
|
||||
n -= result;
|
||||
nwritten += result;
|
||||
buffer = reinterpret_cast<char const*>(buffer) + result;
|
||||
}
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,229 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP
|
||||
#define BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
inline
|
||||
file_stdio::
|
||||
~file_stdio()
|
||||
{
|
||||
if(f_)
|
||||
fclose(f_);
|
||||
}
|
||||
|
||||
inline
|
||||
file_stdio::
|
||||
file_stdio(file_stdio&& other)
|
||||
: f_(other.f_)
|
||||
{
|
||||
other.f_ = nullptr;
|
||||
}
|
||||
|
||||
inline
|
||||
file_stdio&
|
||||
file_stdio::
|
||||
operator=(file_stdio&& other)
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
if(f_)
|
||||
fclose(f_);
|
||||
f_ = other.f_;
|
||||
other.f_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_stdio::
|
||||
native_handle(FILE* f)
|
||||
{
|
||||
if(f_)
|
||||
fclose(f_);
|
||||
f_ = f;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_stdio::
|
||||
close(error_code& ec)
|
||||
{
|
||||
if(f_)
|
||||
{
|
||||
int failed = fclose(f_);
|
||||
f_ = nullptr;
|
||||
if(failed)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return;
|
||||
}
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_stdio::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
if(f_)
|
||||
{
|
||||
fclose(f_);
|
||||
f_ = nullptr;
|
||||
}
|
||||
char const* s;
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
case file_mode::read: s = "rb"; break;
|
||||
case file_mode::scan: s = "rb"; break;
|
||||
case file_mode::write: s = "wb"; break;
|
||||
case file_mode::write_new: s = "wbx"; break;
|
||||
case file_mode::write_existing: s = "wb"; break;
|
||||
case file_mode::append: s = "ab"; break;
|
||||
case file_mode::append_new: s = "abx"; break;
|
||||
case file_mode::append_existing: s = "ab"; break;
|
||||
}
|
||||
f_ = std::fopen(path, s);
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_stdio::
|
||||
size(error_code& ec) const
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
long pos = std::ftell(f_);
|
||||
if(pos == -1L)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
int result = std::fseek(f_, 0, SEEK_END);
|
||||
if(result != 0)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
long size = std::ftell(f_);
|
||||
if(size == -1L)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
std::fseek(f_, pos, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
result = std::fseek(f_, pos, SEEK_SET);
|
||||
if(result != 0)
|
||||
ec.assign(errno, generic_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
return size;
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_stdio::
|
||||
pos(error_code& ec) const
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
long pos = std::ftell(f_);
|
||||
if(pos == -1L)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return pos;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_stdio::
|
||||
seek(std::uint64_t offset, error_code& ec)
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return;
|
||||
}
|
||||
if(offset > (std::numeric_limits<long>::max)())
|
||||
{
|
||||
ec = make_error_code(errc::invalid_seek);
|
||||
return;
|
||||
}
|
||||
int result = std::fseek(f_,
|
||||
static_cast<long>(offset), SEEK_SET);
|
||||
if(result != 0)
|
||||
ec.assign(errno, generic_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_stdio::
|
||||
read(void* buffer, std::size_t n, error_code& ec) const
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
auto nread = std::fread(buffer, 1, n, f_);
|
||||
if(std::ferror(f_))
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_stdio::
|
||||
write(void const* buffer, std::size_t n, error_code& ec)
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
auto nwritten = std::fwrite(buffer, 1, n, f_);
|
||||
if(std::ferror(f_))
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,360 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP
|
||||
#define BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP
|
||||
|
||||
#include <boost/detail/winapi/access_rights.hpp>
|
||||
#include <boost/detail/winapi/error_codes.hpp>
|
||||
#include <boost/detail/winapi/file_management.hpp>
|
||||
#include <boost/detail/winapi/get_last_error.hpp>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// VFALCO Can't seem to get boost/detail/winapi to work with
|
||||
// this so use the non-Ex version for now.
|
||||
inline
|
||||
boost::detail::winapi::BOOL_
|
||||
set_file_pointer_ex(
|
||||
boost::detail::winapi::HANDLE_ hFile,
|
||||
boost::detail::winapi::LARGE_INTEGER_ lpDistanceToMove,
|
||||
boost::detail::winapi::PLARGE_INTEGER_ lpNewFilePointer,
|
||||
boost::detail::winapi::DWORD_ dwMoveMethod)
|
||||
{
|
||||
auto dwHighPart = lpDistanceToMove.u.HighPart;
|
||||
auto dwLowPart = boost::detail::winapi::SetFilePointer(
|
||||
hFile,
|
||||
lpDistanceToMove.u.LowPart,
|
||||
&dwHighPart,
|
||||
dwMoveMethod);
|
||||
if(dwLowPart == boost::detail::winapi::INVALID_SET_FILE_POINTER_)
|
||||
return 0;
|
||||
if(lpNewFilePointer)
|
||||
{
|
||||
lpNewFilePointer->u.LowPart = dwLowPart;
|
||||
lpNewFilePointer->u.HighPart = dwHighPart;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
file_win32::
|
||||
~file_win32()
|
||||
{
|
||||
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
boost::detail::winapi::CloseHandle(h_);
|
||||
}
|
||||
|
||||
inline
|
||||
file_win32::
|
||||
file_win32(file_win32&& other)
|
||||
: h_(other.h_)
|
||||
{
|
||||
other.h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
}
|
||||
|
||||
inline
|
||||
file_win32&
|
||||
file_win32::
|
||||
operator=(file_win32&& other)
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
if(h_)
|
||||
boost::detail::winapi::CloseHandle(h_);
|
||||
h_ = other.h_;
|
||||
other.h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_win32::
|
||||
native_handle(native_handle_type h)
|
||||
{
|
||||
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
boost::detail::winapi::CloseHandle(h_);
|
||||
h_ = h;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_win32::
|
||||
close(error_code& ec)
|
||||
{
|
||||
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
if(! boost::detail::winapi::CloseHandle(h_))
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_win32::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
boost::detail::winapi::CloseHandle(h_);
|
||||
h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
}
|
||||
boost::detail::winapi::DWORD_ dw1 = 0;
|
||||
boost::detail::winapi::DWORD_ dw2 = 0;
|
||||
boost::detail::winapi::DWORD_ dw3 = 0;
|
||||
/*
|
||||
| When the file...
|
||||
This argument: | Exists Does not exist
|
||||
-------------------------+------------------------------------------------------
|
||||
CREATE_ALWAYS | Truncates Creates
|
||||
CREATE_NEW +-----------+ Fails Creates
|
||||
OPEN_ALWAYS ===| does this |===> Opens Creates
|
||||
OPEN_EXISTING +-----------+ Opens Fails
|
||||
TRUNCATE_EXISTING | Truncates Fails
|
||||
*/
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
case file_mode::read:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_;
|
||||
dw2 = boost::detail::winapi::OPEN_EXISTING_;
|
||||
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
|
||||
break;
|
||||
|
||||
case file_mode::scan:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_;
|
||||
dw2 = boost::detail::winapi::OPEN_EXISTING_;
|
||||
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
break;
|
||||
|
||||
case file_mode::write:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::CREATE_ALWAYS_;
|
||||
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
|
||||
break;
|
||||
|
||||
case file_mode::write_new:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::CREATE_NEW_;
|
||||
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
|
||||
break;
|
||||
|
||||
case file_mode::write_existing:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::OPEN_EXISTING_;
|
||||
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
|
||||
break;
|
||||
|
||||
case file_mode::append:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::CREATE_ALWAYS_;
|
||||
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
break;
|
||||
|
||||
case file_mode::append_new:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::CREATE_NEW_;
|
||||
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
break;
|
||||
|
||||
case file_mode::append_existing:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::OPEN_EXISTING_;
|
||||
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
break;
|
||||
}
|
||||
h_ = ::CreateFileA(
|
||||
path,
|
||||
dw1,
|
||||
0,
|
||||
NULL,
|
||||
dw2,
|
||||
dw3,
|
||||
NULL);
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_win32::
|
||||
size(error_code& ec) const
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
boost::detail::winapi::LARGE_INTEGER_ fileSize;
|
||||
if(! boost::detail::winapi::GetFileSizeEx(h_, &fileSize))
|
||||
{
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return fileSize.QuadPart;
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_win32::
|
||||
pos(error_code& ec)
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
boost::detail::winapi::LARGE_INTEGER_ in;
|
||||
boost::detail::winapi::LARGE_INTEGER_ out;
|
||||
in.QuadPart = 0;
|
||||
if(! detail::set_file_pointer_ex(h_, in, &out,
|
||||
boost::detail::winapi::FILE_CURRENT_))
|
||||
{
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return out.QuadPart;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_win32::
|
||||
seek(std::uint64_t offset, error_code& ec)
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return;
|
||||
}
|
||||
boost::detail::winapi::LARGE_INTEGER_ in;
|
||||
in.QuadPart = offset;
|
||||
if(! detail::set_file_pointer_ex(h_, in, 0,
|
||||
boost::detail::winapi::FILE_BEGIN_))
|
||||
{
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_win32::
|
||||
read(void* buffer, std::size_t n, error_code& ec)
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
std::size_t nread = 0;
|
||||
while(n > 0)
|
||||
{
|
||||
boost::detail::winapi::DWORD_ amount;
|
||||
if(n > (std::numeric_limits<
|
||||
boost::detail::winapi::DWORD_>::max)())
|
||||
amount = (std::numeric_limits<
|
||||
boost::detail::winapi::DWORD_>::max)();
|
||||
else
|
||||
amount = static_cast<
|
||||
boost::detail::winapi::DWORD_>(n);
|
||||
boost::detail::winapi::DWORD_ bytesRead;
|
||||
if(! ::ReadFile(h_, buffer, amount, &bytesRead, 0))
|
||||
{
|
||||
auto const dwError = ::GetLastError();
|
||||
if(dwError != boost::detail::winapi::ERROR_HANDLE_EOF_)
|
||||
ec.assign(::GetLastError(), system_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
return nread;
|
||||
}
|
||||
if(bytesRead == 0)
|
||||
return nread;
|
||||
n -= bytesRead;
|
||||
nread += bytesRead;
|
||||
buffer = reinterpret_cast<char*>(buffer) + bytesRead;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return nread;
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_win32::
|
||||
write(void const* buffer, std::size_t n, error_code& ec)
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
std::size_t nwritten = 0;
|
||||
while(n > 0)
|
||||
{
|
||||
boost::detail::winapi::DWORD_ amount;
|
||||
if(n > (std::numeric_limits<
|
||||
boost::detail::winapi::DWORD_>::max)())
|
||||
amount = (std::numeric_limits<
|
||||
boost::detail::winapi::DWORD_>::max)();
|
||||
else
|
||||
amount = static_cast<
|
||||
boost::detail::winapi::DWORD_>(n);
|
||||
boost::detail::winapi::DWORD_ bytesWritten;
|
||||
if(! ::WriteFile(h_, buffer, amount, &bytesWritten, 0))
|
||||
{
|
||||
auto const dwError = ::GetLastError();
|
||||
if(dwError != boost::detail::winapi::ERROR_HANDLE_EOF_)
|
||||
ec.assign(::GetLastError(), system_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
return nwritten;
|
||||
}
|
||||
if(bytesWritten == 0)
|
||||
return nwritten;
|
||||
n -= bytesWritten;
|
||||
nwritten += bytesWritten;
|
||||
buffer = reinterpret_cast<char const*>(buffer) + bytesWritten;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,475 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_FLAT_BUFFER_HPP
|
||||
#define BOOST_BEAST_IMPL_FLAT_BUFFER_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/* Memory is laid out thusly:
|
||||
|
||||
begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
|
||||
*/
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
~basic_flat_buffer()
|
||||
{
|
||||
if(begin_)
|
||||
alloc_traits::deallocate(
|
||||
this->member(), begin_, dist(begin_, end_));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer()
|
||||
: begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_((std::numeric_limits<std::size_t>::max)())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(std::size_t limit)
|
||||
: begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(limit)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_((std::numeric_limits<std::size_t>::max)())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(std::size_t limit, Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(limit)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer&& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
std::move(other.member()))
|
||||
, begin_(other.begin_)
|
||||
, in_(other.in_)
|
||||
, out_(other.out_)
|
||||
, last_(out_)
|
||||
, end_(other.end_)
|
||||
, max_(other.max_)
|
||||
{
|
||||
other.begin_ = nullptr;
|
||||
other.in_ = nullptr;
|
||||
other.out_ = nullptr;
|
||||
other.last_ = nullptr;
|
||||
other.end_ = nullptr;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer&& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
{
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
begin_ = nullptr;
|
||||
in_ = nullptr;
|
||||
out_ = nullptr;
|
||||
last_ = nullptr;
|
||||
end_ = nullptr;
|
||||
max_ = other.max_;
|
||||
copy_from(other);
|
||||
other.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
begin_ = other.begin_;
|
||||
in_ = other.in_;
|
||||
out_ = other.out_;
|
||||
last_ = out_;
|
||||
end_ = other.end_;
|
||||
max_ = other.max_;
|
||||
other.begin_ = nullptr;
|
||||
other.in_ = nullptr;
|
||||
other.out_ = nullptr;
|
||||
other.last_ = nullptr;
|
||||
other.end_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer const& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
alloc_traits::select_on_container_copy_construction(
|
||||
other.member()))
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(other.max_)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer const& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(other.max_)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(
|
||||
basic_flat_buffer<OtherAlloc> const& other)
|
||||
: begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(other.max_)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer<OtherAlloc> const& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(other.max_)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_flat_buffer<Allocator>::
|
||||
operator=(basic_flat_buffer&& other) ->
|
||||
basic_flat_buffer&
|
||||
{
|
||||
if(this != &other)
|
||||
move_assign(other,
|
||||
typename alloc_traits::propagate_on_container_move_assignment{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_flat_buffer<Allocator>::
|
||||
operator=(basic_flat_buffer const& other) ->
|
||||
basic_flat_buffer&
|
||||
{
|
||||
if(this != &other)
|
||||
copy_assign(other,
|
||||
typename alloc_traits::propagate_on_container_copy_assignment{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_flat_buffer<Allocator>::
|
||||
operator=(basic_flat_buffer<OtherAlloc> const& other) ->
|
||||
basic_flat_buffer&
|
||||
{
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
copy_from(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_flat_buffer<Allocator>::
|
||||
prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(n <= dist(out_, end_))
|
||||
{
|
||||
// existing capacity is sufficient
|
||||
last_ = out_ + n;
|
||||
return{out_, n};
|
||||
}
|
||||
auto const len = size();
|
||||
if(n <= capacity() - len)
|
||||
{
|
||||
// after a memmove,
|
||||
// existing capacity is sufficient
|
||||
if(len > 0)
|
||||
std::memmove(begin_, in_, len);
|
||||
in_ = begin_;
|
||||
out_ = in_ + len;
|
||||
last_ = out_ + n;
|
||||
return {out_, n};
|
||||
}
|
||||
// enforce maximum capacity
|
||||
if(n > max_ - len)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"basic_flat_buffer overflow"});
|
||||
// allocate a new buffer
|
||||
auto const new_size = std::min<std::size_t>(
|
||||
max_,
|
||||
std::max<std::size_t>(2 * len, len + n));
|
||||
auto const p = alloc_traits::allocate(
|
||||
this->member(), new_size);
|
||||
if(begin_)
|
||||
{
|
||||
BOOST_ASSERT(p);
|
||||
BOOST_ASSERT(in_);
|
||||
std::memcpy(p, in_, len);
|
||||
alloc_traits::deallocate(
|
||||
this->member(), begin_, capacity());
|
||||
}
|
||||
begin_ = p;
|
||||
in_ = begin_;
|
||||
out_ = in_ + len;
|
||||
last_ = out_ + n;
|
||||
end_ = begin_ + new_size;
|
||||
return {out_, n};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
if(n >= dist(in_, out_))
|
||||
{
|
||||
in_ = begin_;
|
||||
out_ = begin_;
|
||||
return;
|
||||
}
|
||||
in_ += n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
shrink_to_fit()
|
||||
{
|
||||
auto const len = size();
|
||||
if(len == capacity())
|
||||
return;
|
||||
char* p;
|
||||
if(len > 0)
|
||||
{
|
||||
BOOST_ASSERT(begin_);
|
||||
BOOST_ASSERT(in_);
|
||||
p = alloc_traits::allocate(
|
||||
this->member(), len);
|
||||
std::memcpy(p, in_, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = nullptr;
|
||||
}
|
||||
alloc_traits::deallocate(
|
||||
this->member(), begin_, dist(begin_, end_));
|
||||
begin_ = p;
|
||||
in_ = begin_;
|
||||
out_ = begin_ + len;
|
||||
last_ = out_;
|
||||
end_ = out_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
reset()
|
||||
{
|
||||
consume(size());
|
||||
shrink_to_fit();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class DynamicBuffer>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
copy_from(DynamicBuffer const& buffer)
|
||||
{
|
||||
if(buffer.size() == 0)
|
||||
return;
|
||||
using boost::asio::buffer_copy;
|
||||
commit(buffer_copy(
|
||||
prepare(buffer.size()), buffer.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
move_assign(basic_flat_buffer& other, std::true_type)
|
||||
{
|
||||
reset();
|
||||
this->member() = std::move(other.member());
|
||||
begin_ = other.begin_;
|
||||
in_ = other.in_;
|
||||
out_ = other.out_;
|
||||
last_ = out_;
|
||||
end_ = other.end_;
|
||||
max_ = other.max_;
|
||||
other.begin_ = nullptr;
|
||||
other.in_ = nullptr;
|
||||
other.out_ = nullptr;
|
||||
other.last_ = nullptr;
|
||||
other.end_ = nullptr;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
move_assign(basic_flat_buffer& other, std::false_type)
|
||||
{
|
||||
reset();
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
copy_from(other);
|
||||
other.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
move_assign(other, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
copy_assign(basic_flat_buffer const& other, std::true_type)
|
||||
{
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
this->member() = other.member();
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
copy_assign(basic_flat_buffer const& other, std::false_type)
|
||||
{
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
swap(basic_flat_buffer& other)
|
||||
{
|
||||
swap(other, typename
|
||||
alloc_traits::propagate_on_container_swap{});
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
swap(basic_flat_buffer& other, std::true_type)
|
||||
{
|
||||
using std::swap;
|
||||
swap(this->member(), other.member());
|
||||
swap(max_, other.max_);
|
||||
swap(begin_, other.begin_);
|
||||
swap(in_, other.in_);
|
||||
swap(out_, other.out_);
|
||||
last_ = this->out_;
|
||||
other.last_ = other.out_;
|
||||
swap(end_, other.end_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
swap(basic_flat_buffer& other, std::false_type)
|
||||
{
|
||||
BOOST_ASSERT(this->member() == other.member());
|
||||
using std::swap;
|
||||
swap(max_, other.max_);
|
||||
swap(begin_, other.begin_);
|
||||
swap(in_, other.in_);
|
||||
swap(out_, other.out_);
|
||||
last_ = this->out_;
|
||||
other.last_ = other.out_;
|
||||
swap(end_, other.end_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
swap(
|
||||
basic_flat_buffer<Allocator>& lhs,
|
||||
basic_flat_buffer<Allocator>& rhs)
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP
|
||||
#define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP
|
||||
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/* Memory is laid out thusly:
|
||||
|
||||
begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
|
||||
*/
|
||||
|
||||
inline
|
||||
auto
|
||||
flat_static_buffer_base::
|
||||
data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return {in_, dist(in_, out_)};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
flat_static_buffer_base::
|
||||
mutable_data() ->
|
||||
mutable_data_type
|
||||
{
|
||||
return {in_, dist(in_, out_)};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
flat_static_buffer_base::
|
||||
prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
return prepare_impl(n);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
flat_static_buffer_base::
|
||||
reset(void* p, std::size_t n)
|
||||
{
|
||||
reset_impl(p, n);
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
flat_static_buffer_base::
|
||||
reset_impl(void* p, std::size_t n)
|
||||
{
|
||||
begin_ =
|
||||
reinterpret_cast<char*>(p);
|
||||
in_ = begin_;
|
||||
out_ = begin_;
|
||||
last_ = begin_;
|
||||
end_ = begin_ + n;
|
||||
}
|
||||
|
||||
template<class>
|
||||
auto
|
||||
flat_static_buffer_base::
|
||||
prepare_impl(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(n <= dist(out_, end_))
|
||||
{
|
||||
last_ = out_ + n;
|
||||
return {out_, n};
|
||||
}
|
||||
auto const len = size();
|
||||
if(n > capacity() - len)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
if(len > 0)
|
||||
std::memmove(begin_, in_, len);
|
||||
in_ = begin_;
|
||||
out_ = in_ + len;
|
||||
last_ = out_ + n;
|
||||
return {out_, n};
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
flat_static_buffer_base::
|
||||
consume_impl(std::size_t n)
|
||||
{
|
||||
if(n >= size())
|
||||
{
|
||||
in_ = begin_;
|
||||
out_ = in_;
|
||||
return;
|
||||
}
|
||||
in_ += n;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<std::size_t N>
|
||||
flat_static_buffer<N>::
|
||||
flat_static_buffer(flat_static_buffer const& other)
|
||||
: flat_static_buffer_base(buf_, N)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
this->commit(buffer_copy(
|
||||
this->prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
auto
|
||||
flat_static_buffer<N>::
|
||||
operator=(flat_static_buffer const& other) ->
|
||||
flat_static_buffer<N>&
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
this->consume(this->size());
|
||||
this->commit(buffer_copy(
|
||||
this->prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_HANDLER_PTR_HPP
|
||||
#define BOOST_BEAST_IMPL_HANDLER_PTR_HPP
|
||||
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
template<class T, class Handler>
|
||||
template<class DeducedHandler, class... Args>
|
||||
inline
|
||||
handler_ptr<T, Handler>::P::
|
||||
P(DeducedHandler&& h, Args&&... args)
|
||||
: n(1)
|
||||
, handler(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
t = reinterpret_cast<T*>(
|
||||
asio_handler_allocate(
|
||||
sizeof(T), std::addressof(handler)));
|
||||
try
|
||||
{
|
||||
t = new(t) T{handler,
|
||||
std::forward<Args>(args)...};
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
t, sizeof(T), std::addressof(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();
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p_->t, sizeof(T), std::addressof(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)...})
|
||||
{
|
||||
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
template<class... Args>
|
||||
handler_ptr<T, Handler>::
|
||||
handler_ptr(Handler const& handler, Args&&... args)
|
||||
: p_(new P{handler, std::forward<Args>(args)...})
|
||||
{
|
||||
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
auto
|
||||
handler_ptr<T, Handler>::
|
||||
release_handler() ->
|
||||
handler_type
|
||||
{
|
||||
BOOST_ASSERT(p_);
|
||||
BOOST_ASSERT(p_->t);
|
||||
p_->t->~T();
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p_->t, sizeof(T), std::addressof(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();
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p_->t, sizeof(T), std::addressof(p_->handler));
|
||||
p_->t = nullptr;
|
||||
p_->handler(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_READ_SIZE_IPP
|
||||
#define BOOST_BEAST_IMPL_READ_SIZE_IPP
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class T, class = void>
|
||||
struct has_read_size_helper : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_read_size_helper<T, decltype(
|
||||
read_size_helper(std::declval<T&>(), 512),
|
||||
(void)0)> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size(DynamicBuffer& buffer,
|
||||
std::size_t max_size, std::true_type)
|
||||
{
|
||||
return read_size_helper(buffer, max_size);
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size(DynamicBuffer& buffer,
|
||||
std::size_t max_size, std::false_type)
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(max_size >= 1);
|
||||
auto const size = buffer.size();
|
||||
auto const limit = buffer.max_size() - size;
|
||||
BOOST_ASSERT(size <= buffer.max_size());
|
||||
return std::min<std::size_t>(
|
||||
std::max<std::size_t>(512, buffer.capacity() - size),
|
||||
std::min<std::size_t>(max_size, limit));
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class DynamicBuffer>
|
||||
inline
|
||||
std::size_t
|
||||
read_size(
|
||||
DynamicBuffer& buffer, std::size_t max_size)
|
||||
{
|
||||
return detail::read_size(buffer, max_size,
|
||||
detail::has_read_size_helper<DynamicBuffer>{});
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size_or_throw(
|
||||
DynamicBuffer& buffer, std::size_t max_size)
|
||||
{
|
||||
auto const n = read_size(buffer, max_size);
|
||||
if(n == 0)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
return n;
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,321 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_STATIC_BUFFER_IPP
|
||||
#define BOOST_BEAST_IMPL_STATIC_BUFFER_IPP
|
||||
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
template<bool isInput, class T>
|
||||
class static_buffer_base::buffers_type
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
class const_iterator
|
||||
{
|
||||
friend class buffers_type;
|
||||
|
||||
static_buffer_base const* b_ = nullptr;
|
||||
int seg_ = 0;
|
||||
|
||||
const_iterator(bool at_end,
|
||||
static_buffer_base const& b)
|
||||
: b_(&b)
|
||||
{
|
||||
if(! at_end)
|
||||
{
|
||||
seg_ = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_end(std::integral_constant<bool, isInput>{});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_end(std::true_type)
|
||||
{
|
||||
if(b_->in_off_ + b_->in_size_ <= b_->capacity_)
|
||||
seg_ = 1;
|
||||
else
|
||||
seg_ = 2;
|
||||
}
|
||||
|
||||
void
|
||||
set_end(std::false_type)
|
||||
{
|
||||
if(((b_->in_off_ + b_->in_size_) % b_->capacity_)
|
||||
+ b_->out_size_ <= b_->capacity_)
|
||||
seg_ = 1;
|
||||
else
|
||||
seg_ = 2;
|
||||
}
|
||||
|
||||
T
|
||||
dereference(std::true_type) const
|
||||
{
|
||||
switch(seg_)
|
||||
{
|
||||
case 0:
|
||||
return {
|
||||
b_->begin_ + b_->in_off_,
|
||||
(std::min)(
|
||||
b_->in_size_,
|
||||
b_->capacity_ - b_->in_off_)};
|
||||
default:
|
||||
case 1:
|
||||
return {
|
||||
b_->begin_,
|
||||
b_->in_size_ - (
|
||||
b_->capacity_ - b_->in_off_)};
|
||||
}
|
||||
}
|
||||
|
||||
T
|
||||
dereference(std::false_type) const
|
||||
{
|
||||
switch(seg_)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto const out_off =
|
||||
(b_->in_off_ + b_->in_size_)
|
||||
% b_->capacity_;
|
||||
return {
|
||||
b_->begin_ + out_off,
|
||||
(std::min)(
|
||||
b_->out_size_,
|
||||
b_->capacity_ - out_off)};
|
||||
}
|
||||
default:
|
||||
case 1:
|
||||
{
|
||||
auto const out_off =
|
||||
(b_->in_off_ + b_->in_size_)
|
||||
% b_->capacity_;
|
||||
return {
|
||||
b_->begin_,
|
||||
b_->out_size_ - (
|
||||
b_->capacity_ - out_off)};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
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 const& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return b_ == other.b_ && seg_ == other.seg_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
value_type
|
||||
operator*() const
|
||||
{
|
||||
return dereference(
|
||||
std::integral_constant<bool, isInput>{});
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++seg_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--seg_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
buffers_type() = delete;
|
||||
buffers_type(buffers_type const&) = default;
|
||||
buffers_type& operator=(buffers_type const&) = delete;
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return const_iterator{false, b_};
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return const_iterator{true, b_};
|
||||
}
|
||||
|
||||
private:
|
||||
friend class static_buffer_base;
|
||||
|
||||
static_buffer_base const& b_;
|
||||
|
||||
explicit
|
||||
buffers_type(static_buffer_base const& b)
|
||||
: b_(b)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
static_buffer_base::
|
||||
static_buffer_base(void* p, std::size_t size)
|
||||
: begin_(reinterpret_cast<char*>(p))
|
||||
, capacity_(size)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_buffer_base::
|
||||
data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type{*this};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_buffer_base::
|
||||
mutable_data() ->
|
||||
mutable_data_type
|
||||
{
|
||||
return mutable_data_type{*this};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_buffer_base::
|
||||
prepare(std::size_t size) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(size > capacity_ - in_size_)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
out_size_ = size;
|
||||
return mutable_buffers_type{*this};
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
static_buffer_base::
|
||||
commit(std::size_t size)
|
||||
{
|
||||
in_size_ += (std::min)(size, out_size_);
|
||||
out_size_ = 0;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
static_buffer_base::
|
||||
consume(std::size_t size)
|
||||
{
|
||||
if(size < in_size_)
|
||||
{
|
||||
in_off_ = (in_off_ + size) % capacity_;
|
||||
in_size_ -= size;
|
||||
}
|
||||
else
|
||||
{
|
||||
// rewind the offset, so the next call to prepare
|
||||
// can have a longer continguous segment. this helps
|
||||
// algorithms optimized for larger buffesr.
|
||||
in_off_ = 0;
|
||||
in_size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
static_buffer_base::
|
||||
reset(void* p, std::size_t size)
|
||||
{
|
||||
begin_ = reinterpret_cast<char*>(p);
|
||||
capacity_ = size;
|
||||
in_off_ = 0;
|
||||
in_size_ = 0;
|
||||
out_size_ = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<std::size_t N>
|
||||
static_buffer<N>::
|
||||
static_buffer(static_buffer const& other)
|
||||
: static_buffer_base(buf_, N)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
this->commit(buffer_copy(
|
||||
this->prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
auto
|
||||
static_buffer<N>::
|
||||
operator=(static_buffer const& other) ->
|
||||
static_buffer<N>&
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
this->consume(this->size());
|
||||
this->commit(buffer_copy(
|
||||
this->prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,618 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_STATIC_STRING_IPP
|
||||
#define BOOST_BEAST_IMPL_STATIC_STRING_IPP
|
||||
|
||||
#include <boost/beast/core/detail/static_string.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
//
|
||||
// (constructor)
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string()
|
||||
{
|
||||
n_ = 0;
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(size_type count, CharT ch)
|
||||
{
|
||||
assign(count, ch);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string<M, CharT, Traits> const& other,
|
||||
size_type pos)
|
||||
{
|
||||
assign(other, pos);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string<M, CharT, Traits> const& other,
|
||||
size_type pos, size_type count)
|
||||
{
|
||||
assign(other, pos, count);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(CharT const* s, size_type count)
|
||||
{
|
||||
assign(s, count);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(CharT const* s)
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class InputIt>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(InputIt first, InputIt last)
|
||||
{
|
||||
assign(first, last);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string const& s)
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string<M, CharT, Traits> const& s)
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(std::initializer_list<CharT> init)
|
||||
{
|
||||
assign(init.begin(), init.end());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(string_view_type sv)
|
||||
{
|
||||
assign(sv);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class T, class>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(T const& t, size_type pos, size_type n)
|
||||
{
|
||||
assign(t, pos, n);
|
||||
}
|
||||
|
||||
//
|
||||
// (assignment)
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(size_type count, CharT ch) ->
|
||||
static_string&
|
||||
{
|
||||
if(count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"count > max_size()"});
|
||||
n_ = count;
|
||||
Traits::assign(&s_[0], n_, ch);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(static_string const& str) ->
|
||||
static_string&
|
||||
{
|
||||
n_ = str.n_;
|
||||
Traits::copy(&s_[0], &str.s_[0], n_ + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(static_string<M, CharT, Traits> const& str,
|
||||
size_type pos, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
auto const ss = str.substr(pos, count);
|
||||
return assign(ss.data(), ss.size());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(CharT const* s, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
if(count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"count > max_size()"});
|
||||
n_ = count;
|
||||
Traits::copy(&s_[0], s, n_);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class InputIt>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(InputIt first, InputIt last) ->
|
||||
static_string&
|
||||
{
|
||||
std::size_t const n = std::distance(first, last);
|
||||
if(n > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"n > max_size()"});
|
||||
n_ = n;
|
||||
for(auto it = &s_[0]; first != last; ++it, ++first)
|
||||
Traits::assign(*it, *first);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class T>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(T const& t, size_type pos, size_type count) ->
|
||||
typename std::enable_if<std::is_convertible<T,
|
||||
string_view_type>::value, static_string&>::type
|
||||
{
|
||||
auto const sv = string_view_type(t).substr(pos, count);
|
||||
if(sv.size() > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"sv.size() > max_size()"});
|
||||
n_ = sv.size();
|
||||
Traits::copy(&s_[0], &sv[0], n_);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
//
|
||||
// Element access
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
at(size_type pos) ->
|
||||
reference
|
||||
{
|
||||
if(pos >= size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"pos >= size()"});
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
at(size_type pos) const ->
|
||||
const_reference
|
||||
{
|
||||
if(pos >= size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"pos >= size()"});
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
reserve(std::size_t n)
|
||||
{
|
||||
if(n > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"n > max_size()"});
|
||||
}
|
||||
|
||||
//
|
||||
// Operations
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
clear()
|
||||
{
|
||||
n_ = 0;
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(size_type index, size_type count, CharT ch) ->
|
||||
static_string&
|
||||
{
|
||||
if(index > size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"index > size()"});
|
||||
insert(begin() + index, count, ch);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(size_type index, CharT const* s, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
if(index > size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"index > size()"});
|
||||
if(size() + count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() + count > max_size()"});
|
||||
Traits::move(
|
||||
&s_[index + count], &s_[index], size() - index);
|
||||
n_ += count;
|
||||
Traits::copy(&s_[index], s, count);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(size_type index,
|
||||
static_string<M, CharT, Traits> const& str,
|
||||
size_type index_str, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
auto const ss = str.substr(index_str, count);
|
||||
return insert(index, ss.data(), ss.size());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(const_iterator pos, size_type count, CharT ch) ->
|
||||
iterator
|
||||
{
|
||||
if(size() + count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() + count() > max_size()"});
|
||||
auto const index = pos - &s_[0];
|
||||
Traits::move(
|
||||
&s_[index + count], &s_[index], size() - index);
|
||||
n_ += count;
|
||||
Traits::assign(&s_[index], count, ch);
|
||||
term();
|
||||
return &s_[index];
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class InputIt>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(const_iterator pos, InputIt first, InputIt last) ->
|
||||
typename std::enable_if<
|
||||
detail::is_input_iterator<InputIt>::value,
|
||||
iterator>::type
|
||||
{
|
||||
std::size_t const count = std::distance(first, last);
|
||||
if(size() + count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() + count > max_size()"});
|
||||
std::size_t const index = pos - begin();
|
||||
Traits::move(
|
||||
&s_[index + count], &s_[index], size() - index);
|
||||
n_ += count;
|
||||
for(auto it = begin() + index;
|
||||
first != last; ++it, ++first)
|
||||
Traits::assign(*it, *first);
|
||||
term();
|
||||
return begin() + index;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class T>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(size_type index, const T& t,
|
||||
size_type index_str, size_type count) ->
|
||||
typename std::enable_if<std::is_convertible<
|
||||
T const&, string_view_type>::value &&
|
||||
! std::is_convertible<T const&, CharT const*>::value,
|
||||
static_string&>::type
|
||||
{
|
||||
auto const str =
|
||||
string_view_type(t).substr(index_str, count);
|
||||
return insert(index, str.data(), str.size());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
erase(size_type index, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
if(index > size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"index > size()"});
|
||||
auto const n = (std::min)(count, size() - index);
|
||||
Traits::move(
|
||||
&s_[index], &s_[index + n], size() - (index + n) + 1);
|
||||
n_ -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
erase(const_iterator pos) ->
|
||||
iterator
|
||||
{
|
||||
erase(pos - begin(), 1);
|
||||
return begin() + (pos - begin());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
erase(const_iterator first, const_iterator last) ->
|
||||
iterator
|
||||
{
|
||||
erase(first - begin(),
|
||||
std::distance(first, last));
|
||||
return begin() + (first - begin());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
push_back(CharT ch)
|
||||
{
|
||||
if(size() >= max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() >= max_size()"});
|
||||
Traits::assign(s_[n_++], ch);
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
append(static_string<M, CharT, Traits> const& str,
|
||||
size_type pos, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
// Valid range is [0, size)
|
||||
if(pos >= str.size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"pos > str.size()"});
|
||||
string_view_type const ss{&str.s_[pos],
|
||||
(std::min)(count, str.size() - pos)};
|
||||
insert(size(), ss.data(), ss.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
substr(size_type pos, size_type count) const ->
|
||||
string_view_type
|
||||
{
|
||||
if(pos > size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"pos > size()"});
|
||||
return{&s_[pos], (std::min)(count, size() - pos)};
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
copy(CharT* dest, size_type count, size_type pos) const ->
|
||||
size_type
|
||||
{
|
||||
auto const str = substr(pos, count);
|
||||
Traits::copy(dest, str.data(), str.size());
|
||||
return str.size();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
resize(std::size_t n)
|
||||
{
|
||||
if(n > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"n > max_size()"});
|
||||
n_ = n;
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
resize(std::size_t n, CharT c)
|
||||
{
|
||||
if(n > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"n > max_size()"});
|
||||
if(n > n_)
|
||||
Traits::assign(&s_[n_], n - n_, c);
|
||||
n_ = n;
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
swap(static_string& str)
|
||||
{
|
||||
static_string tmp(str);
|
||||
str.n_ = n_;
|
||||
Traits::copy(&str.s_[0], &s_[0], n_ + 1);
|
||||
n_ = tmp.n_;
|
||||
Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
swap(static_string<M, CharT, Traits>& str)
|
||||
{
|
||||
if(size() > str.max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() > str.max_size()"});
|
||||
if(str.size() > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"str.size() > max_size()"});
|
||||
static_string tmp(str);
|
||||
str.n_ = n_;
|
||||
Traits::copy(&str.s_[0], &s_[0], n_ + 1);
|
||||
n_ = tmp.n_;
|
||||
Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
|
||||
}
|
||||
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign_char(CharT ch, std::true_type) ->
|
||||
static_string&
|
||||
{
|
||||
n_ = 1;
|
||||
Traits::assign(s_[0], ch);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign_char(CharT, std::false_type) ->
|
||||
static_string&
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"max_size() == 0"});
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Integer>
|
||||
static_string<max_digits(sizeof(Integer))>
|
||||
to_static_string(Integer x, std::true_type)
|
||||
{
|
||||
if(x == 0)
|
||||
return {'0'};
|
||||
static_string<detail::max_digits(
|
||||
sizeof(Integer))> s;
|
||||
if(x < 0)
|
||||
{
|
||||
x = -x;
|
||||
char buf[max_digits(sizeof(x))];
|
||||
char* p = buf;
|
||||
for(;x > 0; x /= 10)
|
||||
*p++ = "0123456789"[x % 10];
|
||||
s.resize(1 + p - buf);
|
||||
s[0] = '-';
|
||||
auto d = &s[1];
|
||||
while(p > buf)
|
||||
*d++ = *--p;
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[max_digits(sizeof(x))];
|
||||
char* p = buf;
|
||||
for(;x > 0; x /= 10)
|
||||
*p++ = "0123456789"[x % 10];
|
||||
s.resize(p - buf);
|
||||
auto d = &s[0];
|
||||
while(p > buf)
|
||||
*d++ = *--p;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Integer>
|
||||
static_string<max_digits(sizeof(Integer))>
|
||||
to_static_string(Integer x, std::false_type)
|
||||
{
|
||||
if(x == 0)
|
||||
return {'0'};
|
||||
char buf[max_digits(sizeof(x))];
|
||||
char* p = buf;
|
||||
for(;x > 0; x /= 10)
|
||||
*p++ = "0123456789"[x % 10];
|
||||
static_string<detail::max_digits(
|
||||
sizeof(Integer))> s;
|
||||
s.resize(p - buf);
|
||||
auto d = &s[0];
|
||||
while(p > buf)
|
||||
*d++ = *--p;
|
||||
return s;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Integer>
|
||||
static_string<detail::max_digits(sizeof(Integer))>
|
||||
to_static_string(Integer x)
|
||||
{
|
||||
using CharT = char;
|
||||
using Traits = std::char_traits<CharT>;
|
||||
BOOST_STATIC_ASSERT(std::is_integral<Integer>::value);
|
||||
char buf[detail::max_digits(sizeof(Integer))];
|
||||
auto last = buf + sizeof(buf);
|
||||
auto it = detail::raw_to_string<
|
||||
CharT, Integer, Traits>(last, sizeof(buf), x);
|
||||
static_string<detail::max_digits(sizeof(Integer))> s;
|
||||
s.resize(static_cast<std::size_t>(last - it));
|
||||
auto p = s.data();
|
||||
while(it < last)
|
||||
Traits::assign(*p++, *it++);
|
||||
return s;
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_IMPL_STRING_PARAM_IPP
|
||||
#define BOOST_BEAST_IMPL_STRING_PARAM_IPP
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value>::type
|
||||
string_param::
|
||||
print(T const& t)
|
||||
{
|
||||
auto const last = buf_ + sizeof(buf_);
|
||||
auto const it = detail::raw_to_string<
|
||||
char, T, std::char_traits<char>>(
|
||||
last, sizeof(buf_), t);
|
||||
sv_ = {it, static_cast<std::size_t>(
|
||||
last - it)};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_integral<T>::value &&
|
||||
! std::is_convertible<T, string_view>::value
|
||||
>::type
|
||||
string_param::
|
||||
print(T const& t)
|
||||
{
|
||||
os_.emplace(buf_, sizeof(buf_));
|
||||
*os_ << t;
|
||||
os_->flush();
|
||||
sv_ = os_->str();
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
string_param::
|
||||
print(string_view sv)
|
||||
{
|
||||
sv_ = sv;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value>::type
|
||||
string_param::
|
||||
print_1(T const& t)
|
||||
{
|
||||
char buf[detail::max_digits(sizeof(T))];
|
||||
auto const last = buf + sizeof(buf);
|
||||
auto const it = detail::raw_to_string<
|
||||
char, T, std::char_traits<char>>(
|
||||
last, sizeof(buf), t);
|
||||
*os_ << string_view{it,
|
||||
static_cast<std::size_t>(last - it)};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_integral<T>::value>::type
|
||||
string_param::
|
||||
print_1(T const& t)
|
||||
{
|
||||
*os_ << t;
|
||||
}
|
||||
|
||||
template<class T0, class... TN>
|
||||
void
|
||||
string_param::
|
||||
print_n(T0 const& t0, TN const&... tn)
|
||||
{
|
||||
print_1(t0);
|
||||
print_n(tn...);
|
||||
}
|
||||
|
||||
template<class T0, class T1, class... TN>
|
||||
void
|
||||
string_param::
|
||||
print(T0 const& t0, T1 const& t1, TN const&... tn)
|
||||
{
|
||||
os_.emplace(buf_, sizeof(buf_));
|
||||
print_1(t0);
|
||||
print_1(t1);
|
||||
print_n(tn...);
|
||||
os_->flush();
|
||||
sv_ = os_->str();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
string_param::
|
||||
string_param(Args const&... args)
|
||||
{
|
||||
print(args...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,323 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_MULTI_BUFFER_HPP
|
||||
#define BOOST_BEAST_MULTI_BUFFER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A @b DynamicBuffer that uses multiple buffers internally.
|
||||
|
||||
The implementation uses a sequence of one or more character arrays
|
||||
of varying sizes. Additional character array objects are appended to
|
||||
the sequence to accommodate changes in the size of the character
|
||||
sequence.
|
||||
|
||||
@note Meets the requirements of @b DynamicBuffer.
|
||||
|
||||
@tparam Allocator The allocator to use for managing memory.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_multi_buffer
|
||||
{
|
||||
public:
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
/// 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 iter = typename list_type::iterator;
|
||||
using const_iter = 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<iter>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<const_iter>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
std::size_t max_ =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
list_type list_; // list of allocated buffers
|
||||
iter out_; // element that contains out_pos_
|
||||
size_type in_size_ = 0; // size of the input sequence
|
||||
size_type in_pos_ = 0; // input offset in list_.front()
|
||||
size_type out_pos_ = 0; // output offset in *out_
|
||||
size_type out_end_ = 0; // output end offset in list_.back()
|
||||
allocator_type alloc_; // the allocator
|
||||
|
||||
public:
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_buffers_type;
|
||||
|
||||
class mutable_buffers_type;
|
||||
|
||||
#endif
|
||||
|
||||
/// Destructor
|
||||
~basic_multi_buffer();
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
*/
|
||||
basic_multi_buffer();
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param limit The setting for @ref max_size.
|
||||
*/
|
||||
explicit
|
||||
basic_multi_buffer(std::size_t limit);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_multi_buffer(Allocator const& alloc);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param limit The setting for @ref max_size.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_multi_buffer(
|
||||
std::size_t limit, Allocator const& alloc);
|
||||
|
||||
/** Move constructor
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
*/
|
||||
basic_multi_buffer(basic_multi_buffer&& other);
|
||||
|
||||
/** Move constructor
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_multi_buffer(basic_multi_buffer&& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
basic_multi_buffer(basic_multi_buffer const& other);
|
||||
|
||||
/** Copy constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_multi_buffer(basic_multi_buffer const& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_multi_buffer(basic_multi_buffer<
|
||||
OtherAlloc> const& other);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param other The object to copy from.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_multi_buffer(basic_multi_buffer<
|
||||
OtherAlloc> const& other, allocator_type const& alloc);
|
||||
|
||||
/** Move assignment
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
*/
|
||||
basic_multi_buffer&
|
||||
operator=(basic_multi_buffer&& other);
|
||||
|
||||
/** Copy assignment
|
||||
|
||||
After the copy, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
basic_multi_buffer& operator=(basic_multi_buffer const& other);
|
||||
|
||||
/** Copy assignment
|
||||
|
||||
After the copy, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_multi_buffer& operator=(
|
||||
basic_multi_buffer<OtherAlloc> const& other);
|
||||
|
||||
/// Returns a copy of the associated allocator.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return alloc_;
|
||||
}
|
||||
|
||||
/// Returns the size of the input sequence.
|
||||
size_type
|
||||
size() const
|
||||
{
|
||||
return in_size_;
|
||||
}
|
||||
|
||||
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
|
||||
size_type
|
||||
max_size() const
|
||||
{
|
||||
return max_;
|
||||
}
|
||||
|
||||
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
|
||||
std::size_t
|
||||
capacity() const;
|
||||
|
||||
/** Get a list of buffers that represents the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/** Get a list of buffers that represents the output sequence, with the given size.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(size_type n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
void
|
||||
commit(size_type n);
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(size_type n);
|
||||
|
||||
template<class Alloc>
|
||||
friend
|
||||
void
|
||||
swap(
|
||||
basic_multi_buffer<Alloc>& lhs,
|
||||
basic_multi_buffer<Alloc>& rhs);
|
||||
|
||||
private:
|
||||
template<class OtherAlloc>
|
||||
friend class basic_multi_buffer;
|
||||
|
||||
void
|
||||
delete_element(element& e);
|
||||
|
||||
void
|
||||
delete_list();
|
||||
|
||||
void
|
||||
reset();
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
copy_from(DynamicBuffer const& other);
|
||||
|
||||
void
|
||||
move_assign(basic_multi_buffer& other, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_multi_buffer& other, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_multi_buffer const& other, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_multi_buffer const& other, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_multi_buffer&);
|
||||
|
||||
void
|
||||
swap(basic_multi_buffer&, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_multi_buffer&, std::false_type);
|
||||
|
||||
void
|
||||
debug_check() const;
|
||||
};
|
||||
|
||||
/// A typical multi buffer
|
||||
using multi_buffer = basic_multi_buffer<std::allocator<char>>;
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/multi_buffer.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_WRITE_OSTREAM_HPP
|
||||
#define BOOST_BEAST_WRITE_OSTREAM_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/ostream.hpp>
|
||||
#include <type_traits>
|
||||
#include <streambuf>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** Return an object representing a @b ConstBufferSequence.
|
||||
|
||||
This function wraps a reference to a buffer sequence and permits
|
||||
the following operation:
|
||||
|
||||
@li `operator<<` to `std::ostream`. No character translation is
|
||||
performed; unprintable and null characters will be transferred
|
||||
as-is to the output stream.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
multi_buffer b;
|
||||
...
|
||||
std::cout << buffers(b.data()) << std::endl;
|
||||
@endcode
|
||||
|
||||
@param b An object meeting the requirements of @b ConstBufferSequence
|
||||
to be streamed. The implementation will make a copy of this object.
|
||||
Ownership of the underlying memory is not transferred, the application
|
||||
is still responsible for managing its lifetime.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
implementation_defined
|
||||
#else
|
||||
detail::buffers_helper<ConstBufferSequence>
|
||||
#endif
|
||||
buffers(ConstBufferSequence const& b)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
return detail::buffers_helper<
|
||||
ConstBufferSequence>{b};
|
||||
}
|
||||
|
||||
/** Return an output stream that formats values into a @b DynamicBuffer.
|
||||
|
||||
This function wraps the caller provided @b DynamicBuffer into
|
||||
a `std::ostream` derived class, to allow `operator<<` stream style
|
||||
formatting operations.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
ostream(buffer) << "Hello, world!" << std::endl;
|
||||
@endcode
|
||||
|
||||
@note Calling members of the underlying buffer before the output
|
||||
stream is destroyed results in undefined behavior.
|
||||
|
||||
@param buffer An object meeting the requirements of @b DynamicBuffer
|
||||
into which the formatted output will be placed.
|
||||
|
||||
@return An object derived from `std::ostream` which redirects output
|
||||
The wrapped dynamic buffer is not modified, a copy is made instead.
|
||||
Ownership of the underlying memory is not transferred, the application
|
||||
is still responsible for managing its lifetime. The caller is
|
||||
responsible for ensuring the dynamic buffer is not destroyed for the
|
||||
lifetime of the output stream.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
implementation_defined
|
||||
#else
|
||||
detail::ostream_helper<
|
||||
DynamicBuffer, char, std::char_traits<char>,
|
||||
detail::basic_streambuf_movable::value>
|
||||
#endif
|
||||
ostream(DynamicBuffer& buffer)
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
return detail::ostream_helper<
|
||||
DynamicBuffer, char, std::char_traits<char>,
|
||||
detail::basic_streambuf_movable::value>{buffer};
|
||||
}
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_READ_SIZE_HELPER_HPP
|
||||
#define BOOST_BEAST_READ_SIZE_HELPER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** Returns a natural read size.
|
||||
|
||||
This function inspects the capacity, size, and maximum
|
||||
size of the dynamic buffer. Then it computes a natural
|
||||
read size given the passed-in upper limit. It favors
|
||||
a read size that does not require a reallocation, subject
|
||||
to a reasonable minimum to avoid tiny reads.
|
||||
|
||||
@param buffer The dynamic buffer to inspect.
|
||||
|
||||
@param max_size An upper limit on the returned value.
|
||||
|
||||
@note If the buffer is already at its maximum size, zero
|
||||
is returned.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size(DynamicBuffer& buffer, std::size_t max_size);
|
||||
|
||||
/** Returns a natural read size or throw if the buffer is full.
|
||||
|
||||
This function inspects the capacity, size, and maximum
|
||||
size of the dynamic buffer. Then it computes a natural
|
||||
read size given the passed-in upper limit. It favors
|
||||
a read size that does not require a reallocation, subject
|
||||
to a reasonable minimum to avoid tiny reads.
|
||||
|
||||
@param buffer The dynamic buffer to inspect.
|
||||
|
||||
@param max_size An upper limit on the returned value.
|
||||
|
||||
@throws std::length_error if `max_size > 0` and the buffer
|
||||
is full.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size_or_throw(DynamicBuffer& buffer,
|
||||
std::size_t max_size);
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/read_size.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_CORE_SPAN_HPP
|
||||
#define BOOST_BEAST_CORE_SPAN_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A range of bytes expressed as a ContiguousContainer
|
||||
|
||||
This class implements a non-owning reference to a storage
|
||||
area of a certain size and having an underlying integral
|
||||
type with size of 1.
|
||||
|
||||
@tparam T The type pointed to by span iterators
|
||||
*/
|
||||
template<class T>
|
||||
class span
|
||||
{
|
||||
T* data_ = nullptr;
|
||||
std::size_t size_ = 0;
|
||||
|
||||
public:
|
||||
/// The type of value, including cv qualifiers
|
||||
using element_type = T;
|
||||
|
||||
/// The type of value of each span element
|
||||
using value_type = typename std::remove_const<T>::type;
|
||||
|
||||
/// The type of integer used to index the span
|
||||
using index_type = std::ptrdiff_t;
|
||||
|
||||
/// A pointer to a span element
|
||||
using pointer = T*;
|
||||
|
||||
/// A reference to a span element
|
||||
using reference = T&;
|
||||
|
||||
/// The iterator used by the container
|
||||
using iterator = pointer;
|
||||
|
||||
/// The const pointer used by the container
|
||||
using const_pointer = T const*;
|
||||
|
||||
/// The const reference used by the container
|
||||
using const_reference = T const&;
|
||||
|
||||
/// The const iterator used by the container
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
/// Constructor
|
||||
span() = default;
|
||||
|
||||
/// Constructor
|
||||
span(span const&) = default;
|
||||
|
||||
/// Assignment
|
||||
span& operator=(span const&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param data A pointer to the beginning of the range of elements
|
||||
|
||||
@param size The number of elements pointed to by `data`
|
||||
*/
|
||||
span(T* data, std::size_t size)
|
||||
: data_(data), size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param container The container to construct from
|
||||
*/
|
||||
template<class ContiguousContainer
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
detail::is_contiguous_container<
|
||||
ContiguousContainer, T>::value>::type
|
||||
#endif
|
||||
>
|
||||
explicit
|
||||
span(ContiguousContainer&& container)
|
||||
: data_(container.data())
|
||||
, size_(container.size())
|
||||
{
|
||||
}
|
||||
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
explicit
|
||||
span(std::basic_string<CharT, Traits, Allocator>& s)
|
||||
: data_(&s[0])
|
||||
, size_(s.size())
|
||||
{
|
||||
}
|
||||
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
explicit
|
||||
span(std::basic_string<CharT, Traits, Allocator> const& s)
|
||||
: data_(s.data())
|
||||
, size_(s.size())
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Assignment
|
||||
|
||||
@param container The container to assign from
|
||||
*/
|
||||
template<class ContiguousContainer>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
span&
|
||||
#else
|
||||
typename std::enable_if<detail::is_contiguous_container<
|
||||
ContiguousContainer, T>::value,
|
||||
span&>::type
|
||||
#endif
|
||||
operator=(ContiguousContainer&& container)
|
||||
{
|
||||
data_ = container.data();
|
||||
size_ = container.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
span&
|
||||
operator=(std::basic_string<
|
||||
CharT, Traits, Allocator>& s)
|
||||
{
|
||||
data_ = &s[0];
|
||||
size_ = s.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
span&
|
||||
operator=(std::basic_string<
|
||||
CharT, Traits, Allocator> const& s)
|
||||
{
|
||||
data_ = s.data();
|
||||
size_ = s.size();
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Returns `true` if the span is empty
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
/// Returns a pointer to the beginning of the span
|
||||
T*
|
||||
data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the span
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/// Returns an iterator to the beginning of the span
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/// Returns an iterator to the beginning of the span
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/// Returns an iterator to one past the end of the span
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
/// Returns an iterator to one past the end of the span
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return data_ + size_;
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,226 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_STATIC_BUFFER_HPP
|
||||
#define BOOST_BEAST_STATIC_BUFFER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A circular @b DynamicBuffer with a fixed size internal buffer.
|
||||
|
||||
This implements a circular dynamic buffer. Calls to @ref prepare
|
||||
never require moving memory. The buffer sequences returned may
|
||||
be up to length two.
|
||||
Ownership of the underlying storage belongs to the derived class.
|
||||
|
||||
@note Variables are usually declared using the template class
|
||||
@ref static_buffer; however, to reduce the number of instantiations
|
||||
of template functions receiving static stream buffer arguments in a
|
||||
deduced context, the signature of the receiving function should use
|
||||
@ref static_buffer_base.
|
||||
|
||||
When used with @ref static_buffer this implements a dynamic
|
||||
buffer using no memory allocations.
|
||||
|
||||
@see @ref static_buffer
|
||||
*/
|
||||
class static_buffer_base
|
||||
{
|
||||
char* begin_;
|
||||
std::size_t in_off_ = 0;
|
||||
std::size_t in_size_ = 0;
|
||||
std::size_t out_size_ = 0;
|
||||
std::size_t capacity_;
|
||||
|
||||
template<bool, class T>
|
||||
class buffers_type;
|
||||
|
||||
static_buffer_base(static_buffer_base const& other) = delete;
|
||||
static_buffer_base& operator=(static_buffer_base const&) = delete;
|
||||
|
||||
public:
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_buffers_type = implementation_defined;
|
||||
#else
|
||||
using const_buffers_type =
|
||||
buffers_type<true, boost::asio::const_buffer>;
|
||||
#endif
|
||||
|
||||
/// The type used to represent the mutable input sequence as a list of buffers.
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using mutable_data_type = implementation_defined;
|
||||
#else
|
||||
using mutable_data_type =
|
||||
buffers_type<true, boost::asio::mutable_buffer>;
|
||||
#endif
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
#else
|
||||
using mutable_buffers_type =
|
||||
buffers_type<false, boost::asio::mutable_buffer>;
|
||||
#endif
|
||||
|
||||
/** Constructor
|
||||
|
||||
This creates a dynamic buffer using the provided storage area.
|
||||
|
||||
@param p A pointer to valid storage of at least `n` bytes.
|
||||
|
||||
@param size The number of valid bytes pointed to by `p`.
|
||||
*/
|
||||
static_buffer_base(void* p, std::size_t size);
|
||||
|
||||
/// Return the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return in_size_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the input sequence.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/** Get a list of mutable buffers that represent the input sequence.
|
||||
*/
|
||||
mutable_data_type
|
||||
mutable_data();
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@param size The number of bytes to request.
|
||||
|
||||
@throws std::length_error if the size would exceed the capacity.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t size);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@param size The nubmer of bytes to commit. If this is greater
|
||||
than the size of the output sequence, the entire output
|
||||
sequence is committed.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t size);
|
||||
|
||||
/** Remove bytes from the input sequence.
|
||||
|
||||
@param size The number of bytes to consume. If this is greater
|
||||
than the size of the input sequence, the entire input sequence
|
||||
is consumed.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t size);
|
||||
|
||||
protected:
|
||||
/** Constructor
|
||||
|
||||
The buffer will be in an undefined state. It is necessary
|
||||
for the derived class to call @ref reset in order to
|
||||
initialize the object.
|
||||
*/
|
||||
static_buffer_base();
|
||||
|
||||
/** Reset the pointed-to buffer.
|
||||
|
||||
This function resets the internal state to the buffer provided.
|
||||
All input and output sequences are invalidated. This function
|
||||
allows the derived class to construct its members before
|
||||
initializing the static buffer.
|
||||
|
||||
@param p A pointer to valid storage of at least `n` bytes.
|
||||
|
||||
@param size The number of valid bytes pointed to by `p`.
|
||||
*/
|
||||
void
|
||||
reset(void* p, std::size_t size);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A circular @b DynamicBuffer with a fixed size internal buffer.
|
||||
|
||||
This implements a circular dynamic buffer. Calls to @ref prepare
|
||||
never require moving memory. The buffer sequences returned may
|
||||
be up to length two.
|
||||
Ownership of the underlying storage belongs to the derived class.
|
||||
|
||||
@tparam N The number of bytes in the internal buffer.
|
||||
|
||||
@note To reduce the number of template instantiations when passing
|
||||
objects of this type in a deduced context, the signature of the
|
||||
receiving function should use @ref static_buffer_base instead.
|
||||
|
||||
@see @ref static_buffer_base
|
||||
*/
|
||||
template<std::size_t N>
|
||||
class static_buffer : public static_buffer_base
|
||||
{
|
||||
char buf_[N];
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
static_buffer(static_buffer const&);
|
||||
|
||||
/// Constructor
|
||||
static_buffer()
|
||||
: static_buffer_base(buf_, N)
|
||||
{
|
||||
}
|
||||
|
||||
/// Assignment
|
||||
static_buffer& operator=(static_buffer const&);
|
||||
|
||||
/// Returns the @ref static_buffer_base portion of this object
|
||||
static_buffer_base&
|
||||
base()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns the @ref static_buffer_base portion of this object
|
||||
static_buffer_base const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/static_buffer.ipp>
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_STRING_HPP
|
||||
#define BOOST_BEAST_STRING_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#ifndef BOOST_BEAST_NO_BOOST_STRING_VIEW
|
||||
# if BOOST_VERSION >= 106400
|
||||
# define BOOST_BEAST_NO_BOOST_STRING_VIEW 0
|
||||
# else
|
||||
# define BOOST_BEAST_NO_BOOST_STRING_VIEW 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BOOST_BEAST_NO_BOOST_STRING_VIEW
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#else
|
||||
#include <boost/utility/string_view.hpp>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
#if BOOST_BEAST_NO_BOOST_STRING_VIEW
|
||||
/// The type of string view used by the library
|
||||
using string_view = boost::string_ref;
|
||||
|
||||
/// The type of basic string view used by the library
|
||||
template<class CharT, class Traits>
|
||||
using basic_string_view =
|
||||
boost::basic_string_ref<CharT, Traits>;
|
||||
#else
|
||||
/// The type of string view used by the library
|
||||
using string_view = boost::string_view;
|
||||
|
||||
/// The type of basic string view used by the library
|
||||
template<class CharT, class Traits>
|
||||
using basic_string_view =
|
||||
boost::basic_string_view<CharT, Traits>;
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
char
|
||||
ascii_tolower(char c)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c += 'a' - 'A';
|
||||
return c;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
bool
|
||||
iequals(
|
||||
beast::string_view lhs,
|
||||
beast::string_view rhs)
|
||||
{
|
||||
auto n = lhs.size();
|
||||
if(rhs.size() != n)
|
||||
return false;
|
||||
auto p1 = lhs.data();
|
||||
auto p2 = rhs.data();
|
||||
char a, b;
|
||||
while(n--)
|
||||
{
|
||||
a = *p1++;
|
||||
b = *p2++;
|
||||
if(a != b)
|
||||
goto slow;
|
||||
}
|
||||
return true;
|
||||
|
||||
while(n--)
|
||||
{
|
||||
slow:
|
||||
if(ascii_tolower(a) != ascii_tolower(b))
|
||||
return false;
|
||||
a = *p1++;
|
||||
b = *p2++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
/** Returns `true` if two strings are equal, using a case-insensitive comparison.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
|
||||
@param lhs The string on the left side of the equality
|
||||
|
||||
@param rhs The string on the right side of the equality
|
||||
*/
|
||||
inline
|
||||
bool
|
||||
iequals(
|
||||
beast::string_view lhs,
|
||||
beast::string_view rhs)
|
||||
{
|
||||
return detail::iequals(lhs, rhs);
|
||||
}
|
||||
|
||||
/** A case-insensitive less predicate for strings.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
*/
|
||||
struct iless
|
||||
{
|
||||
bool
|
||||
operator()(
|
||||
string_view lhs,
|
||||
string_view rhs) const
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
return std::lexicographical_compare(
|
||||
begin(lhs), end(lhs), begin(rhs), end(rhs),
|
||||
[](char c1, char c2)
|
||||
{
|
||||
return detail::ascii_tolower(c1) < detail::ascii_tolower(c2);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** A case-insensitive equality predicate for strings.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
*/
|
||||
struct iequal
|
||||
{
|
||||
bool
|
||||
operator()(
|
||||
string_view lhs,
|
||||
string_view rhs) const
|
||||
{
|
||||
return iequals(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_STRING_PARAM_HPP
|
||||
#define BOOST_BEAST_STRING_PARAM_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/static_string.hpp>
|
||||
#include <boost/beast/core/detail/static_ostream.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
/** A function parameter which efficiently converts to string.
|
||||
|
||||
This is used as a function parameter type to allow callers
|
||||
notational convenience: objects other than strings may be
|
||||
passed in contexts where a string is expected. The conversion
|
||||
to string is made using `operator<<` to a non-dynamically
|
||||
allocated static buffer if possible, else to a `std::string`
|
||||
on overflow.
|
||||
|
||||
To use it, modify your function signature to accept
|
||||
`string_param` and then extract the string inside the
|
||||
function:
|
||||
@code
|
||||
void print(string_param s)
|
||||
{
|
||||
std::cout << s.str();
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class string_param
|
||||
{
|
||||
string_view sv_;
|
||||
char buf_[128];
|
||||
boost::optional<detail::static_ostream> os_;
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value>::type
|
||||
print(T const&);
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_integral<T>::value &&
|
||||
! std::is_convertible<T, string_view>::value
|
||||
>::type
|
||||
print(T const&);
|
||||
|
||||
void
|
||||
print(string_view);
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value>::type
|
||||
print_1(T const&);
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_integral<T>::value>::type
|
||||
print_1(T const&);
|
||||
|
||||
void
|
||||
print_n()
|
||||
{
|
||||
}
|
||||
|
||||
template<class T0, class... TN>
|
||||
void
|
||||
print_n(T0 const&, TN const&...);
|
||||
|
||||
template<class T0, class T1, class... TN>
|
||||
void
|
||||
print(T0 const&, T1 const&, TN const&...);
|
||||
|
||||
public:
|
||||
/// Copy constructor (disallowed)
|
||||
string_param(string_param const&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
string_param& operator=(string_param const&) = delete;
|
||||
|
||||
/** Constructor
|
||||
|
||||
This function constructs a string as if by concatenating
|
||||
the result of streaming each argument in order into an
|
||||
output stream. It is used as a notational convenience
|
||||
at call sites which expect a parameter with the semantics
|
||||
of a @ref string_view.
|
||||
|
||||
The implementation uses a small, internal static buffer
|
||||
to avoid memory allocations especially for the case where
|
||||
the list of arguments to be converted consists of a single
|
||||
integral type.
|
||||
|
||||
@param args One or more arguments to convert
|
||||
*/
|
||||
template<class... Args>
|
||||
string_param(Args const&... args);
|
||||
|
||||
/// Returns the contained string
|
||||
string_view
|
||||
str() const
|
||||
{
|
||||
return sv_;
|
||||
}
|
||||
|
||||
/// Implicit conversion to @ref string_view
|
||||
operator string_view const() const
|
||||
{
|
||||
return sv_;
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/core/impl/string_param.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,637 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_TYPE_TRAITS_HPP
|
||||
#define BOOST_BEAST_TYPE_TRAITS_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/file_base.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Buffer concepts
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Determine if `T` meets the requirements of @b ConstBufferSequence.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class ConstBufferSequence>
|
||||
void f(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class ConstBufferSequence>
|
||||
typename std::enable_if<is_const_buffer_sequence<ConstBufferSequence>::value>::type
|
||||
f(ConstBufferSequence const& buffers);
|
||||
@endcode
|
||||
*/
|
||||
template<class T>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
struct is_const_buffer_sequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_const_buffer_sequence :
|
||||
detail::is_buffer_sequence<T, boost::asio::const_buffer>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/** Determine if `T` meets the requirements of @b MutableBufferSequence.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class MutableBufferSequence>
|
||||
void f(MutableBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class MutableBufferSequence>
|
||||
typename std::enable_if<is_mutable_buffer_sequence<MutableBufferSequence>::value>::type
|
||||
f(MutableBufferSequence const& buffers);
|
||||
@endcode
|
||||
*/
|
||||
template<class T>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
struct is_mutable_buffer_sequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_mutable_buffer_sequence :
|
||||
detail::is_buffer_sequence<T, boost::asio::mutable_buffer>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/** Determine if `T` meets the requirements of @b DynamicBuffer.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class DynamicBuffer>
|
||||
void f(DynamicBuffer& buffer)
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class DynamicBuffer>
|
||||
typename std::enable_if<is_dynamic_buffer<DynamicBuffer>::value>::type
|
||||
f(DynamicBuffer const& buffer);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_dynamic_buffer : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_dynamic_buffer : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_dynamic_buffer<T, detail::void_t<decltype(
|
||||
std::declval<std::size_t&>() =
|
||||
std::declval<T const&>().size(),
|
||||
std::declval<std::size_t&>() =
|
||||
std::declval<T const&>().max_size(),
|
||||
std::declval<std::size_t&>() =
|
||||
std::declval<T const&>().capacity(),
|
||||
std::declval<T&>().commit(std::declval<std::size_t>()),
|
||||
std::declval<T&>().consume(std::declval<std::size_t>()),
|
||||
(void)0)> > : std::integral_constant<bool,
|
||||
is_const_buffer_sequence<
|
||||
typename T::const_buffers_type>::value &&
|
||||
is_mutable_buffer_sequence<
|
||||
typename T::mutable_buffers_type>::value &&
|
||||
std::is_same<typename T::const_buffers_type,
|
||||
decltype(std::declval<T const&>().data())>::value &&
|
||||
std::is_same<typename T::mutable_buffers_type,
|
||||
decltype(std::declval<T&>().prepare(
|
||||
std::declval<std::size_t>()))>::value
|
||||
>
|
||||
{
|
||||
};
|
||||
|
||||
// Special case for Boost.Asio which doesn't adhere to
|
||||
// net-ts but still provides a read_size_helper so things work
|
||||
template<class Allocator>
|
||||
struct is_dynamic_buffer<
|
||||
boost::asio::basic_streambuf<Allocator>> : std::true_type
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Handler concepts
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Determine if `T` meets the requirements of @b CompletionHandler.
|
||||
|
||||
This trait checks whether a type meets the requirements for a completion
|
||||
handler, and is also callable with the specified signature.
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
struct handler
|
||||
{
|
||||
void operator()(error_code&);
|
||||
};
|
||||
|
||||
static_assert(is_completion_handler<handler, void(error_code&)>::value,
|
||||
"Not a completion handler");
|
||||
@endcode
|
||||
*/
|
||||
template<class T, class Signature>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using is_completion_handler = std::integral_constant<bool, ...>;
|
||||
#else
|
||||
using is_completion_handler = std::integral_constant<bool,
|
||||
std::is_copy_constructible<typename std::decay<T>::type>::value &&
|
||||
detail::is_invocable<T, Signature>::value>;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Stream concepts
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Determine if `T` has the `get_io_service` member.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` has the member
|
||||
function with the correct signature, else type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with tag dispatching:
|
||||
|
||||
@code
|
||||
template<class T>
|
||||
void maybe_hello(T& t, std::true_type)
|
||||
{
|
||||
t.get_io_service().post([]{ std::cout << "Hello, world!" << std::endl; });
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void maybe_hello(T&, std::false_type)
|
||||
{
|
||||
// T does not have get_io_service
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void maybe_hello(T& t)
|
||||
{
|
||||
maybe_hello(t, has_get_io_service<T>{});
|
||||
}
|
||||
@endcode
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
struct stream
|
||||
{
|
||||
boost::asio::io_service& get_io_service();
|
||||
};
|
||||
|
||||
static_assert(has_get_io_service<stream>::value,
|
||||
"Missing get_io_service member");
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct has_get_io_service : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct has_get_io_service : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_get_io_service<T, beast::detail::void_t<decltype(
|
||||
detail::accept_rv<boost::asio::io_service&>(
|
||||
std::declval<T&>().get_io_service()),
|
||||
(void)0)>> : std::true_type {};
|
||||
#endif
|
||||
|
||||
/** Returns `T::lowest_layer_type` if it exists, else `T`
|
||||
|
||||
This will contain a nested `type` equal to `T::lowest_layer_type`
|
||||
if it exists, else `type` will be equal to `T`.
|
||||
|
||||
@par Example
|
||||
|
||||
Declaring a wrapper:
|
||||
|
||||
@code
|
||||
template<class Stream>
|
||||
struct stream_wrapper
|
||||
{
|
||||
using next_layer_type = typename std::remove_reference<Stream>::type;
|
||||
using lowest_layer_type = typename get_lowest_layer<stream_type>::type;
|
||||
};
|
||||
@endcode
|
||||
|
||||
Defining a metafunction:
|
||||
|
||||
@code
|
||||
/// Alias for `std::true_type` if `T` wraps another stream
|
||||
template<class T>
|
||||
using is_stream_wrapper : std::integral_constant<bool,
|
||||
! std::is_same<T, typename get_lowest_layer<T>::type>::value> {};
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct get_lowest_layer;
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct get_lowest_layer
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct get_lowest_layer<T, detail::void_t<
|
||||
typename T::lowest_layer_type>>
|
||||
{
|
||||
using type = typename T::lowest_layer_type;
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b AsyncReadStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class AsyncReadStream>
|
||||
void f(AsyncReadStream& stream)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class AsyncReadStream>
|
||||
typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type
|
||||
f(AsyncReadStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_async_read_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_async_read_stream : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_async_read_stream<T, detail::void_t<decltype(
|
||||
std::declval<T>().async_read_some(
|
||||
std::declval<detail::MutableBufferSequence>(),
|
||||
std::declval<detail::ReadHandler>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
has_get_io_service<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b AsyncWriteStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class AsyncWriteStream>
|
||||
void f(AsyncWriteStream& stream)
|
||||
{
|
||||
static_assert(is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class AsyncWriteStream>
|
||||
typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type
|
||||
f(AsyncWriteStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_async_write_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_async_write_stream : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_async_write_stream<T, detail::void_t<decltype(
|
||||
std::declval<T>().async_write_some(
|
||||
std::declval<detail::ConstBufferSequence>(),
|
||||
std::declval<detail::WriteHandler>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
has_get_io_service<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b SyncReadStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class SyncReadStream>
|
||||
void f(SyncReadStream& stream)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class SyncReadStream>
|
||||
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
|
||||
f(SyncReadStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_sync_read_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_sync_read_stream : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_sync_read_stream<T, detail::void_t<decltype(
|
||||
std::declval<std::size_t&>() = std::declval<T>().read_some(
|
||||
std::declval<detail::MutableBufferSequence>()),
|
||||
std::declval<std::size_t&>() = std::declval<T>().read_some(
|
||||
std::declval<detail::MutableBufferSequence>(),
|
||||
std::declval<boost::system::error_code&>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
has_get_io_service<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b SyncWriterStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class SyncReadStream>
|
||||
void f(SyncReadStream& stream)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class SyncReadStream>
|
||||
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
|
||||
f(SyncReadStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_sync_write_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_sync_write_stream : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_sync_write_stream<T, detail::void_t<decltype(
|
||||
std::declval<std::size_t&>() = std::declval<T&>().write_some(
|
||||
std::declval<detail::ConstBufferSequence>()),
|
||||
std::declval<std::size_t&>() = std::declval<T&>().write_some(
|
||||
std::declval<detail::ConstBufferSequence>(),
|
||||
std::declval<boost::system::error_code&>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
has_get_io_service<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b AsyncStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class AsyncStream>
|
||||
void f(AsyncStream& stream)
|
||||
{
|
||||
static_assert(is_async_stream<AsyncStream>::value,
|
||||
"AsyncStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class AsyncStream>
|
||||
typename std::enable_if<is_async_stream<AsyncStream>::value>::type
|
||||
f(AsyncStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_async_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T>
|
||||
using is_async_stream = std::integral_constant<bool,
|
||||
is_async_read_stream<T>::value && is_async_write_stream<T>::value>;
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b SyncStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class SyncStream>
|
||||
void f(SyncStream& stream)
|
||||
{
|
||||
static_assert(is_sync_stream<SyncStream>::value,
|
||||
"SyncStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class SyncStream>
|
||||
typename std::enable_if<is_sync_stream<SyncStream>::value>::type
|
||||
f(SyncStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_sync_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T>
|
||||
using is_sync_stream = std::integral_constant<bool,
|
||||
is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// File concepts
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Determine if `T` meets the requirements of @b File.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class File>
|
||||
void f(File& file)
|
||||
{
|
||||
static_assert(is_file<File>::value,
|
||||
"File requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class File>
|
||||
typename std::enable_if<is_file<File>::value>::type
|
||||
f(File& file);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_file : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_file : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_file<T, detail::void_t<decltype(
|
||||
std::declval<bool&>() = std::declval<T const&>().is_open(),
|
||||
std::declval<T&>().close(std::declval<error_code&>()),
|
||||
std::declval<T&>().open(
|
||||
std::declval<char const*>(),
|
||||
std::declval<file_mode>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::uint64_t&>() = std::declval<T&>().size(
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::uint64_t&>() = std::declval<T&>().pos(
|
||||
std::declval<error_code&>()),
|
||||
std::declval<T&>().seek(
|
||||
std::declval<std::uint64_t>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::size_t&>() = std::declval<T&>().read(
|
||||
std::declval<void*>(),
|
||||
std::declval<std::size_t>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::size_t&>() = std::declval<T&>().write(
|
||||
std::declval<void const*>(),
|
||||
std::declval<std::size_t>(),
|
||||
std::declval<error_code&>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
std::is_default_constructible<T>::value &&
|
||||
std::is_destructible<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_HPP
|
||||
#define BOOST_BEAST_HTTP_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
|
||||
#include <boost/beast/http/basic_dynamic_body.hpp>
|
||||
#include <boost/beast/http/basic_parser.hpp>
|
||||
#include <boost/beast/http/buffer_body.hpp>
|
||||
#include <boost/beast/http/chunk_encode.hpp>
|
||||
#include <boost/beast/http/dynamic_body.hpp>
|
||||
#include <boost/beast/http/empty_body.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/beast/http/fields.hpp>
|
||||
#include <boost/beast/http/file_body.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <boost/beast/http/span_body.hpp>
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/beast/http/string_body.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/http/vector_body.hpp>
|
||||
#include <boost/beast/http/verb.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_BASIC_DYNAMIC_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_BASIC_DYNAMIC_BODY_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A @b Body using a @b DynamicBuffer
|
||||
|
||||
This body uses a @b DynamicBuffer as a memory-based container
|
||||
for holding message payloads. Messages using this body type
|
||||
may be serialized and parsed.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
struct basic_dynamic_body
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
using value_type = DynamicBuffer;
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& v)
|
||||
{
|
||||
return v.size();
|
||||
}
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
DynamicBuffer const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
typename DynamicBuffer::const_buffers_type;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
basic_dynamic_body, Fields> const& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return {{body_.data(), false}};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
basic_dynamic_body, Fields>& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const n = buffer_size(buffers);
|
||||
if(body_.size() > body_.max_size() - n)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
}
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> b;
|
||||
try
|
||||
{
|
||||
b.emplace(body_.prepare((std::min)(n,
|
||||
body_.max_size() - body_.size())));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
auto const bytes_transferred =
|
||||
buffer_copy(*b, buffers);
|
||||
body_.commit(bytes_transferred);
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,532 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/file_base.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
//[example_http_file_body_1
|
||||
|
||||
/** A message body represented by a file on the filesystem.
|
||||
|
||||
Messages with this type have bodies represented by a
|
||||
file on the file system. When parsing a message using
|
||||
this body type, the data is stored in the file pointed
|
||||
to by the path, which must be writable. When serializing,
|
||||
the implementation will read the file and present those
|
||||
octets as the body content. This may be used to serve
|
||||
content from a directory as part of a web service.
|
||||
|
||||
@tparam File The implementation to use for accessing files.
|
||||
This type must meet the requirements of @b File.
|
||||
*/
|
||||
template<class File>
|
||||
struct basic_file_body
|
||||
{
|
||||
// Make sure the type meets the requirements
|
||||
static_assert(is_file<File>::value,
|
||||
"File requirements not met");
|
||||
|
||||
/// The type of File this body uses
|
||||
using file_type = File;
|
||||
|
||||
// Algorithm for retrieving buffers when serializing.
|
||||
class reader;
|
||||
|
||||
// Algorithm for storing buffers when parsing.
|
||||
class writer;
|
||||
|
||||
// The type of the @ref message::body member.
|
||||
class value_type;
|
||||
|
||||
/** Returns the size of the body
|
||||
|
||||
@param body The file body to use
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_2
|
||||
|
||||
/** The type of the @ref message::body member.
|
||||
|
||||
Messages declared using `basic_file_body` will have this type for
|
||||
the body member. This rich class interface allow the file to be
|
||||
opened with the file handle maintained directly in the object,
|
||||
which is attached to the message.
|
||||
*/
|
||||
template<class File>
|
||||
class basic_file_body<File>::value_type
|
||||
{
|
||||
// This body container holds a handle to the file
|
||||
// when it is open, and also caches the size when set.
|
||||
|
||||
friend class reader;
|
||||
friend class writer;
|
||||
friend struct basic_file_body;
|
||||
|
||||
// This represents the open file
|
||||
File file_;
|
||||
|
||||
// The cached file size
|
||||
std::uint64_t file_size_ = 0;
|
||||
|
||||
public:
|
||||
/** Destructor.
|
||||
|
||||
If the file is open, it is closed first.
|
||||
*/
|
||||
~value_type() = default;
|
||||
|
||||
/// Constructor
|
||||
value_type() = default;
|
||||
|
||||
/// Constructor
|
||||
value_type(value_type&& other) = default;
|
||||
|
||||
/// Move assignment
|
||||
value_type& operator=(value_type&& other) = default;
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
/// Returns the size of the file if open
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
return file_size_;
|
||||
}
|
||||
|
||||
/// Close the file if open
|
||||
void
|
||||
close();
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Set the open file
|
||||
|
||||
This function is used to set the open
|
||||
*/
|
||||
void
|
||||
reset(File&& file, error_code& ec);
|
||||
};
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
close()
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
// Open the file
|
||||
file_.open(path, mode, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
// Cache the size
|
||||
file_size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
reset(File&& file, error_code& ec)
|
||||
{
|
||||
// First close the file if open
|
||||
if(file_.is_open())
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
// Take ownership of the new file
|
||||
file_ = std::move(file);
|
||||
|
||||
// Cache the size
|
||||
file_size_ = file_.size(ec);
|
||||
}
|
||||
|
||||
// This is called from message::payload_size
|
||||
template<class File>
|
||||
std::uint64_t
|
||||
basic_file_body<File>::
|
||||
size(value_type const& body)
|
||||
{
|
||||
// Forward the call to the body
|
||||
return body.size();
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_3
|
||||
|
||||
/** Algorithm for retrieving buffers when serializing.
|
||||
|
||||
Objects of this type are created during serialization
|
||||
to extract the buffers representing the body.
|
||||
*/
|
||||
template<class File>
|
||||
class basic_file_body<File>::reader
|
||||
{
|
||||
value_type& body_; // The body we are reading from
|
||||
std::uint64_t remain_; // The number of unread bytes
|
||||
char buf_[4096]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
// The type of buffer sequence returned by `get`.
|
||||
//
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
// Constructor.
|
||||
//
|
||||
// `m` holds the message we are sending, which will
|
||||
// always have the `file_body` as the body type.
|
||||
//
|
||||
// Note that the message is passed by non-const reference.
|
||||
// This is intentional, because reading from the file
|
||||
// changes its "current position" which counts makes the
|
||||
// operation logically not-const (although it is bitwise
|
||||
// const).
|
||||
//
|
||||
// The BodyReader concept allows the reader to choose
|
||||
// whether to take the message by const reference or
|
||||
// non-const reference. Depending on the choice, a
|
||||
// serializer constructed using that body type will
|
||||
// require the same const or non-const reference to
|
||||
// construct.
|
||||
//
|
||||
// Readers which accept const messages usually allow
|
||||
// the same body to be serialized by multiple threads
|
||||
// concurrently, while readers accepting non-const
|
||||
// messages may only be serialized by one thread at
|
||||
// a time.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
reader(message<
|
||||
isRequest, basic_file_body, Fields>& m);
|
||||
|
||||
// Initializer
|
||||
//
|
||||
// This is called before the body is serialized and
|
||||
// gives the reader a chance to do something that might
|
||||
// need to return an error code.
|
||||
//
|
||||
void
|
||||
init(error_code& ec);
|
||||
|
||||
// This function is called zero or more times to
|
||||
// retrieve buffers. A return value of `boost::none`
|
||||
// means there are no more buffers. Otherwise,
|
||||
// the contained pair will have the next buffer
|
||||
// to serialize, and a `bool` indicating whether
|
||||
// or not there may be additional buffers.
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_4
|
||||
|
||||
// Here we just stash a reference to the path for later.
|
||||
// Rather than dealing with messy constructor exceptions,
|
||||
// we save the things that might fail for the call to `init`.
|
||||
//
|
||||
template<class File>
|
||||
template<bool isRequest, class Fields>
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
reader(message<isRequest, basic_file_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
// The file must already be open
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
|
||||
// Get the size of the file
|
||||
remain_ = body_.file_size_;
|
||||
}
|
||||
|
||||
// Initializer
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
init(error_code& ec)
|
||||
{
|
||||
// The error_code specification requires that we
|
||||
// either set the error to some value, or set it
|
||||
// to indicate no error.
|
||||
//
|
||||
// We don't do anything fancy so set "no error"
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
// This function is called repeatedly by the serializer to
|
||||
// retrieve the buffers representing the body. Our strategy
|
||||
// is to read into our buffer and return it until we have
|
||||
// read through the whole file.
|
||||
//
|
||||
template<class File>
|
||||
auto
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
get(error_code& ec) ->
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
{
|
||||
// Calculate the smaller of our buffer size,
|
||||
// or the amount of unread data in the file.
|
||||
auto const amount = remain_ > sizeof(buf_) ?
|
||||
sizeof(buf_) : static_cast<std::size_t>(remain_);
|
||||
|
||||
// Handle the case where the file is zero length
|
||||
if(amount == 0)
|
||||
{
|
||||
// Modify the error code to indicate success
|
||||
// This is required by the error_code specification.
|
||||
//
|
||||
// NOTE We use the existing category instead of calling
|
||||
// into the library to get the generic category because
|
||||
// that saves us a possibly expensive atomic operation.
|
||||
//
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Now read the next buffer
|
||||
auto const nread = body_.file_.read(buf_, amount, ec);
|
||||
if(ec)
|
||||
return boost::none;
|
||||
|
||||
// Make sure there is forward progress
|
||||
BOOST_ASSERT(nread != 0);
|
||||
BOOST_ASSERT(nread <= remain_);
|
||||
|
||||
// Update the amount remaining based on what we got
|
||||
remain_ -= nread;
|
||||
|
||||
// Return the buffer to the caller.
|
||||
//
|
||||
// The second element of the pair indicates whether or
|
||||
// not there is more data. As long as there is some
|
||||
// unread bytes, there will be more data. Otherwise,
|
||||
// we set this bool to `false` so we will not be called
|
||||
// again.
|
||||
//
|
||||
ec.assign(0, ec.category());
|
||||
return {{
|
||||
const_buffers_type{buf_, nread}, // buffer to return.
|
||||
remain_ > 0 // `true` if there are more buffers.
|
||||
}};
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_5
|
||||
|
||||
/** Algorithm for storing buffers when parsing.
|
||||
|
||||
Objects of this type are created during parsing
|
||||
to store incoming buffers representing the body.
|
||||
*/
|
||||
template<class File>
|
||||
class basic_file_body<File>::writer
|
||||
{
|
||||
value_type& body_; // The body we are writing to
|
||||
|
||||
public:
|
||||
// Constructor.
|
||||
//
|
||||
// This is called after the header is parsed and
|
||||
// indicates that a non-zero sized body may be present.
|
||||
// `m` holds the message we are receiving, which will
|
||||
// always have the `file_body` as the body type.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(
|
||||
message<isRequest, basic_file_body, Fields>& m);
|
||||
|
||||
// Initializer
|
||||
//
|
||||
// This is called before the body is parsed and
|
||||
// gives the writer a chance to do something that might
|
||||
// need to return an error code. It informs us of
|
||||
// the payload size (`content_length`) which we can
|
||||
// optionally use for optimization.
|
||||
//
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec);
|
||||
|
||||
// This function is called one or more times to store
|
||||
// buffer sequences corresponding to the incoming body.
|
||||
//
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
|
||||
// This function is called when writing is complete.
|
||||
// It is an opportunity to perform any final actions
|
||||
// which might fail, in order to return an error code.
|
||||
// Operations that might fail should not be attemped in
|
||||
// destructors, since an exception thrown from there
|
||||
// would terminate the program.
|
||||
//
|
||||
void
|
||||
finish(error_code& ec);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_6
|
||||
|
||||
// We don't do much in the writer constructor since the
|
||||
// file is already open.
|
||||
//
|
||||
template<class File>
|
||||
template<bool isRequest, class Fields>
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
writer(message<isRequest, basic_file_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
init(
|
||||
boost::optional<std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
// The file must already be open for writing
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
|
||||
// We don't do anything with this but a sophisticated
|
||||
// application might check available space on the device
|
||||
// to see if there is enough room to store the body.
|
||||
boost::ignore_unused(content_length);
|
||||
|
||||
// The error_code specification requires that we
|
||||
// either set the error to some value, or set it
|
||||
// to indicate no error.
|
||||
//
|
||||
// We don't do anything fancy so set "no error"
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
// This will get called one or more times with body buffers
|
||||
//
|
||||
template<class File>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
put(ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
// This function must return the total number of
|
||||
// bytes transferred from the input buffers.
|
||||
std::size_t nwritten = 0;
|
||||
|
||||
// Loop over all the buffers in the sequence,
|
||||
// and write each one to the file.
|
||||
for(boost::asio::const_buffer buffer : buffers)
|
||||
{
|
||||
// Write this buffer to the file
|
||||
nwritten += body_.file_.write(
|
||||
boost::asio::buffer_cast<void const*>(buffer),
|
||||
boost::asio::buffer_size(buffer),
|
||||
ec);
|
||||
if(ec)
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
// Indicate success
|
||||
// This is required by the error_code specification
|
||||
ec.assign(0, ec.category());
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
// Called after writing is done when there's no error.
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
finish(error_code& ec)
|
||||
{
|
||||
// This has to be cleared before returning, to
|
||||
// indicate no error. The specification requires it.
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
// operator<< is not supported for file_body
|
||||
template<bool isRequest, class File, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, message<
|
||||
isRequest, basic_file_body<File>, Fields> const& msg) = delete;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,611 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_BASIC_PARSER_HPP
|
||||
#define BOOST_BEAST_HTTP_BASIC_PARSER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/beast/http/verb.hpp>
|
||||
#include <boost/beast/http/detail/basic_parser.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A parser for decoding HTTP/1 wire format messages.
|
||||
|
||||
This parser is designed to efficiently parse messages in the
|
||||
HTTP/1 wire format. It allocates no memory when input is
|
||||
presented as a single contiguous buffer, and uses minimal
|
||||
state. It will handle chunked encoding and it understands
|
||||
the semantics of the Connection, Content-Length, and Upgrade
|
||||
fields.
|
||||
The parser is optimized for the case where the input buffer
|
||||
sequence consists of a single contiguous buffer. The
|
||||
@ref beast::flat_buffer class is provided, which guarantees
|
||||
that the input sequence of the stream buffer will be represented
|
||||
by exactly one contiguous buffer. To ensure the optimum performance
|
||||
of the parser, use @ref beast::flat_buffer with HTTP algorithms
|
||||
such as @ref beast::http::read, @ref beast::http::read_some,
|
||||
@ref beast::http::async_read, and @ref beast::http::async_read_some.
|
||||
Alternatively, the caller may use custom techniques to ensure that
|
||||
the structured portion of the HTTP message (header or chunk header)
|
||||
is contained in a linear buffer.
|
||||
|
||||
The interface uses CRTP (Curiously Recurring Template Pattern).
|
||||
To use this class directly, derive from @ref basic_parser. When
|
||||
bytes are presented, the implementation will make a series of zero
|
||||
or more calls to derived class members functions (termed "callbacks"
|
||||
in this context) matching a specific signature.
|
||||
|
||||
Every callback must be provided by the derived class, or else
|
||||
a compilation error will be generated. This exemplar shows
|
||||
the signature and description of the callbacks required in
|
||||
the derived class.
|
||||
For each callback, the function will ensure that `!ec` is `true`
|
||||
if there was no error or set to the appropriate error code if
|
||||
there was one. If an error is set, the value is propagated to
|
||||
the caller of the parser.
|
||||
|
||||
@par Derived Class Requirements
|
||||
@code
|
||||
template<bool isRequest>
|
||||
class derived
|
||||
: public basic_parser<isRequest, derived<isRequest>>
|
||||
{
|
||||
private:
|
||||
// The friend declaration is needed,
|
||||
// otherwise the callbacks must be made public.
|
||||
friend class basic_parser<isRequest, derived>;
|
||||
|
||||
/// Called after receiving the request-line (isRequest == true).
|
||||
void
|
||||
on_request_impl(
|
||||
verb method, // The method verb, verb::unknown if no match
|
||||
string_view method_str, // The method as a string
|
||||
string_view target, // The request-target
|
||||
int version, // The HTTP-version
|
||||
error_code& ec); // The error returned to the caller, if any
|
||||
|
||||
/// Called after receiving the start-line (isRequest == false).
|
||||
void
|
||||
on_response_impl(
|
||||
int code, // The status-code
|
||||
string_view reason, // The obsolete reason-phrase
|
||||
int version, // The HTTP-version
|
||||
error_code& ec); // The error returned to the caller, if any
|
||||
|
||||
/// Called after receiving a header field.
|
||||
void
|
||||
on_field_impl(
|
||||
field f, // The known-field enumeration constant
|
||||
string_view name, // The field name string.
|
||||
string_view value, // The field value
|
||||
error_code& ec); // The error returned to the caller, if any
|
||||
|
||||
/// Called after the complete header is received.
|
||||
void
|
||||
on_header_impl(
|
||||
error_code& ec); // The error returned to the caller, if any
|
||||
|
||||
/// Called just before processing the body, if a body exists.
|
||||
void
|
||||
on_body_init_impl(
|
||||
boost::optional<
|
||||
std::uint64_t> const&
|
||||
content_length, // Content length if known, else `boost::none`
|
||||
error_code& ec); // The error returned to the caller, if any
|
||||
|
||||
/// Called for each piece of the body, if a body exists.
|
||||
//!
|
||||
//! This is used when there is no chunked transfer coding.
|
||||
//!
|
||||
//! The function returns the number of bytes consumed from the
|
||||
//! input buffer. Any input octets not consumed will be will be
|
||||
//! presented on subsequent calls.
|
||||
//!
|
||||
std::size_t
|
||||
on_body_impl(
|
||||
string_view s, // A portion of the body
|
||||
error_code& ec); // The error returned to the caller, if any
|
||||
|
||||
/// Called for each chunk header.
|
||||
void
|
||||
on_chunk_header_impl(
|
||||
std::uint64_t size, // The size of the upcoming chunk,
|
||||
// or zero for the last chunk
|
||||
string_view extension, // The chunk extensions (may be empty)
|
||||
error_code& ec); // The error returned to the caller, if any
|
||||
|
||||
/// Called to deliver the chunk body.
|
||||
//!
|
||||
//! This is used when there is a chunked transfer coding. The
|
||||
//! implementation will automatically remove the encoding before
|
||||
//! calling this function.
|
||||
//!
|
||||
//! The function returns the number of bytes consumed from the
|
||||
//! input buffer. Any input octets not consumed will be will be
|
||||
//! presented on subsequent calls.
|
||||
//!
|
||||
std::size_t
|
||||
on_chunk_body_impl(
|
||||
std::uint64_t remain, // The number of bytes remaining in the chunk,
|
||||
// including what is being passed here.
|
||||
// or zero for the last chunk
|
||||
string_view body, // The next piece of the chunk body
|
||||
error_code& ec); // The error returned to the caller, if any
|
||||
|
||||
/// Called when the complete message is parsed.
|
||||
void
|
||||
on_finish_impl(error_code& ec);
|
||||
|
||||
public:
|
||||
derived() = default;
|
||||
};
|
||||
@endcode
|
||||
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
|
||||
@tparam Derived The derived class type. This is part of the
|
||||
Curiously Recurring Template Pattern interface.
|
||||
|
||||
@note If the parser encounters a field value with obs-fold
|
||||
longer than 4 kilobytes in length, an error is generated.
|
||||
*/
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser
|
||||
: private detail::basic_parser_base
|
||||
{
|
||||
template<bool OtherIsRequest, class OtherDerived>
|
||||
friend class basic_parser;
|
||||
|
||||
// limit on the size of the stack flat buffer
|
||||
static std::size_t constexpr max_stack_buffer = 8192;
|
||||
|
||||
// Message will be complete after reading header
|
||||
static unsigned constexpr flagSkipBody = 1<< 0;
|
||||
|
||||
// Consume input buffers across semantic boundaries
|
||||
static unsigned constexpr flagEager = 1<< 1;
|
||||
|
||||
// The parser has read at least one byte
|
||||
static unsigned constexpr flagGotSome = 1<< 2;
|
||||
|
||||
// Message semantics indicate a body is expected.
|
||||
// cleared if flagSkipBody set
|
||||
//
|
||||
static unsigned constexpr flagHasBody = 1<< 3;
|
||||
|
||||
static unsigned constexpr flagHTTP11 = 1<< 4;
|
||||
static unsigned constexpr flagNeedEOF = 1<< 5;
|
||||
static unsigned constexpr flagExpectCRLF = 1<< 6;
|
||||
static unsigned constexpr flagConnectionClose = 1<< 7;
|
||||
static unsigned constexpr flagConnectionUpgrade = 1<< 8;
|
||||
static unsigned constexpr flagConnectionKeepAlive = 1<< 9;
|
||||
static unsigned constexpr flagContentLength = 1<< 10;
|
||||
static unsigned constexpr flagChunked = 1<< 11;
|
||||
static unsigned constexpr flagUpgrade = 1<< 12;
|
||||
static unsigned constexpr flagFinalChunk = 1<< 13;
|
||||
|
||||
static
|
||||
std::uint64_t
|
||||
default_body_limit(std::true_type)
|
||||
{
|
||||
// limit for requests
|
||||
return 1 * 1024 * 1024; // 1MB
|
||||
}
|
||||
|
||||
static
|
||||
std::uint64_t
|
||||
default_body_limit(std::false_type)
|
||||
{
|
||||
// limit for responses
|
||||
return 8 * 1024 * 1024; // 8MB
|
||||
}
|
||||
|
||||
std::uint64_t body_limit_; // max payload body
|
||||
std::uint64_t len_; // size of chunk or body
|
||||
std::unique_ptr<char[]> buf_; // temp storage
|
||||
std::size_t buf_len_ = 0; // size of buf_
|
||||
std::size_t skip_ = 0; // resume search here
|
||||
std::uint32_t
|
||||
header_limit_ = 8192; // max header size
|
||||
unsigned short status_; // response status
|
||||
state state_ = // initial state
|
||||
state::nothing_yet;
|
||||
unsigned f_ = 0; // flags
|
||||
|
||||
public:
|
||||
/// `true` if this parser parses requests, `false` for responses.
|
||||
using is_request =
|
||||
std::integral_constant<bool, isRequest>;
|
||||
|
||||
/// Destructor
|
||||
~basic_parser();
|
||||
|
||||
/// Constructor
|
||||
basic_parser(basic_parser const&) = delete;
|
||||
|
||||
/// Constructor
|
||||
basic_parser& operator=(basic_parser const&) = delete;
|
||||
|
||||
/// Constructor
|
||||
basic_parser();
|
||||
|
||||
/** Move constructor
|
||||
|
||||
After the move, the only valid operation on the
|
||||
moved-from object is destruction.
|
||||
*/
|
||||
template<class OtherDerived>
|
||||
basic_parser(basic_parser<isRequest, OtherDerived>&&);
|
||||
|
||||
/** Returns a reference to this object as a @ref basic_parser.
|
||||
|
||||
This is used to pass a derived class where a base class is
|
||||
expected, to choose a correct function overload when the
|
||||
resolution would be ambiguous.
|
||||
*/
|
||||
basic_parser&
|
||||
base()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns a constant reference to this object as a @ref basic_parser.
|
||||
|
||||
This is used to pass a derived class where a base class is
|
||||
expected, to choose a correct function overload when the
|
||||
resolution would be ambiguous.
|
||||
*/
|
||||
basic_parser const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns `true` if the parser has received at least one byte of input.
|
||||
bool
|
||||
got_some() const
|
||||
{
|
||||
return state_ != state::nothing_yet;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is complete.
|
||||
|
||||
The message is complete after the full header is prduced
|
||||
and one of the following is true:
|
||||
|
||||
@li The skip body option was set.
|
||||
|
||||
@li The semantics of the message indicate there is no body.
|
||||
|
||||
@li The semantics of the message indicate a body is expected,
|
||||
and the entire body was parsed.
|
||||
*/
|
||||
bool
|
||||
is_done() const
|
||||
{
|
||||
return state_ == state::complete;
|
||||
}
|
||||
|
||||
/** Returns `true` if a the parser has produced the full header.
|
||||
*/
|
||||
bool
|
||||
is_header_done() const
|
||||
{
|
||||
return state_ > state::fields;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_upgrade() const
|
||||
{
|
||||
return (f_ & flagConnectionUpgrade) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if the last value for Transfer-Encoding is "chunked".
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_chunked() const
|
||||
{
|
||||
return (f_ & flagChunked) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message has keep-alive connection semantics.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_keep_alive() const;
|
||||
|
||||
/** Returns the optional value of Content-Length if known.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
content_length() const;
|
||||
|
||||
/** Returns `true` if the message semantics require an end of file.
|
||||
|
||||
Depending on the contents of the header, the parser may
|
||||
require and end of file notification to know where the end
|
||||
of the body lies. If this function returns `true` it will be
|
||||
necessary to call @ref put_eof when there will never be additional
|
||||
data from the input.
|
||||
*/
|
||||
bool
|
||||
need_eof() const
|
||||
{
|
||||
return (f_ & flagNeedEOF) != 0;
|
||||
}
|
||||
|
||||
/** Set the limit on the payload body.
|
||||
|
||||
This function sets the maximum allowed size of the payload body,
|
||||
before any encodings except chunked have been removed. Depending
|
||||
on the message semantics, one of these cases will apply:
|
||||
|
||||
@li The Content-Length is specified and exceeds the limit. In
|
||||
this case the result @ref error::body_limit is returned
|
||||
immediately after the header is parsed.
|
||||
|
||||
@li The Content-Length is unspecified and the chunked encoding
|
||||
is not specified as the last encoding. In this case the end of
|
||||
message is determined by the end of file indicator on the
|
||||
associated stream or input source. If a sufficient number of
|
||||
body payload octets are presented to the parser to exceed the
|
||||
configured limit, the parse fails with the result
|
||||
@ref error::body_limit
|
||||
|
||||
@li The Transfer-Encoding specifies the chunked encoding as the
|
||||
last encoding. In this case, when the number of payload body
|
||||
octets produced by removing the chunked encoding exceeds
|
||||
the configured limit, the parse fails with the result
|
||||
@ref error::body_limit.
|
||||
|
||||
Setting the limit after any body octets have been parsed
|
||||
results in undefined behavior.
|
||||
|
||||
The default limit is 1MB for requests and 8MB for responses.
|
||||
|
||||
@param v The payload body limit to set
|
||||
*/
|
||||
void
|
||||
body_limit(std::uint64_t v)
|
||||
{
|
||||
body_limit_ = v;
|
||||
}
|
||||
|
||||
/** Set a limit on the total size of the header.
|
||||
|
||||
This function sets the maximum allowed size of the header
|
||||
including all field name, value, and delimiter characters
|
||||
and also including the CRLF sequences in the serialized
|
||||
input. If the end of the header is not found within the
|
||||
limit of the header size, the error @ref error::header_limit
|
||||
is returned by @ref put.
|
||||
|
||||
Setting the limit after any header octets have been parsed
|
||||
results in undefined behavior.
|
||||
*/
|
||||
void
|
||||
header_limit(std::uint32_t v)
|
||||
{
|
||||
header_limit_ = v;
|
||||
}
|
||||
|
||||
/// Returns `true` if the eager parse option is set.
|
||||
bool
|
||||
eager() const
|
||||
{
|
||||
return (f_ & flagEager) != 0;
|
||||
}
|
||||
|
||||
/** Set the eager parse option.
|
||||
|
||||
Normally the parser returns after successfully parsing a structured
|
||||
element (header, chunk header, or chunk body) even if there are octets
|
||||
remaining in the input. This is necessary when attempting to parse the
|
||||
header first, or when the caller wants to inspect information which may
|
||||
be invalidated by subsequent parsing, such as a chunk extension. The
|
||||
`eager` option controls whether the parser keeps going after parsing
|
||||
structured element if there are octets remaining in the buffer and no
|
||||
error occurs. This option is automatically set or cleared during certain
|
||||
stream operations to improve performance with no change in functionality.
|
||||
|
||||
The default setting is `false`.
|
||||
|
||||
@param v `true` to set the eager parse option or `false` to disable it.
|
||||
*/
|
||||
void
|
||||
eager(bool v)
|
||||
{
|
||||
if(v)
|
||||
f_ |= flagEager;
|
||||
else
|
||||
f_ &= ~flagEager;
|
||||
}
|
||||
|
||||
/// Returns `true` if the skip parse option is set.
|
||||
bool
|
||||
skip()
|
||||
{
|
||||
return (f_ & flagSkipBody) != 0;
|
||||
}
|
||||
|
||||
/** Set the skip parse option.
|
||||
|
||||
This option controls whether or not the parser expects to see an HTTP
|
||||
body, regardless of the presence or absence of certain fields such as
|
||||
Content-Length or a chunked Transfer-Encoding. Depending on the request,
|
||||
some responses do not carry a body. For example, a 200 response to a
|
||||
CONNECT request from a tunneling proxy, or a response to a HEAD request.
|
||||
In these cases, callers may use this function inform the parser that
|
||||
no body is expected. The parser will consider the message complete
|
||||
after the header has been received.
|
||||
|
||||
@param v `true` to set the skip body option or `false` to disable it.
|
||||
|
||||
@note This function must called before any bytes are processed.
|
||||
*/
|
||||
void
|
||||
skip(bool v);
|
||||
|
||||
/** Write a buffer sequence to the parser.
|
||||
|
||||
This function attempts to incrementally parse the HTTP
|
||||
message data stored in the caller provided buffers. Upon
|
||||
success, a positive return value indicates that the parser
|
||||
made forward progress, consuming that number of
|
||||
bytes.
|
||||
|
||||
In some cases there may be an insufficient number of octets
|
||||
in the input buffer in order to make forward progress. This
|
||||
is indicated by the code @ref error::need_more. When
|
||||
this happens, the caller should place additional bytes into
|
||||
the buffer sequence and call @ref put again.
|
||||
|
||||
The error code @ref error::need_more is special. When this
|
||||
error is returned, a subsequent call to @ref put may succeed
|
||||
if the buffers have been updated. Otherwise, upon error
|
||||
the parser may not be restarted.
|
||||
|
||||
@param buffers An object meeting the requirements of
|
||||
@b ConstBufferSequence that represents the next chunk of
|
||||
message data. If the length of this buffer sequence is
|
||||
one, the implementation will not allocate additional memory.
|
||||
The class @ref beast::flat_buffer is provided as one way to
|
||||
meet this requirement
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of octets consumed in the buffer
|
||||
sequence. The caller should remove these octets even if the
|
||||
error is set.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
put(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec);
|
||||
#endif
|
||||
|
||||
/** Inform the parser that the end of stream was reached.
|
||||
|
||||
In certain cases, HTTP needs to know where the end of
|
||||
the stream is. For example, sometimes servers send
|
||||
responses without Content-Length and expect the client
|
||||
to consume input (for the body) until EOF. Callbacks
|
||||
and errors will still be processed as usual.
|
||||
|
||||
This is typically called when a read from the
|
||||
underlying stream object sets the error code to
|
||||
`boost::asio::error::eof`.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
put_eof(error_code& ec);
|
||||
|
||||
private:
|
||||
inline
|
||||
Derived&
|
||||
impl()
|
||||
{
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put_from_stack(std::size_t size,
|
||||
ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
maybe_need_more(
|
||||
char const* p, std::size_t n,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
parse_start_line(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
void
|
||||
parse_start_line(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_fields(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
finish_header(
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
void
|
||||
finish_header(
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_chunk_header(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
do_field(field f,
|
||||
string_view value, error_code& ec);
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/basic_parser.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,228 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_BUFFER_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_BUFFER_BODY_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A @b Body using a caller provided buffer
|
||||
|
||||
Messages using this body type may be serialized and parsed.
|
||||
To use this class, the caller must initialize the members
|
||||
of @ref buffer_body::value_type to appropriate values before
|
||||
each call to read or write during a stream operation.
|
||||
*/
|
||||
struct buffer_body
|
||||
{
|
||||
/// The type of the body member when used in a message.
|
||||
struct value_type
|
||||
{
|
||||
/** A pointer to a contiguous area of memory of @ref size octets, else `nullptr`.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If this is `nullptr` and `more` is `true`, the error
|
||||
@ref error::need_buffer will be returned from @ref serializer::get
|
||||
Otherwise, the serializer will use the memory pointed to
|
||||
by `data` having `size` octets of valid storage as the
|
||||
next buffer representing the body.
|
||||
|
||||
@par When Parsing
|
||||
|
||||
If this is `nullptr`, the error @ref error::need_buffer
|
||||
will be returned from @ref parser::put. Otherwise, the
|
||||
parser will store body octets into the memory pointed to
|
||||
by `data` having `size` octets of valid storage. After
|
||||
octets are stored, the `data` and `size` members are
|
||||
adjusted: `data` is incremented to point to the next
|
||||
octet after the data written, while `size` is decremented
|
||||
to reflect the remaining space at the memory location
|
||||
pointed to by `data`.
|
||||
*/
|
||||
void* data = nullptr;
|
||||
|
||||
/** The number of octets in the buffer pointed to by @ref data.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If `data` is `nullptr` during serialization, this value
|
||||
is ignored. Otherwise, it represents the number of valid
|
||||
body octets pointed to by `data`.
|
||||
|
||||
@par When Parsing
|
||||
|
||||
The value of this field will be decremented during parsing
|
||||
to indicate the number of remaining free octets in the
|
||||
buffer pointed to by `data`. When it reaches zero, the
|
||||
parser will return @ref error::need_buffer, indicating to
|
||||
the caller that the values of `data` and `size` should be
|
||||
updated to point to a new memory buffer.
|
||||
*/
|
||||
std::size_t size = 0;
|
||||
|
||||
/** `true` if this is not the last buffer.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If this is `true` and `data` is `nullptr`, the error
|
||||
@ref error::need_buffer will be returned from @ref serializer::get
|
||||
|
||||
@par When Parsing
|
||||
|
||||
This field is not used during parsing.
|
||||
*/
|
||||
bool more = true;
|
||||
};
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
bool toggle_ = false;
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
buffer_body, Fields> const& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<
|
||||
std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
if(toggle_)
|
||||
{
|
||||
if(body_.more)
|
||||
{
|
||||
toggle_ = false;
|
||||
ec = error::need_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
if(body_.data)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
toggle_ = true;
|
||||
return {{const_buffers_type{
|
||||
body_.data, body_.size}, body_.more}};
|
||||
}
|
||||
if(body_.more)
|
||||
ec = error::need_buffer;
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, buffer_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
if(! body_.data)
|
||||
{
|
||||
ec = error::need_buffer;
|
||||
return 0;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
buffer_copy(boost::asio::buffer(
|
||||
body_.data, body_.size), buffers);
|
||||
body_.data = reinterpret_cast<char*>(
|
||||
body_.data) + bytes_transferred;
|
||||
body_.size -= bytes_transferred;
|
||||
if(bytes_transferred == buffer_size(buffers))
|
||||
ec.assign(0, ec.category());
|
||||
else
|
||||
ec = error::need_buffer;
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
// operator<< is not supported for buffer_body
|
||||
template<bool isRequest, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, message<isRequest,
|
||||
buffer_body, Fields> const& msg) = delete;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,736 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
#define BOOST_BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/buffer_cat.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/http/detail/chunk_encode.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A chunked encoding crlf
|
||||
|
||||
This implements a @b ConstBufferSequence holding the CRLF
|
||||
(`"\r\n"`) used as a delimiter in a @em chunk.
|
||||
|
||||
To use this class, pass an instance of it to a
|
||||
stream algorithm as the buffer sequence:
|
||||
@code
|
||||
// writes "\r\n"
|
||||
boost::asio::write(stream, chunk_crlf{});
|
||||
@endcode
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
struct chunk_crlf
|
||||
{
|
||||
/// Constructor
|
||||
chunk_crlf() = default;
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = implementation_defined;
|
||||
#else
|
||||
using value_type = detail::chunk_crlf_iter::value_type;
|
||||
#endif
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
chunk_crlf(chunk_crlf const&) = default;
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return &detail::chunk_crlf_iter::value;
|
||||
}
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A @em chunk header
|
||||
|
||||
This implements a @b ConstBufferSequence representing the
|
||||
header of a @em chunk. The serialized format is as follows:
|
||||
@code
|
||||
chunk-header = 1*HEXDIG chunk-ext CRLF
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
@endcode
|
||||
The chunk extension is optional. After the header and
|
||||
chunk body have been serialized, it is the callers
|
||||
responsibility to also serialize the final CRLF (`"\r\n"`).
|
||||
|
||||
This class allows the caller to emit piecewise chunk bodies,
|
||||
by first serializing the chunk header using this class and then
|
||||
serializing the chunk body in a series of one or more calls to
|
||||
a stream write operation.
|
||||
|
||||
To use this class, pass an instance of it to a
|
||||
stream algorithm as the buffer sequence:
|
||||
@code
|
||||
// writes "400;x\r\n"
|
||||
boost::asio::write(stream, chunk_header{1024, "x"});
|
||||
@endcode
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
class chunk_header
|
||||
{
|
||||
using view_type = buffer_cat_view<
|
||||
detail::chunk_size, // chunk-size
|
||||
boost::asio::const_buffers_1, // chunk-extensions
|
||||
chunk_crlf>; // CRLF
|
||||
|
||||
std::shared_ptr<
|
||||
detail::chunk_extensions> exts_;
|
||||
view_type view_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
This constructs a buffer sequence representing a
|
||||
@em chunked-body size and terminating CRLF (`"\r\n"`)
|
||||
with no chunk extensions.
|
||||
|
||||
@param size The size of the chunk body that follows.
|
||||
The value must be greater than zero.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
explicit
|
||||
chunk_header(std::size_t size);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs a buffer sequence representing a
|
||||
@em chunked-body size and terminating CRLF (`"\r\n"`)
|
||||
with provided chunk extensions.
|
||||
|
||||
@param size The size of the chunk body that follows.
|
||||
The value must be greater than zero.
|
||||
|
||||
@param extensions The chunk extensions string. This
|
||||
string must be formatted correctly as per rfc7230,
|
||||
using this BNF syntax:
|
||||
@code
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
@endcode
|
||||
The data pointed to by this string view must remain
|
||||
valid for the lifetime of any operations performed on
|
||||
the object.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1.1
|
||||
*/
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
string_view extensions);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs a buffer sequence representing a
|
||||
@em chunked-body size and terminating CRLF (`"\r\n"`)
|
||||
with provided chunk extensions.
|
||||
The default allocator is used to provide storage for the
|
||||
extensions object.
|
||||
|
||||
@param size The size of the chunk body that follows.
|
||||
The value must be greater than zero.
|
||||
|
||||
@param extensions The chunk extensions object. The expression
|
||||
`extensions.str()` must be valid, and the return type must
|
||||
be convertible to @ref string_view. This object will be copied
|
||||
or moved as needed to ensure that the chunk header object retains
|
||||
ownership of the buffers provided by the chunk extensions object.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if @b ChunkExtensions meets the requirements stated above.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ChunkExtensions
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
detail::is_chunk_extensions<
|
||||
ChunkExtensions>::value>::type
|
||||
#endif
|
||||
>
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs a buffer sequence representing a
|
||||
@em chunked-body size and terminating CRLF (`"\r\n"`)
|
||||
with provided chunk extensions.
|
||||
The specified allocator is used to provide storage for the
|
||||
extensions object.
|
||||
|
||||
@param size The size of the chunk body that follows.
|
||||
The value be greater than zero.
|
||||
|
||||
@param extensions The chunk extensions object. The expression
|
||||
`extensions.str()` must be valid, and the return type must
|
||||
be convertible to @ref string_view. This object will be copied
|
||||
or moved as needed to ensure that the chunk header object retains
|
||||
ownership of the buffers provided by the chunk extensions object.
|
||||
|
||||
@param allocator The allocator to provide storage for the moved
|
||||
or copied extensions object.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if @b ChunkExtensions meets the requirements stated above.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ChunkExtensions, class Allocator
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
detail::is_chunk_extensions<
|
||||
ChunkExtensions>::value>::type
|
||||
#endif
|
||||
>
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator);
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = implementation_defined;
|
||||
#else
|
||||
using value_type = typename view_type::value_type;
|
||||
#endif
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
using const_iterator = typename view_type::const_iterator;
|
||||
#endif
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
chunk_header(chunk_header const&) = default;
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return view_.begin();
|
||||
}
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return view_.end();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A @em chunk
|
||||
|
||||
This implements a @b ConstBufferSequence representing
|
||||
a @em chunk. The serialized format is as follows:
|
||||
@code
|
||||
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
|
||||
chunk-size = 1*HEXDIG
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
chunk-data = 1*OCTET ; a sequence of chunk-size octets
|
||||
@endcode
|
||||
The chunk extension is optional.
|
||||
|
||||
To use this class, pass an instance of it to a
|
||||
stream algorithm as the buffer sequence.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
class chunk_body
|
||||
{
|
||||
using view_type = buffer_cat_view<
|
||||
detail::chunk_size, // chunk-size
|
||||
boost::asio::const_buffers_1, // chunk-extensions
|
||||
chunk_crlf, // CRLF
|
||||
ConstBufferSequence, // chunk-body
|
||||
chunk_crlf>; // CRLF
|
||||
|
||||
std::shared_ptr<
|
||||
detail::chunk_extensions> exts_;
|
||||
view_type view_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
This constructs buffers representing a complete @em chunk
|
||||
with no chunk extensions and having the size and contents
|
||||
of the specified buffer sequence.
|
||||
|
||||
@param buffers A buffer sequence representing the chunk
|
||||
body. Although the buffers object may be copied as necessary,
|
||||
ownership of the underlying memory blocks is retained by the
|
||||
caller, which must guarantee that they remain valid while this
|
||||
object is in use.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
explicit
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs buffers representing a complete @em chunk
|
||||
with the passed chunk extensions and having the size and
|
||||
contents of the specified buffer sequence.
|
||||
|
||||
@param buffers A buffer sequence representing the chunk
|
||||
body. Although the buffers object may be copied as necessary,
|
||||
ownership of the underlying memory blocks is retained by the
|
||||
caller, which must guarantee that they remain valid while this
|
||||
object is in use.
|
||||
|
||||
@param extensions The chunk extensions string. This
|
||||
string must be formatted correctly as per rfc7230,
|
||||
using this BNF syntax:
|
||||
@code
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
@endcode
|
||||
The data pointed to by this string view must remain
|
||||
valid for the lifetime of any operations performed on
|
||||
the object.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1.1
|
||||
*/
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
string_view extensions);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs buffers representing a complete @em chunk
|
||||
with the passed chunk extensions and having the size and
|
||||
contents of the specified buffer sequence.
|
||||
The default allocator is used to provide storage for the
|
||||
extensions object.
|
||||
|
||||
@param buffers A buffer sequence representing the chunk
|
||||
body. Although the buffers object may be copied as necessary,
|
||||
ownership of the underlying memory blocks is retained by the
|
||||
caller, which must guarantee that they remain valid while this
|
||||
object is in use.
|
||||
|
||||
@param extensions The chunk extensions object. The expression
|
||||
`extensions.str()` must be valid, and the return type must
|
||||
be convertible to @ref string_view. This object will be copied
|
||||
or moved as needed to ensure that the chunk header object retains
|
||||
ownership of the buffers provided by the chunk extensions object.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if @b ChunkExtensions meets the requirements stated above.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ChunkExtensions
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
! std::is_convertible<typename std::decay<
|
||||
ChunkExtensions>::type, string_view>::value>::type
|
||||
#endif
|
||||
>
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs buffers representing a complete @em chunk
|
||||
with the passed chunk extensions and having the size and
|
||||
contents of the specified buffer sequence.
|
||||
The specified allocator is used to provide storage for the
|
||||
extensions object.
|
||||
|
||||
@param buffers A buffer sequence representing the chunk
|
||||
body. Although the buffers object may be copied as necessary,
|
||||
ownership of the underlying memory blocks is retained by the
|
||||
caller, which must guarantee that they remain valid while this
|
||||
object is in use.
|
||||
|
||||
@param extensions The chunk extensions object. The expression
|
||||
`extensions.str()` must be valid, and the return type must
|
||||
be convertible to @ref string_view. This object will be copied
|
||||
or moved as needed to ensure that the chunk header object retains
|
||||
ownership of the buffers provided by the chunk extensions object.
|
||||
|
||||
@param allocator The allocator to provide storage for the moved
|
||||
or copied extensions object.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if @b ChunkExtensions meets the requirements stated above.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ChunkExtensions, class Allocator
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
! std::is_convertible<typename std::decay<
|
||||
ChunkExtensions>::type, string_view>::value>::type
|
||||
#endif
|
||||
>
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator);
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = implementation_defined;
|
||||
#else
|
||||
using value_type = typename view_type::value_type;
|
||||
#endif
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
using const_iterator = typename view_type::const_iterator;
|
||||
#endif
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return view_.begin();
|
||||
}
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return view_.end();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A chunked-encoding last chunk
|
||||
*/
|
||||
template<class Trailer = chunk_crlf>
|
||||
class chunk_last
|
||||
{
|
||||
static_assert(
|
||||
is_fields<Trailer>::value ||
|
||||
is_const_buffer_sequence<Trailer>::value,
|
||||
"Trailer requirements not met");
|
||||
|
||||
using buffers_type = typename
|
||||
detail::buffers_or_fields<Trailer>::type;
|
||||
|
||||
using view_type =
|
||||
buffer_cat_view<
|
||||
detail::chunk_size0, // "0\r\n"
|
||||
buffers_type>; // Trailer (includes CRLF)
|
||||
|
||||
template<class Allocator>
|
||||
buffers_type
|
||||
prepare(Trailer const& trailer, Allocator const& alloc);
|
||||
|
||||
buffers_type
|
||||
prepare(Trailer const& trailer, std::true_type);
|
||||
|
||||
buffers_type
|
||||
prepare(Trailer const& trailer, std::false_type);
|
||||
|
||||
std::shared_ptr<void> sp_;
|
||||
view_type view_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
The last chunk will have an empty trailer
|
||||
*/
|
||||
chunk_last();
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param trailer The trailer to use. This may be
|
||||
a type meeting the requirements of either Fields
|
||||
or ConstBufferSequence. If it is a ConstBufferSequence,
|
||||
the trailer must be formatted correctly as per rfc7230
|
||||
including a CRLF on its own line to denote the end
|
||||
of the trailer.
|
||||
*/
|
||||
explicit
|
||||
chunk_last(Trailer const& trailer);
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param trailer The trailer to use. This type must
|
||||
meet the requirements of Fields.
|
||||
|
||||
@param allocator The allocator to use for storing temporary
|
||||
data associated with the serialized trailer buffers.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class Allocator>
|
||||
chunk_last(Trailer const& trailer, Allocator const& allocator);
|
||||
#else
|
||||
template<class DeducedTrailer, class Allocator,
|
||||
class = typename std::enable_if<
|
||||
is_fields<DeducedTrailer>::value>::type>
|
||||
chunk_last(
|
||||
DeducedTrailer const& trailer, Allocator const& allocator);
|
||||
#endif
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
chunk_last(chunk_last const&) = default;
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = implementation_defined;
|
||||
#else
|
||||
using value_type =
|
||||
typename view_type::value_type;
|
||||
#endif
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
using const_iterator =
|
||||
typename view_type::const_iterator;
|
||||
#endif
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return view_.begin();
|
||||
}
|
||||
|
||||
/// Required for @b ConstBufferSequence
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return view_.end();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A set of chunk extensions
|
||||
|
||||
This container stores a set of chunk extensions suited for use with
|
||||
@ref chunk_header and @ref chunk_body. The container may be iterated
|
||||
to access the extensions in their structured form.
|
||||
|
||||
Meets the requirements of ChunkExtensions
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_chunk_extensions
|
||||
{
|
||||
std::basic_string<char,
|
||||
std::char_traits<char>, Allocator> s_;
|
||||
|
||||
std::basic_string<char,
|
||||
std::char_traits<char>, Allocator> range_;
|
||||
|
||||
template<class FwdIt>
|
||||
FwdIt
|
||||
do_parse(FwdIt it, FwdIt last, error_code& ec);
|
||||
|
||||
void
|
||||
do_insert(string_view name, string_view value);
|
||||
|
||||
public:
|
||||
/** The type of value when iterating.
|
||||
|
||||
The first element of the pair is the name, and the second
|
||||
element is the value which may be empty. The value is
|
||||
stored in its raw representation, without quotes or escapes.
|
||||
*/
|
||||
using value_type = std::pair<string_view, string_view>;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
/// Constructor
|
||||
basic_chunk_extensions() = default;
|
||||
|
||||
/// Constructor
|
||||
basic_chunk_extensions(basic_chunk_extensions&&) = default;
|
||||
|
||||
/// Constructor
|
||||
basic_chunk_extensions(basic_chunk_extensions const&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param allocator The allocator to use for storing the serialized extension
|
||||
*/
|
||||
explicit
|
||||
basic_chunk_extensions(Allocator const& allocator)
|
||||
: s_(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
/** Clear the chunk extensions
|
||||
|
||||
This preserves the capacity of the internal string
|
||||
used to hold the serialized representation.
|
||||
*/
|
||||
void
|
||||
clear()
|
||||
{
|
||||
s_.clear();
|
||||
}
|
||||
|
||||
/** Parse a set of chunk extensions
|
||||
|
||||
Any previous extensions will be cleared
|
||||
*/
|
||||
void
|
||||
parse(string_view s, error_code& ec);
|
||||
|
||||
/** Insert an extension name with an empty value
|
||||
|
||||
@param name The name of the extension
|
||||
*/
|
||||
void
|
||||
insert(string_view name);
|
||||
|
||||
/** Insert an extension value
|
||||
|
||||
@param name The name of the extension
|
||||
|
||||
@param value The value to insert. Depending on the
|
||||
contents, the serialized extension may use a quoted string.
|
||||
*/
|
||||
void
|
||||
insert(string_view name, string_view value);
|
||||
|
||||
/// Return the serialized representation of the chunk extension
|
||||
string_view
|
||||
str() const
|
||||
{
|
||||
return s_;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// A set of chunk extensions
|
||||
using chunk_extensions =
|
||||
basic_chunk_extensions<std::allocator<char>>;
|
||||
|
||||
/** Returns a @ref chunk_body
|
||||
|
||||
This functions constructs and returns a complete
|
||||
@ref chunk_body for a chunk body represented by the
|
||||
specified buffer sequence.
|
||||
|
||||
@param buffers The buffers representing the chunk body.
|
||||
|
||||
@param args Optional arguments passed to the @ref chunk_body constructor.
|
||||
|
||||
@note This function is provided as a notational convenience
|
||||
to omit specification of the class template arguments.
|
||||
*/
|
||||
template<class ConstBufferSequence, class... Args>
|
||||
auto
|
||||
make_chunk(
|
||||
ConstBufferSequence const& buffers,
|
||||
Args&&... args) ->
|
||||
chunk_body<ConstBufferSequence>
|
||||
{
|
||||
return chunk_body<ConstBufferSequence>(
|
||||
buffers, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/** Returns a @ref chunk_last
|
||||
|
||||
@note This function is provided as a notational convenience
|
||||
to omit specification of the class template arguments.
|
||||
*/
|
||||
inline
|
||||
chunk_last<chunk_crlf>
|
||||
make_chunk_last()
|
||||
{
|
||||
return chunk_last<chunk_crlf>{};
|
||||
}
|
||||
|
||||
/** Returns a @ref chunk_last
|
||||
|
||||
This function construct and returns a complete
|
||||
@ref chunk_last for a last chunk containing the
|
||||
specified trailers.
|
||||
|
||||
@param trailer A ConstBufferSequence or
|
||||
@note This function is provided as a notational convenience
|
||||
to omit specification of the class template arguments.
|
||||
|
||||
@param args Optional arguments passed to the @ref chunk_last
|
||||
constructor.
|
||||
*/
|
||||
template<class Trailer, class... Args>
|
||||
chunk_last<Trailer>
|
||||
make_chunk_last(
|
||||
Trailer const& trailer,
|
||||
Args&&... args)
|
||||
{
|
||||
return chunk_last<Trailer>{
|
||||
trailer, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/chunk_encode.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,198 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
/** A list parser which presents the sequence as a container.
|
||||
*/
|
||||
template<class Policy>
|
||||
class basic_parsed_list
|
||||
{
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/// The type of policy this list uses for parsing.
|
||||
using policy_type = Policy;
|
||||
|
||||
/// The type of each element in the list.
|
||||
using value_type = typename Policy::value_type;
|
||||
|
||||
/// A constant iterator to a list element.
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
class const_iterator
|
||||
: private beast::detail::
|
||||
empty_base_optimization<Policy>
|
||||
{
|
||||
basic_parsed_list const* list_ = nullptr;
|
||||
char const* it_ = nullptr;
|
||||
typename Policy::value_type v_;
|
||||
bool error_ = false;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename Policy::value_type;
|
||||
using reference = value_type const&;
|
||||
using pointer = value_type const*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.list_ == list_ &&
|
||||
other.it_ == it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool
|
||||
error() const
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parsed_list;
|
||||
|
||||
const_iterator(
|
||||
basic_parsed_list const& list, bool at_end)
|
||||
: list_(&list)
|
||||
, it_(at_end ? nullptr :
|
||||
list.s_.begin())
|
||||
{
|
||||
if(! at_end)
|
||||
increment();
|
||||
}
|
||||
|
||||
void
|
||||
increment()
|
||||
{
|
||||
if(! this->member()(
|
||||
v_, it_, list_->s_))
|
||||
{
|
||||
it_ = nullptr;
|
||||
error_ = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Construct a list from a string
|
||||
explicit
|
||||
basic_parsed_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
};
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,896 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
|
||||
#include <boost/beast/core/static_string.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/detail/cpu_info.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class basic_parser_base
|
||||
{
|
||||
protected:
|
||||
// limit on the size of the obs-fold buffer
|
||||
//
|
||||
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
||||
//
|
||||
static std::size_t constexpr max_obs_fold = 4096;
|
||||
|
||||
enum class state
|
||||
{
|
||||
nothing_yet = 0,
|
||||
start_line,
|
||||
fields,
|
||||
body0,
|
||||
body,
|
||||
body_to_eof0,
|
||||
body_to_eof,
|
||||
chunk_header0,
|
||||
chunk_header,
|
||||
chunk_body,
|
||||
complete
|
||||
};
|
||||
|
||||
static
|
||||
bool
|
||||
is_pathchar(char c)
|
||||
{
|
||||
// VFALCO This looks the same as the one below...
|
||||
|
||||
// TEXT = <any OCTET except CTLs, and excluding LWS>
|
||||
static bool constexpr tab[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
unhex(unsigned char& d, char c)
|
||||
{
|
||||
static signed char constexpr tab[256] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 112
|
||||
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 128
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 144
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 160
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 176
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 192
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 208
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 224
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240
|
||||
};
|
||||
d = static_cast<unsigned char>(
|
||||
tab[static_cast<unsigned char>(c)]);
|
||||
return d != static_cast<unsigned char>(-1);
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_digit(char c)
|
||||
{
|
||||
return static_cast<unsigned char>(c-'0') < 10;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_print(char c)
|
||||
{
|
||||
return static_cast<unsigned char>(c-32) < 95;
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
static
|
||||
FwdIt
|
||||
trim_front(FwdIt it, FwdIt const& end)
|
||||
{
|
||||
while(it != end)
|
||||
{
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
template<class RanIt>
|
||||
static
|
||||
RanIt
|
||||
trim_back(
|
||||
RanIt it, RanIt const& first)
|
||||
{
|
||||
while(it != first)
|
||||
{
|
||||
auto const c = it[-1];
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
--it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
static
|
||||
string_view
|
||||
make_string(char const* first, char const* last)
|
||||
{
|
||||
return {first, static_cast<
|
||||
std::size_t>(last - first)};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static
|
||||
std::pair<char const*, bool>
|
||||
find_fast(
|
||||
char const* buf,
|
||||
char const* buf_end,
|
||||
char const* ranges,
|
||||
size_t ranges_size)
|
||||
{
|
||||
bool found = false;
|
||||
boost::ignore_unused(buf_end, ranges, ranges_size);
|
||||
return {buf, found};
|
||||
}
|
||||
|
||||
// VFALCO Can SIMD help this?
|
||||
static
|
||||
char const*
|
||||
find_eol(
|
||||
char const* it, char const* last,
|
||||
error_code& ec)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return nullptr;
|
||||
}
|
||||
if(*it == '\r')
|
||||
{
|
||||
if(++it == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return nullptr;
|
||||
}
|
||||
if(*it != '\n')
|
||||
{
|
||||
ec = error::bad_line_ending;
|
||||
return nullptr;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return ++it;
|
||||
}
|
||||
// VFALCO Should we handle the legacy case
|
||||
// for lines terminated with a single '\n'?
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
char const*
|
||||
find_eom(char const* p, char const* last)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(p + 4 > last)
|
||||
return nullptr;
|
||||
if(p[3] != '\n')
|
||||
{
|
||||
if(p[3] == '\r')
|
||||
++p;
|
||||
else
|
||||
p += 4;
|
||||
}
|
||||
else if(p[2] != '\r')
|
||||
{
|
||||
p += 4;
|
||||
}
|
||||
else if(p[1] != '\n')
|
||||
{
|
||||
p += 2;
|
||||
}
|
||||
else if(p[0] != '\r')
|
||||
{
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return p + 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static
|
||||
char const*
|
||||
parse_token_to_eol(
|
||||
char const* p,
|
||||
char const* last,
|
||||
char const*& token_last,
|
||||
error_code& ec)
|
||||
{
|
||||
for(;; ++p)
|
||||
{
|
||||
if(p >= last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return p;
|
||||
}
|
||||
if(BOOST_UNLIKELY(! is_print(*p)))
|
||||
if((BOOST_LIKELY(static_cast<
|
||||
unsigned char>(*p) < '\040') &&
|
||||
BOOST_LIKELY(*p != '\011')) ||
|
||||
BOOST_UNLIKELY(*p == '\177'))
|
||||
goto found_control;
|
||||
}
|
||||
found_control:
|
||||
if(BOOST_LIKELY(*p == '\r'))
|
||||
{
|
||||
if(++p >= last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return last;
|
||||
}
|
||||
if(*p++ != '\n')
|
||||
{
|
||||
ec = error::bad_line_ending;
|
||||
return last;
|
||||
}
|
||||
token_last = p - 2;
|
||||
}
|
||||
#if 0
|
||||
// VFALCO This allows `\n` by itself
|
||||
// to terminate a line
|
||||
else if(*p == '\n')
|
||||
{
|
||||
token_last = p;
|
||||
++p;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// invalid character
|
||||
return nullptr;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
template<class Iter, class Unsigned>
|
||||
static
|
||||
bool
|
||||
parse_dec(Iter it, Iter last, Unsigned& v)
|
||||
{
|
||||
if(! is_digit(*it))
|
||||
return false;
|
||||
v = *it - '0';
|
||||
for(;;)
|
||||
{
|
||||
if(! is_digit(*++it))
|
||||
break;
|
||||
auto const d = *it - '0';
|
||||
if(v > ((std::numeric_limits<
|
||||
Unsigned>::max)() - 10) / 10)
|
||||
return false;
|
||||
v = 10 * v + d;
|
||||
}
|
||||
return it == last;
|
||||
}
|
||||
|
||||
template<class Iter, class Unsigned>
|
||||
static
|
||||
bool
|
||||
parse_hex(Iter& it, Unsigned& v)
|
||||
{
|
||||
unsigned char d;
|
||||
if(! unhex(d, *it))
|
||||
return false;
|
||||
v = d;
|
||||
for(;;)
|
||||
{
|
||||
if(! unhex(d, *++it))
|
||||
break;
|
||||
auto const v0 = v;
|
||||
v = 16 * v + d;
|
||||
if(v < v0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
parse_crlf(char const*& it)
|
||||
{
|
||||
if( it[0] != '\r' || it[1] != '\n')
|
||||
return false;
|
||||
it += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse_method(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
// parse token SP
|
||||
auto const first = it;
|
||||
for(;; ++it)
|
||||
{
|
||||
if(it + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
if(it + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ')
|
||||
{
|
||||
ec = error::bad_method;
|
||||
return;
|
||||
}
|
||||
if(it == first)
|
||||
{
|
||||
// cannot be empty
|
||||
ec = error::bad_method;
|
||||
return;
|
||||
}
|
||||
result = make_string(first, it++);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse_target(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
// parse target SP
|
||||
auto const first = it;
|
||||
for(;; ++it)
|
||||
{
|
||||
if(it + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! is_pathchar(*it))
|
||||
break;
|
||||
}
|
||||
if(it + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ')
|
||||
{
|
||||
ec = error::bad_target;
|
||||
return;
|
||||
}
|
||||
if(it == first)
|
||||
{
|
||||
// cannot be empty
|
||||
ec = error::bad_target;
|
||||
return;
|
||||
}
|
||||
result = make_string(first, it++);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse_version(
|
||||
char const*& it, char const* last,
|
||||
int& result, error_code& ec)
|
||||
{
|
||||
if(it + 8 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'H')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'T')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'T')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'P')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(*it++ != '/')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
result = 10 * (*it++ - '0');
|
||||
if(*it++ != '.')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
result += *it++ - '0';
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse_status(
|
||||
char const*& it, char const* last,
|
||||
unsigned short& result, error_code& ec)
|
||||
{
|
||||
// parse 3(digit) SP
|
||||
if(it + 4 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_status;
|
||||
return;
|
||||
}
|
||||
result = 100 * (*it++ - '0');
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_status;
|
||||
return;
|
||||
}
|
||||
result += 10 * (*it++ - '0');
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_status;
|
||||
return;
|
||||
}
|
||||
result += *it++ - '0';
|
||||
if(*it++ != ' ')
|
||||
{
|
||||
ec = error::bad_status;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
parse_reason(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
auto const first = it;
|
||||
char const* token_last = nullptr;
|
||||
auto p = parse_token_to_eol(
|
||||
it, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
ec = error::bad_reason;
|
||||
return;
|
||||
}
|
||||
result = make_string(first, token_last);
|
||||
it = p;
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
void
|
||||
parse_field(
|
||||
char const*& p,
|
||||
char const* last,
|
||||
string_view& name,
|
||||
string_view& value,
|
||||
static_string<N>& buf,
|
||||
error_code& ec)
|
||||
{
|
||||
/* header-field = field-name ":" OWS field-value OWS
|
||||
|
||||
field-name = token
|
||||
field-value = *( field-content / obs-fold )
|
||||
field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
field-vchar = VCHAR / obs-text
|
||||
|
||||
obs-fold = CRLF 1*( SP / HTAB )
|
||||
; obsolete line folding
|
||||
; see Section 3.2.4
|
||||
|
||||
token = 1*<any CHAR except CTLs or separators>
|
||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
sep = "(" | ")" | "<" | ">" | "@"
|
||||
| "," | ";" | ":" | "\" | <">
|
||||
| "/" | "[" | "]" | "?" | "="
|
||||
| "{" | "}" | SP | HT
|
||||
*/
|
||||
static char const* is_token =
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
|
||||
"\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
|
||||
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
|
||||
// name
|
||||
BOOST_ALIGNMENT(16) static const char ranges1[] =
|
||||
"\x00 " /* control chars and up to SP */
|
||||
"\"\"" /* 0x22 */
|
||||
"()" /* 0x28,0x29 */
|
||||
",," /* 0x2c */
|
||||
"//" /* 0x2f */
|
||||
":@" /* 0x3a-0x40 */
|
||||
"[]" /* 0x5b-0x5d */
|
||||
"{\377"; /* 0x7b-0xff */
|
||||
auto first = p;
|
||||
bool found;
|
||||
std::tie(p, found) = find_fast(
|
||||
p, last, ranges1, sizeof(ranges1)-1);
|
||||
if(! found && p >= last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
if(*p == ':')
|
||||
break;
|
||||
if(! is_token[static_cast<
|
||||
unsigned char>(*p)])
|
||||
{
|
||||
ec = error::bad_field;
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
if(p >= last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(p == first)
|
||||
{
|
||||
// empty name
|
||||
ec = error::bad_field;
|
||||
return;
|
||||
}
|
||||
name = make_string(first, p);
|
||||
++p; // eat ':'
|
||||
char const* token_last = nullptr;
|
||||
for(;;)
|
||||
{
|
||||
// eat leading ' ' and '\t'
|
||||
for(;;++p)
|
||||
{
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! (*p == ' ' || *p == '\t'))
|
||||
break;
|
||||
}
|
||||
// parse to CRLF
|
||||
first = p;
|
||||
p = parse_token_to_eol(p, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
ec = error::bad_value;
|
||||
return;
|
||||
}
|
||||
// Look 1 char past the CRLF to handle obs-fold.
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
token_last =
|
||||
trim_back(token_last, first);
|
||||
if(*p != ' ' && *p != '\t')
|
||||
{
|
||||
value = make_string(first, token_last);
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
if(token_last != first)
|
||||
break;
|
||||
}
|
||||
buf.resize(0);
|
||||
buf.append(first, token_last);
|
||||
BOOST_ASSERT(! buf.empty());
|
||||
try
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
// eat leading ' ' and '\t'
|
||||
for(;;++p)
|
||||
{
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! (*p == ' ' || *p == '\t'))
|
||||
break;
|
||||
}
|
||||
// parse to CRLF
|
||||
first = p;
|
||||
p = parse_token_to_eol(p, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
ec = error::bad_value;
|
||||
return;
|
||||
}
|
||||
// Look 1 char past the CRLF to handle obs-fold.
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
token_last = trim_back(token_last, first);
|
||||
if(first != token_last)
|
||||
{
|
||||
buf.push_back(' ');
|
||||
buf.append(first, token_last);
|
||||
}
|
||||
if(*p != ' ' && *p != '\t')
|
||||
{
|
||||
value = {buf.data(), buf.size()};
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
parse_chunk_extensions(
|
||||
char const*& it,
|
||||
char const* last,
|
||||
error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
|
||||
BWS = *( SP / HTAB ) ; "Bad White Space"
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
token = 1*tchar
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
|
||||
https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
|
||||
*/
|
||||
loop:
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t' && *it != ';')
|
||||
return;
|
||||
// BWS
|
||||
if(*it == ' ' || *it == '\t')
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ';'
|
||||
if(*it != ';')
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
semi:
|
||||
++it; // skip ';'
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-name
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
// BWS [ ";" / "=" ]
|
||||
{
|
||||
bool bws;
|
||||
if(*it == ' ' || *it == '\t')
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
}
|
||||
bws = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bws = false;
|
||||
}
|
||||
if(*it == ';')
|
||||
goto semi;
|
||||
if(*it != '=')
|
||||
{
|
||||
if(bws)
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
++it; // skip '='
|
||||
}
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-val
|
||||
if(*it != '"')
|
||||
{
|
||||
// token
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// quoted-string
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
goto loop;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,262 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
struct chunk_extensions
|
||||
{
|
||||
virtual ~chunk_extensions() = default;
|
||||
virtual boost::asio::const_buffers_1 str() = 0;
|
||||
};
|
||||
|
||||
template<class ChunkExtensions>
|
||||
struct chunk_extensions_impl : chunk_extensions
|
||||
{
|
||||
ChunkExtensions ext_;
|
||||
|
||||
chunk_extensions_impl(ChunkExtensions&& ext)
|
||||
: ext_(std::move(ext))
|
||||
{
|
||||
}
|
||||
|
||||
chunk_extensions_impl(ChunkExtensions const& ext)
|
||||
: ext_(ext)
|
||||
{
|
||||
}
|
||||
|
||||
boost::asio::const_buffers_1
|
||||
str() override
|
||||
{
|
||||
auto const s = ext_.str();
|
||||
return {s.data(), s.size()};
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, class = void>
|
||||
struct is_chunk_extensions : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_chunk_extensions<T, beast::detail::void_t<decltype(
|
||||
std::declval<string_view&>() = std::declval<T&>().str(),
|
||||
(void)0)>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A buffer sequence containing a chunk-encoding header
|
||||
*/
|
||||
class chunk_size
|
||||
{
|
||||
public:
|
||||
// Storage for the longest hex string we might need
|
||||
class value_type
|
||||
{
|
||||
friend class chunk_size;
|
||||
|
||||
// First byte holds the length
|
||||
char buf_[1 + 2 * sizeof(std::size_t)];
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
prepare(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:
|
||||
operator
|
||||
boost::asio::const_buffer() const
|
||||
{
|
||||
return {
|
||||
buf_ + sizeof(buf_) - buf_[0],
|
||||
static_cast<unsigned>(buf_[0])};
|
||||
}
|
||||
};
|
||||
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
chunk_size(chunk_size const& other) = default;
|
||||
|
||||
/** Construct a chunk header
|
||||
|
||||
@param n The number of octets in this chunk.
|
||||
*/
|
||||
chunk_size(std::size_t n)
|
||||
{
|
||||
value_.prepare(n);
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return &value_;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
|
||||
private:
|
||||
value_type value_;
|
||||
};
|
||||
|
||||
template<class>
|
||||
void
|
||||
chunk_size::
|
||||
value_type::
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
auto const last = &buf_[sizeof(buf_)];
|
||||
auto it = to_hex(last, n);
|
||||
buf_[0] = static_cast<char>(last - it);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Returns a buffer sequence holding a CRLF for chunk encoding
|
||||
inline
|
||||
boost::asio::const_buffers_1
|
||||
chunk_crlf()
|
||||
{
|
||||
return {"\r\n", 2};
|
||||
}
|
||||
|
||||
/// Returns a buffer sequence holding a final chunk header
|
||||
inline
|
||||
boost::asio::const_buffers_1
|
||||
chunk_last()
|
||||
{
|
||||
return {"0\r\n", 3};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class = void>
|
||||
struct chunk_crlf_iter_type
|
||||
{
|
||||
class value_type
|
||||
{
|
||||
char const s[2] = {'\r', '\n'};
|
||||
|
||||
public:
|
||||
value_type() = default;
|
||||
|
||||
operator
|
||||
boost::asio::const_buffer() const
|
||||
{
|
||||
return {s, sizeof(s)};
|
||||
}
|
||||
};
|
||||
static value_type value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
typename chunk_crlf_iter_type<T>::value_type
|
||||
chunk_crlf_iter_type<T>::value;
|
||||
|
||||
using chunk_crlf_iter = chunk_crlf_iter_type<void>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class = void>
|
||||
struct chunk_size0_iter_type
|
||||
{
|
||||
class value_type
|
||||
{
|
||||
char const s[3] = {'0', '\r', '\n'};
|
||||
|
||||
public:
|
||||
value_type() = default;
|
||||
|
||||
operator
|
||||
boost::asio::const_buffer() const
|
||||
{
|
||||
return {s, sizeof(s)};
|
||||
}
|
||||
};
|
||||
static value_type value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
typename chunk_size0_iter_type<T>::value_type
|
||||
chunk_size0_iter_type<T>::value;
|
||||
|
||||
using chunk_size0_iter = chunk_size0_iter_type<void>;
|
||||
|
||||
struct chunk_size0
|
||||
{
|
||||
using value_type = chunk_size0_iter::value_type;
|
||||
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return &chunk_size0_iter::value;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class T,
|
||||
bool = is_fields<T>::value>
|
||||
struct buffers_or_fields
|
||||
{
|
||||
using type = typename
|
||||
T::reader::const_buffers_type;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct buffers_or_fields<T, false>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,473 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
bool
|
||||
is_digit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
inline
|
||||
char
|
||||
is_alpha(char c)
|
||||
{
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
char
|
||||
is_text(char c)
|
||||
{
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
char
|
||||
is_token_char(char c)
|
||||
{
|
||||
/*
|
||||
tchar = "!" | "#" | "$" | "%" | "&" |
|
||||
"'" | "*" | "+" | "-" | "." |
|
||||
"^" | "_" | "`" | "|" | "~" |
|
||||
DIGIT | ALPHA
|
||||
*/
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
char
|
||||
is_qdchar(char c)
|
||||
{
|
||||
/*
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
*/
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
char
|
||||
is_qpchar(char c)
|
||||
{
|
||||
/*
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
*/
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
// returns 0 if not a valid text char
|
||||
//
|
||||
inline
|
||||
char
|
||||
to_value_char(char c)
|
||||
{
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
static unsigned char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
|
||||
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
|
||||
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
|
||||
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
|
||||
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
|
||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
|
||||
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
|
||||
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
|
||||
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
|
||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return static_cast<char>(tab[static_cast<unsigned char>(c)]);
|
||||
}
|
||||
|
||||
// VFALCO TODO Make this return unsigned?
|
||||
inline
|
||||
std::int8_t
|
||||
unhex(char c)
|
||||
{
|
||||
static signed char constexpr tab[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
inline
|
||||
void
|
||||
skip_ows(FwdIt& it, FwdIt const& end)
|
||||
{
|
||||
while(it != end)
|
||||
{
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template<class RanIt>
|
||||
inline
|
||||
void
|
||||
skip_ows_rev(
|
||||
RanIt& it, RanIt const& first)
|
||||
{
|
||||
while(it != first)
|
||||
{
|
||||
auto const c = it[-1];
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
--it;
|
||||
}
|
||||
}
|
||||
|
||||
// obs-fold = CRLF 1*( SP / HTAB )
|
||||
// return `false` on parse error
|
||||
//
|
||||
template<class FwdIt>
|
||||
inline
|
||||
bool
|
||||
skip_obs_fold(
|
||||
FwdIt& it, FwdIt const& last)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(*it != '\r')
|
||||
return true;
|
||||
if(++it == last)
|
||||
return false;
|
||||
if(*it != '\n')
|
||||
return false;
|
||||
if(++it == last)
|
||||
return false;
|
||||
if(*it != ' ' && *it != '\t')
|
||||
return false;
|
||||
for(;;)
|
||||
{
|
||||
if(++it == last)
|
||||
return true;
|
||||
if(*it != ' ' && *it != '\t')
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
void
|
||||
skip_token(FwdIt& it, FwdIt const& last)
|
||||
{
|
||||
while(it != last && is_token_char(*it))
|
||||
++it;
|
||||
}
|
||||
|
||||
inline
|
||||
string_view
|
||||
trim(string_view s)
|
||||
{
|
||||
auto first = s.begin();
|
||||
auto last = s.end();
|
||||
skip_ows(first, last);
|
||||
while(first != last)
|
||||
{
|
||||
auto const c = *std::prev(last);
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
--last;
|
||||
}
|
||||
if(first == last)
|
||||
return {};
|
||||
return {&*first,
|
||||
static_cast<std::size_t>(last - first)};
|
||||
}
|
||||
|
||||
struct param_iter
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
iter_type it;
|
||||
iter_type first;
|
||||
iter_type last;
|
||||
std::pair<string_view, string_view> v;
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return first == it;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
template<class>
|
||||
void
|
||||
param_iter::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS [ "=" OWS ( token / quoted-string ) ]
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it = first;
|
||||
};
|
||||
v.first.clear();
|
||||
v.second.clear();
|
||||
detail::skip_ows(it, last);
|
||||
first = it;
|
||||
if(it == last)
|
||||
return err();
|
||||
if(*it != ';')
|
||||
return err();
|
||||
++it;
|
||||
detail::skip_ows(it, last);
|
||||
if(it == last)
|
||||
return err();
|
||||
// param
|
||||
if(! detail::is_token_char(*it))
|
||||
return err();
|
||||
auto const p0 = it;
|
||||
skip_token(++it, last);
|
||||
auto const p1 = it;
|
||||
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
|
||||
detail::skip_ows(it, last);
|
||||
if(it == last)
|
||||
return;
|
||||
if(*it == ';')
|
||||
return;
|
||||
if(*it != '=')
|
||||
return err();
|
||||
++it;
|
||||
detail::skip_ows(it, last);
|
||||
if(it == last)
|
||||
return;
|
||||
if(*it == '"')
|
||||
{
|
||||
// quoted-string
|
||||
auto const p2 = it;
|
||||
++it;
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
return err();
|
||||
auto c = *it++;
|
||||
if(c == '"')
|
||||
break;
|
||||
if(detail::is_qdchar(c))
|
||||
continue;
|
||||
if(c != '\\')
|
||||
return err();
|
||||
if(it == last)
|
||||
return err();
|
||||
c = *it++;
|
||||
if(! detail::is_qpchar(c))
|
||||
return err();
|
||||
}
|
||||
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||
}
|
||||
else
|
||||
{
|
||||
// token
|
||||
if(! detail::is_token_char(*it))
|
||||
return err();
|
||||
auto const p2 = it;
|
||||
skip_token(++it, last);
|
||||
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
|
||||
*/
|
||||
struct opt_token_list_policy
|
||||
{
|
||||
using value_type = string_view;
|
||||
|
||||
bool
|
||||
operator()(value_type& v,
|
||||
char const*& it, string_view s) const
|
||||
{
|
||||
v = {};
|
||||
auto need_comma = it != s.begin();
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it, s.end());
|
||||
if(it == s.end())
|
||||
{
|
||||
it = nullptr;
|
||||
return true;
|
||||
}
|
||||
auto const c = *it;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return false;
|
||||
auto const p0 = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == s.end())
|
||||
break;
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
v = string_view{&*p0,
|
||||
static_cast<std::size_t>(it - p0)};
|
||||
return true;
|
||||
}
|
||||
if(c != ',')
|
||||
return false;
|
||||
need_comma = false;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
|
||||
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
struct header;
|
||||
|
||||
template<bool, class, class>
|
||||
struct message;
|
||||
|
||||
template<bool isRequest,class Body, class Fields>
|
||||
class parser;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class is_header_impl
|
||||
{
|
||||
template<bool b, class F>
|
||||
static std::true_type check(
|
||||
header<b, F> const*);
|
||||
static std::false_type check(...);
|
||||
public:
|
||||
using type = decltype(check((T*)0));
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using is_header = typename is_header_impl<T>::type;
|
||||
|
||||
template<class T>
|
||||
struct is_parser : std::false_type {};
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
struct is_parser<parser<isRequest, Body, Fields>> : std::true_type {};
|
||||
|
||||
struct fields_model
|
||||
{
|
||||
string_view method() const;
|
||||
string_view reason() const;
|
||||
string_view target() const;
|
||||
|
||||
protected:
|
||||
string_view get_method_impl() const;
|
||||
string_view get_target_impl() const;
|
||||
string_view get_reason_impl() const;
|
||||
bool get_chunked_impl() const;
|
||||
bool get_keep_alive_impl(unsigned) const;
|
||||
void set_method_impl(string_view);
|
||||
void set_target_impl(string_view);
|
||||
void set_reason_impl(string_view);
|
||||
void set_chunked_impl(bool);
|
||||
void set_content_length_impl(boost::optional<std::uint64_t>);
|
||||
void set_keep_alive_impl(unsigned, bool);
|
||||
};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_value_type : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_value_type<T, beast::detail::void_t<
|
||||
typename T::value_type
|
||||
> > : std::true_type {};
|
||||
|
||||
/** Determine if a @b Body type has a size
|
||||
|
||||
This metafunction is equivalent to `std::true_type` if
|
||||
Body contains a static member function called `size`.
|
||||
*/
|
||||
template<class T, class = void>
|
||||
struct is_body_sized : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_body_sized<T, beast::detail::void_t<
|
||||
typename T::value_type,
|
||||
decltype(
|
||||
std::declval<std::uint64_t&>() =
|
||||
T::size(std::declval<typename T::value_type const&>()),
|
||||
(void)0)>> : std::true_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_fields_helper : T
|
||||
{
|
||||
template<class U = is_fields_helper>
|
||||
static auto f1(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_method_impl(),
|
||||
std::true_type());
|
||||
static auto f1(...) -> std::false_type;
|
||||
using t1 = decltype(f1(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f2(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_target_impl(),
|
||||
std::true_type());
|
||||
static auto f2(...) -> std::false_type;
|
||||
using t2 = decltype(f2(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f3(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_reason_impl(),
|
||||
std::true_type());
|
||||
static auto f3(...) -> std::false_type;
|
||||
using t3 = decltype(f3(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f4(int) -> decltype(
|
||||
std::declval<bool&>() = std::declval<U const&>().get_chunked_impl(),
|
||||
std::true_type());
|
||||
static auto f4(...) -> std::false_type;
|
||||
using t4 = decltype(f4(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f5(int) -> decltype(
|
||||
std::declval<bool&>() = std::declval<U const&>().get_keep_alive_impl(
|
||||
std::declval<unsigned>()),
|
||||
std::true_type());
|
||||
static auto f5(...) -> std::false_type;
|
||||
using t5 = decltype(f5(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f6(int) -> decltype(
|
||||
void(std::declval<U&>().set_method_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f6(...) -> std::false_type;
|
||||
using t6 = decltype(f6(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f7(int) -> decltype(
|
||||
void(std::declval<U&>().set_target_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f7(...) -> std::false_type;
|
||||
using t7 = decltype(f7(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f8(int) -> decltype(
|
||||
void(std::declval<U&>().set_reason_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f8(...) -> std::false_type;
|
||||
using t8 = decltype(f8(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f9(int) -> decltype(
|
||||
void(std::declval<U&>().set_chunked_impl(std::declval<bool>())),
|
||||
std::true_type());
|
||||
static auto f9(...) -> std::false_type;
|
||||
using t9 = decltype(f9(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f10(int) -> decltype(
|
||||
void(std::declval<U&>().set_content_length_impl(
|
||||
std::declval<boost::optional<std::uint64_t>>())),
|
||||
std::true_type());
|
||||
static auto f10(...) -> std::false_type;
|
||||
using t10 = decltype(f10(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f11(int) -> decltype(
|
||||
void(std::declval<U&>().set_keep_alive_impl(
|
||||
std::declval<unsigned>(),
|
||||
std::declval<bool>())),
|
||||
std::true_type());
|
||||
static auto f11(...) -> std::false_type;
|
||||
using t11 = decltype(f11(0));
|
||||
|
||||
using type = std::integral_constant<bool,
|
||||
t1::value && t2::value && t3::value &&
|
||||
t4::value && t5::value && t6::value &&
|
||||
t7::value && t8::value && t9::value &&
|
||||
t10::value && t11::value>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DYNAMIC_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_DYNAMIC_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/http/basic_dynamic_body.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A dynamic message body represented by a @ref multi_buffer
|
||||
|
||||
Meets the requirements of @b Body.
|
||||
*/
|
||||
using dynamic_body = basic_dynamic_body<multi_buffer>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_EMPTY_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_EMPTY_BODY_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** An empty @b Body
|
||||
|
||||
This body is used to represent messages which do not have a
|
||||
message body. If this body is used with a parser, and the
|
||||
parser encounters octets corresponding to a message body,
|
||||
the parser will fail with the error @ref http::unexpected_body.
|
||||
|
||||
The Content-Length of this body is always 0.
|
||||
*/
|
||||
struct empty_body
|
||||
{
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
struct value_type
|
||||
{
|
||||
};
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
struct reader
|
||||
{
|
||||
using const_buffers_type =
|
||||
boost::asio::null_buffers;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
empty_body, Fields> const&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
struct writer
|
||||
{
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, empty_body, Fields>&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const&,
|
||||
error_code& ec)
|
||||
{
|
||||
ec = error::unexpected_body;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_ERROR_HPP
|
||||
#define BOOST_BEAST_HTTP_ERROR_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// Error codes returned from HTTP algorithms and operations.
|
||||
enum class error
|
||||
{
|
||||
/** The end of the stream was reached.
|
||||
|
||||
This error is returned under the following conditions:
|
||||
|
||||
@li When attempting to read HTTP data from a stream and the stream
|
||||
read returns the error `boost::asio::error::eof` before any new octets
|
||||
have been received.
|
||||
|
||||
@li When sending a complete HTTP message at once and the semantics of
|
||||
the message are that the connection should be closed to indicate the
|
||||
end of the message.
|
||||
*/
|
||||
end_of_stream = 1,
|
||||
|
||||
/** The incoming message is incomplete.
|
||||
|
||||
This happens when the end of stream is reached during
|
||||
parsing and some octets have been received, but not the
|
||||
entire message.
|
||||
*/
|
||||
partial_message,
|
||||
|
||||
/** Additional buffers are required.
|
||||
|
||||
This error is returned during parsing when additional
|
||||
octets are needed. The caller should append more data
|
||||
to the existing buffer and retry the parse operaetion.
|
||||
*/
|
||||
need_more,
|
||||
|
||||
/** An unexpected body was encountered during parsing.
|
||||
|
||||
This error is returned when attempting to parse body
|
||||
octets into a message container which has the
|
||||
@ref empty_body body type.
|
||||
|
||||
@see @ref empty_body
|
||||
*/
|
||||
unexpected_body,
|
||||
|
||||
/** Additional buffers are required.
|
||||
|
||||
This error is returned under the following conditions:
|
||||
|
||||
@li During serialization when using @ref buffer_body.
|
||||
The caller should update the body to point to a new
|
||||
buffer or indicate that there are no more octets in
|
||||
the body.
|
||||
|
||||
@li During parsing when using @ref buffer_body.
|
||||
The caller should update the body to point to a new
|
||||
storage area to receive additional body octets.
|
||||
*/
|
||||
need_buffer,
|
||||
|
||||
/** The end of a chunk was reached
|
||||
*/
|
||||
end_of_chunk,
|
||||
|
||||
/** Buffer maximum exceeded.
|
||||
|
||||
This error is returned when reading HTTP content
|
||||
into a dynamic buffer, and the operation would
|
||||
exceed the maximum size of the buffer.
|
||||
*/
|
||||
buffer_overflow,
|
||||
|
||||
/** Header limit exceeded.
|
||||
|
||||
The parser detected an incoming message header which
|
||||
exceeded a configured limit.
|
||||
*/
|
||||
header_limit,
|
||||
|
||||
/** Body limit exceeded.
|
||||
|
||||
The parser detected an incoming message body which
|
||||
exceeded a configured limit.
|
||||
*/
|
||||
body_limit,
|
||||
|
||||
/** A memory allocation failed.
|
||||
|
||||
When basic_fields throws std::bad_alloc, it is
|
||||
converted into this error by @ref parser.
|
||||
*/
|
||||
bad_alloc,
|
||||
|
||||
//
|
||||
// (parser errors)
|
||||
//
|
||||
|
||||
/// The line ending was malformed
|
||||
bad_line_ending,
|
||||
|
||||
/// The method is invalid.
|
||||
bad_method,
|
||||
|
||||
/// The request-target is invalid.
|
||||
bad_target,
|
||||
|
||||
/// The HTTP-version is invalid.
|
||||
bad_version,
|
||||
|
||||
/// The status-code is invalid.
|
||||
bad_status,
|
||||
|
||||
/// The reason-phrase is invalid.
|
||||
bad_reason,
|
||||
|
||||
/// The field name is invalid.
|
||||
bad_field,
|
||||
|
||||
/// The field value is invalid.
|
||||
bad_value,
|
||||
|
||||
/// The Content-Length is invalid.
|
||||
bad_content_length,
|
||||
|
||||
/// The Transfer-Encoding is invalid.
|
||||
bad_transfer_encoding,
|
||||
|
||||
/// The chunk syntax is invalid.
|
||||
bad_chunk,
|
||||
|
||||
/// The chunk extension is invalid.
|
||||
bad_chunk_extension,
|
||||
|
||||
/// An obs-fold exceeded an internal limit.
|
||||
bad_obs_fold
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/error.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,409 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_FIELD_HPP
|
||||
#define BOOST_BEAST_HTTP_FIELD_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class field : unsigned short
|
||||
{
|
||||
unknown = 0,
|
||||
|
||||
a_im,
|
||||
accept,
|
||||
accept_additions,
|
||||
accept_charset,
|
||||
accept_datetime,
|
||||
accept_encoding,
|
||||
accept_features,
|
||||
accept_language,
|
||||
accept_patch,
|
||||
accept_post,
|
||||
accept_ranges,
|
||||
access_control,
|
||||
access_control_allow_credentials,
|
||||
access_control_allow_headers,
|
||||
access_control_allow_methods,
|
||||
access_control_allow_origin,
|
||||
access_control_max_age,
|
||||
access_control_request_headers,
|
||||
access_control_request_method,
|
||||
age,
|
||||
allow,
|
||||
alpn,
|
||||
also_control,
|
||||
alt_svc,
|
||||
alt_used,
|
||||
alternate_recipient,
|
||||
alternates,
|
||||
apparently_to,
|
||||
apply_to_redirect_ref,
|
||||
approved,
|
||||
archive,
|
||||
archived_at,
|
||||
article_names,
|
||||
article_updates,
|
||||
authentication_control,
|
||||
authentication_info,
|
||||
authentication_results,
|
||||
authorization,
|
||||
auto_submitted,
|
||||
autoforwarded,
|
||||
autosubmitted,
|
||||
base,
|
||||
bcc,
|
||||
body,
|
||||
c_ext,
|
||||
c_man,
|
||||
c_opt,
|
||||
c_pep,
|
||||
c_pep_info,
|
||||
cache_control,
|
||||
caldav_timezones,
|
||||
cancel_key,
|
||||
cancel_lock,
|
||||
cc,
|
||||
close,
|
||||
comments,
|
||||
compliance,
|
||||
connection,
|
||||
content_alternative,
|
||||
content_base,
|
||||
content_description,
|
||||
content_disposition,
|
||||
content_duration,
|
||||
content_encoding,
|
||||
content_features,
|
||||
content_id,
|
||||
content_identifier,
|
||||
content_language,
|
||||
content_length,
|
||||
content_location,
|
||||
content_md5,
|
||||
content_range,
|
||||
content_return,
|
||||
content_script_type,
|
||||
content_style_type,
|
||||
content_transfer_encoding,
|
||||
content_type,
|
||||
content_version,
|
||||
control,
|
||||
conversion,
|
||||
conversion_with_loss,
|
||||
cookie,
|
||||
cookie2,
|
||||
cost,
|
||||
dasl,
|
||||
date,
|
||||
date_received,
|
||||
dav,
|
||||
default_style,
|
||||
deferred_delivery,
|
||||
delivery_date,
|
||||
delta_base,
|
||||
depth,
|
||||
derived_from,
|
||||
destination,
|
||||
differential_id,
|
||||
digest,
|
||||
discarded_x400_ipms_extensions,
|
||||
discarded_x400_mts_extensions,
|
||||
disclose_recipients,
|
||||
disposition_notification_options,
|
||||
disposition_notification_to,
|
||||
distribution,
|
||||
dkim_signature,
|
||||
dl_expansion_history,
|
||||
downgraded_bcc,
|
||||
downgraded_cc,
|
||||
downgraded_disposition_notification_to,
|
||||
downgraded_final_recipient,
|
||||
downgraded_from,
|
||||
downgraded_in_reply_to,
|
||||
downgraded_mail_from,
|
||||
downgraded_message_id,
|
||||
downgraded_original_recipient,
|
||||
downgraded_rcpt_to,
|
||||
downgraded_references,
|
||||
downgraded_reply_to,
|
||||
downgraded_resent_bcc,
|
||||
downgraded_resent_cc,
|
||||
downgraded_resent_from,
|
||||
downgraded_resent_reply_to,
|
||||
downgraded_resent_sender,
|
||||
downgraded_resent_to,
|
||||
downgraded_return_path,
|
||||
downgraded_sender,
|
||||
downgraded_to,
|
||||
ediint_features,
|
||||
eesst_version,
|
||||
encoding,
|
||||
encrypted,
|
||||
errors_to,
|
||||
etag,
|
||||
expect,
|
||||
expires,
|
||||
expiry_date,
|
||||
ext,
|
||||
followup_to,
|
||||
forwarded,
|
||||
from,
|
||||
generate_delivery_report,
|
||||
getprofile,
|
||||
hobareg,
|
||||
host,
|
||||
http2_settings,
|
||||
if_,
|
||||
if_match,
|
||||
if_modified_since,
|
||||
if_none_match,
|
||||
if_range,
|
||||
if_schedule_tag_match,
|
||||
if_unmodified_since,
|
||||
im,
|
||||
importance,
|
||||
in_reply_to,
|
||||
incomplete_copy,
|
||||
injection_date,
|
||||
injection_info,
|
||||
jabber_id,
|
||||
keep_alive,
|
||||
keywords,
|
||||
label,
|
||||
language,
|
||||
last_modified,
|
||||
latest_delivery_time,
|
||||
lines,
|
||||
link,
|
||||
list_archive,
|
||||
list_help,
|
||||
list_id,
|
||||
list_owner,
|
||||
list_post,
|
||||
list_subscribe,
|
||||
list_unsubscribe,
|
||||
list_unsubscribe_post,
|
||||
location,
|
||||
lock_token,
|
||||
man,
|
||||
max_forwards,
|
||||
memento_datetime,
|
||||
message_context,
|
||||
message_id,
|
||||
message_type,
|
||||
meter,
|
||||
method_check,
|
||||
method_check_expires,
|
||||
mime_version,
|
||||
mmhs_acp127_message_identifier,
|
||||
mmhs_authorizing_users,
|
||||
mmhs_codress_message_indicator,
|
||||
mmhs_copy_precedence,
|
||||
mmhs_exempted_address,
|
||||
mmhs_extended_authorisation_info,
|
||||
mmhs_handling_instructions,
|
||||
mmhs_message_instructions,
|
||||
mmhs_message_type,
|
||||
mmhs_originator_plad,
|
||||
mmhs_originator_reference,
|
||||
mmhs_other_recipients_indicator_cc,
|
||||
mmhs_other_recipients_indicator_to,
|
||||
mmhs_primary_precedence,
|
||||
mmhs_subject_indicator_codes,
|
||||
mt_priority,
|
||||
negotiate,
|
||||
newsgroups,
|
||||
nntp_posting_date,
|
||||
nntp_posting_host,
|
||||
non_compliance,
|
||||
obsoletes,
|
||||
opt,
|
||||
optional,
|
||||
optional_www_authenticate,
|
||||
ordering_type,
|
||||
organization,
|
||||
origin,
|
||||
original_encoded_information_types,
|
||||
original_from,
|
||||
original_message_id,
|
||||
original_recipient,
|
||||
original_sender,
|
||||
original_subject,
|
||||
originator_return_address,
|
||||
overwrite,
|
||||
p3p,
|
||||
path,
|
||||
pep,
|
||||
pep_info,
|
||||
pics_label,
|
||||
position,
|
||||
posting_version,
|
||||
pragma,
|
||||
prefer,
|
||||
preference_applied,
|
||||
prevent_nondelivery_report,
|
||||
priority,
|
||||
privicon,
|
||||
profileobject,
|
||||
protocol,
|
||||
protocol_info,
|
||||
protocol_query,
|
||||
protocol_request,
|
||||
proxy_authenticate,
|
||||
proxy_authentication_info,
|
||||
proxy_authorization,
|
||||
proxy_connection,
|
||||
proxy_features,
|
||||
proxy_instruction,
|
||||
public_,
|
||||
public_key_pins,
|
||||
public_key_pins_report_only,
|
||||
range,
|
||||
received,
|
||||
received_spf,
|
||||
redirect_ref,
|
||||
references,
|
||||
referer,
|
||||
referer_root,
|
||||
relay_version,
|
||||
reply_by,
|
||||
reply_to,
|
||||
require_recipient_valid_since,
|
||||
resent_bcc,
|
||||
resent_cc,
|
||||
resent_date,
|
||||
resent_from,
|
||||
resent_message_id,
|
||||
resent_reply_to,
|
||||
resent_sender,
|
||||
resent_to,
|
||||
resolution_hint,
|
||||
resolver_location,
|
||||
retry_after,
|
||||
return_path,
|
||||
safe,
|
||||
schedule_reply,
|
||||
schedule_tag,
|
||||
sec_websocket_accept,
|
||||
sec_websocket_extensions,
|
||||
sec_websocket_key,
|
||||
sec_websocket_protocol,
|
||||
sec_websocket_version,
|
||||
security_scheme,
|
||||
see_also,
|
||||
sender,
|
||||
sensitivity,
|
||||
server,
|
||||
set_cookie,
|
||||
set_cookie2,
|
||||
setprofile,
|
||||
sio_label,
|
||||
sio_label_history,
|
||||
slug,
|
||||
soapaction,
|
||||
solicitation,
|
||||
status_uri,
|
||||
strict_transport_security,
|
||||
subject,
|
||||
subok,
|
||||
subst,
|
||||
summary,
|
||||
supersedes,
|
||||
surrogate_capability,
|
||||
surrogate_control,
|
||||
tcn,
|
||||
te,
|
||||
timeout,
|
||||
title,
|
||||
to,
|
||||
topic,
|
||||
trailer,
|
||||
transfer_encoding,
|
||||
ttl,
|
||||
ua_color,
|
||||
ua_media,
|
||||
ua_pixels,
|
||||
ua_resolution,
|
||||
ua_windowpixels,
|
||||
upgrade,
|
||||
urgency,
|
||||
uri,
|
||||
user_agent,
|
||||
variant_vary,
|
||||
vary,
|
||||
vbr_info,
|
||||
version,
|
||||
via,
|
||||
want_digest,
|
||||
warning,
|
||||
www_authenticate,
|
||||
x_archived_at,
|
||||
x_device_accept,
|
||||
x_device_accept_charset,
|
||||
x_device_accept_encoding,
|
||||
x_device_accept_language,
|
||||
x_device_user_agent,
|
||||
x_frame_options,
|
||||
x_mittente,
|
||||
x_pgp_sig,
|
||||
x_ricevuta,
|
||||
x_riferimento_message_id,
|
||||
x_tiporicevuta,
|
||||
x_trasporto,
|
||||
x_verificasicurezza,
|
||||
x400_content_identifier,
|
||||
x400_content_return,
|
||||
x400_content_type,
|
||||
x400_mts_identifier,
|
||||
x400_originator,
|
||||
x400_received,
|
||||
x400_recipients,
|
||||
x400_trace,
|
||||
xref,
|
||||
};
|
||||
|
||||
/** Convert a field enum to a string.
|
||||
|
||||
@param f The field to convert
|
||||
*/
|
||||
string_view
|
||||
to_string(field f);
|
||||
|
||||
/** Attempt to convert a string to a field enum.
|
||||
|
||||
The string comparison is case-insensitive.
|
||||
|
||||
@return The corresponding field, or @ref field::unknown
|
||||
if no known field matches.
|
||||
*/
|
||||
field
|
||||
string_to_field(string_view s);
|
||||
|
||||
/// Write the text for a field name to an output stream.
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, field f)
|
||||
{
|
||||
return os << to_string(f);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/field.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,737 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_FIELDS_HPP
|
||||
#define BOOST_BEAST_HTTP_FIELDS_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/string_param.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A container for storing HTTP header fields.
|
||||
|
||||
This container is designed to store the field value pairs that make
|
||||
up the fields and trailers in an HTTP message. Objects of this type
|
||||
are iterable, with each element holding the field name and field
|
||||
value.
|
||||
|
||||
Field names are stored as-is, but comparisons are case-insensitive.
|
||||
The container behaves as a `std::multiset`; there will be a separate
|
||||
value for each occurrence of the same field name. When the container
|
||||
is iterated the fields are presented in the order of insertion, with
|
||||
fields having the same name following each other consecutively.
|
||||
|
||||
Meets the requirements of @b Fields
|
||||
|
||||
@tparam Allocator The allocator to use. This must meet the
|
||||
requirements of @b Allocator.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_fields
|
||||
{
|
||||
friend class fields_test; // for `header`
|
||||
|
||||
static std::size_t constexpr max_static_buffer = 4096;
|
||||
|
||||
using off_t = std::uint16_t;
|
||||
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
|
||||
/// The type of element used to represent a field
|
||||
class value_type
|
||||
{
|
||||
friend class basic_fields;
|
||||
|
||||
boost::asio::const_buffer
|
||||
buffer() const;
|
||||
|
||||
value_type(field name,
|
||||
string_view sname, string_view value);
|
||||
|
||||
boost::intrusive::list_member_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
list_hook_;
|
||||
boost::intrusive::set_member_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
set_hook_;
|
||||
off_t off_;
|
||||
off_t len_;
|
||||
field f_;
|
||||
|
||||
public:
|
||||
/// Returns the field enum, which can be @ref field::unknown
|
||||
field
|
||||
name() const;
|
||||
|
||||
/// Returns the field name as a string
|
||||
string_view
|
||||
name_string() const;
|
||||
|
||||
/// Returns the value of the field
|
||||
string_view
|
||||
value() const;
|
||||
};
|
||||
|
||||
/** A strictly less predicate for comparing keys, using a case-insensitive comparison.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
*/
|
||||
struct key_compare : beast::iless
|
||||
{
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
template<class String>
|
||||
bool
|
||||
operator()(String const& lhs, value_type const& rhs) const
|
||||
{
|
||||
if(lhs.size() < rhs.name_string().size())
|
||||
return true;
|
||||
if(lhs.size() > rhs.name_string().size())
|
||||
return false;
|
||||
return iless::operator()(lhs, rhs.name_string());
|
||||
}
|
||||
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
template<class String>
|
||||
bool
|
||||
operator()(value_type const& lhs, String const& rhs) const
|
||||
{
|
||||
if(lhs.name_string().size() < rhs.size())
|
||||
return true;
|
||||
if(lhs.name_string().size() > rhs.size())
|
||||
return false;
|
||||
return iless::operator()(lhs.name_string(), rhs);
|
||||
}
|
||||
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
bool
|
||||
operator()(value_type const& lhs, value_type const& rhs) const
|
||||
{
|
||||
if(lhs.name_string().size() < rhs.name_string().size())
|
||||
return true;
|
||||
if(lhs.name_string().size() > rhs.name_string().size())
|
||||
return false;
|
||||
return iless::operator()(lhs.name_string(), rhs.name_string());
|
||||
}
|
||||
};
|
||||
|
||||
/// The algorithm used to serialize the header
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
class reader;
|
||||
#endif
|
||||
|
||||
private:
|
||||
using list_t = typename boost::intrusive::make_list<
|
||||
value_type, boost::intrusive::member_hook<
|
||||
value_type, boost::intrusive::list_member_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>,
|
||||
&value_type::list_hook_>,
|
||||
boost::intrusive::constant_time_size<
|
||||
false>>::type;
|
||||
|
||||
using set_t = typename boost::intrusive::make_multiset<
|
||||
value_type, boost::intrusive::member_hook<value_type,
|
||||
boost::intrusive::set_member_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>,
|
||||
&value_type::set_hook_>,
|
||||
boost::intrusive::constant_time_size<true>,
|
||||
boost::intrusive::compare<key_compare>>::type;
|
||||
|
||||
|
||||
public:
|
||||
/// Destructor
|
||||
~basic_fields();
|
||||
|
||||
/// Constructor.
|
||||
basic_fields() = default;
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_fields(Allocator const& alloc);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
*/
|
||||
basic_fields(basic_fields&&);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_fields(basic_fields&&, Allocator const& alloc);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_fields(basic_fields const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_fields(basic_fields const&, Allocator const& alloc);
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
*/
|
||||
basic_fields& operator=(basic_fields&&);
|
||||
|
||||
/// Copy assignment.
|
||||
basic_fields& operator=(basic_fields const&);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherAlloc>
|
||||
basic_fields& operator=(basic_fields<OtherAlloc> const&);
|
||||
|
||||
public:
|
||||
/// A constant iterator to the field sequence.
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
using const_iterator = typename list_t::const_iterator;
|
||||
#endif
|
||||
|
||||
/// A constant iterator to the field sequence.
|
||||
using iterator = const_iterator;
|
||||
|
||||
/// Return a copy of the allocator associated with the container.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return typename std::allocator_traits<
|
||||
Allocator>::template rebind_alloc<
|
||||
value_type>(alloc_);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Element access
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Returns the value for a field, or throws an exception.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@return The field value.
|
||||
|
||||
@throws std::out_of_range if the field is not found.
|
||||
*/
|
||||
string_view
|
||||
at(field name) const;
|
||||
|
||||
/** Returns the value for a field, or throws an exception.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@return The field value.
|
||||
|
||||
@throws std::out_of_range if the field is not found.
|
||||
*/
|
||||
string_view
|
||||
at(string_view name) const;
|
||||
|
||||
/** Returns the value for a field, or `""` if it does not exist.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field.
|
||||
*/
|
||||
string_view
|
||||
operator[](field name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or `""` if it does not exist.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field.
|
||||
*/
|
||||
string_view
|
||||
operator[](string_view name) const;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Iterators
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Return a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
// VFALCO Since the header and message derive from Fields,
|
||||
// what does the expression m.empty() mean? Its confusing.
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return list_.empty();
|
||||
}
|
||||
public:
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Modifiers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
// VFALCO But this leaves behind the method, target, and reason!
|
||||
/** Remove all fields from the container
|
||||
|
||||
All references, pointers, or iterators referring to contained
|
||||
elements are invalidated. All past-the-end iterators are also
|
||||
invalidated.
|
||||
*/
|
||||
void
|
||||
clear();
|
||||
public:
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
*/
|
||||
void
|
||||
insert(field name, string_param const& value);
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
*/
|
||||
void
|
||||
insert(string_view name, string_param const& value);
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param name_string The literal text corresponding to the
|
||||
field name. If `name != field::unknown`, then this value
|
||||
must be equal to `to_string(name)` using a case-insensitive
|
||||
comparison, otherwise the behavior is undefined.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
*/
|
||||
void
|
||||
insert(field name, string_view name_string,
|
||||
string_param const& value);
|
||||
|
||||
/** Set a field value, removing any other instances of that field.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
|
||||
@return The field value.
|
||||
*/
|
||||
void
|
||||
set(field name, string_param const& value);
|
||||
|
||||
/** Set a field value, removing any other instances of that field.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
*/
|
||||
void
|
||||
set(string_view name, string_param const& value);
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param pos An iterator to the element to remove.
|
||||
|
||||
@return An iterator following the last removed element.
|
||||
If the iterator refers to the last element, the end()
|
||||
iterator is returned.
|
||||
*/
|
||||
const_iterator
|
||||
erase(const_iterator pos);
|
||||
|
||||
/** Remove all fields with the specified name.
|
||||
|
||||
All fields with the same field name are erased from the
|
||||
container.
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(field name);
|
||||
|
||||
/** Remove all fields with the specified name.
|
||||
|
||||
All fields with the same field name are erased from the
|
||||
container.
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(string_view name);
|
||||
|
||||
/** Return a buffer sequence representing the trailers.
|
||||
|
||||
This function returns a buffer sequence holding the
|
||||
serialized representation of the trailer fields promised
|
||||
in the Accept field. Before calling this function the
|
||||
Accept field must contain the exact trailer fields
|
||||
desired. Each field must also exist.
|
||||
*/
|
||||
|
||||
|
||||
/// Swap this container with another
|
||||
void
|
||||
swap(basic_fields& other);
|
||||
|
||||
/// Swap two field containers
|
||||
template<class Alloc>
|
||||
friend
|
||||
void
|
||||
swap(basic_fields<Alloc>& lhs, basic_fields<Alloc>& rhs);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Lookup
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Return the number of fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
*/
|
||||
std::size_t
|
||||
count(field name) const;
|
||||
|
||||
/** Return the number of fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
*/
|
||||
std::size_t
|
||||
count(string_view name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return An iterator to the matching field, or `end()` if
|
||||
no match was found.
|
||||
*/
|
||||
const_iterator
|
||||
find(field name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field name.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return An iterator to the matching field, or `end()` if
|
||||
no match was found.
|
||||
*/
|
||||
const_iterator
|
||||
find(string_view name) const;
|
||||
|
||||
/** Returns a range of iterators to the fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return A range of iterators to fields with the same name,
|
||||
otherwise an empty range.
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(field name) const;
|
||||
|
||||
/** Returns a range of iterators to the fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return A range of iterators to fields with the same name,
|
||||
otherwise an empty range.
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(string_view name) const;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Observers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Returns a copy of the key comparison function
|
||||
key_compare
|
||||
key_comp() const
|
||||
{
|
||||
return key_compare{};
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Returns the request-method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view
|
||||
get_method_impl() const;
|
||||
|
||||
/** Returns the request-target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view
|
||||
get_target_impl() const;
|
||||
|
||||
/** Returns the response reason-phrase string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
string_view
|
||||
get_reason_impl() const;
|
||||
|
||||
/** Returns the chunked Transfer-Encoding setting
|
||||
*/
|
||||
bool
|
||||
get_chunked_impl() const;
|
||||
|
||||
/** Returns the keep-alive setting
|
||||
*/
|
||||
bool
|
||||
get_keep_alive_impl(unsigned version) const;
|
||||
|
||||
/** Set or clear the method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_method_impl(string_view s);
|
||||
|
||||
/** Set or clear the target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_target_impl(string_view s);
|
||||
|
||||
/** Set or clear the reason string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
void
|
||||
set_reason_impl(string_view s);
|
||||
|
||||
/** Adjusts the chunked Transfer-Encoding value
|
||||
*/
|
||||
void
|
||||
set_chunked_impl(bool value);
|
||||
|
||||
/** Sets or clears the Content-Length field
|
||||
*/
|
||||
void
|
||||
set_content_length_impl(
|
||||
boost::optional<std::uint64_t> const& value);
|
||||
|
||||
/** Adjusts the Connection field
|
||||
*/
|
||||
void
|
||||
set_keep_alive_impl(
|
||||
unsigned version, bool keep_alive);
|
||||
|
||||
private:
|
||||
template<class OtherAlloc>
|
||||
friend class basic_fields;
|
||||
|
||||
using alloc_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<value_type>;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<alloc_type>;
|
||||
|
||||
using size_type =
|
||||
typename std::allocator_traits<Allocator>::size_type;
|
||||
|
||||
value_type&
|
||||
new_element(field name,
|
||||
string_view sname, string_view value);
|
||||
|
||||
void
|
||||
delete_element(value_type& e);
|
||||
|
||||
void
|
||||
set_element(value_type& e);
|
||||
|
||||
void
|
||||
realloc_string(string_view& dest, string_view s);
|
||||
|
||||
void
|
||||
realloc_target(
|
||||
string_view& dest, string_view s);
|
||||
|
||||
template<class OtherAlloc>
|
||||
void
|
||||
copy_all(basic_fields<OtherAlloc> const&);
|
||||
|
||||
void
|
||||
clear_all();
|
||||
|
||||
void
|
||||
delete_list();
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::true_type);
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::false_type);
|
||||
|
||||
void
|
||||
swap(basic_fields& other, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_fields& other, std::false_type);
|
||||
|
||||
alloc_type alloc_;
|
||||
set_t set_;
|
||||
list_t list_;
|
||||
string_view method_;
|
||||
string_view target_or_reason_;
|
||||
};
|
||||
|
||||
/// A typical HTTP header fields container
|
||||
using fields = basic_fields<std::allocator<char>>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/fields.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_FILE_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_FILE_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/file.hpp>
|
||||
#include <boost/beast/http/basic_file_body.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// A message body represented by a file on the filesystem.
|
||||
using file_body = basic_file_body<file>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/file_body_win32.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,939 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
|
||||
|
||||
#include <boost/beast/core/static_string.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
basic_parser<isRequest, Derived>::
|
||||
~basic_parser()
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
basic_parser<isRequest, Derived>::
|
||||
basic_parser()
|
||||
: body_limit_(
|
||||
default_body_limit(is_request{}))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class OtherDerived>
|
||||
basic_parser<isRequest, Derived>::
|
||||
basic_parser(basic_parser<
|
||||
isRequest, OtherDerived>&& other)
|
||||
: body_limit_(other.body_limit_)
|
||||
, len_(other.len_)
|
||||
, buf_(std::move(other.buf_))
|
||||
, buf_len_(other.buf_len_)
|
||||
, skip_(other.skip_)
|
||||
, state_(other.state_)
|
||||
, f_(other.f_)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
basic_parser<isRequest, Derived>::
|
||||
is_keep_alive() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(f_ & flagHTTP11)
|
||||
{
|
||||
if(f_ & flagConnectionClose)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(! (f_ & flagConnectionKeepAlive))
|
||||
return false;
|
||||
}
|
||||
return (f_ & flagNeedEOF) == 0;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest, Derived>::
|
||||
content_length() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(! (f_ & flagContentLength))
|
||||
return boost::none;
|
||||
return len_;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
skip(bool v)
|
||||
{
|
||||
BOOST_ASSERT(! got_some());
|
||||
if(v)
|
||||
f_ |= flagSkipBody;
|
||||
else
|
||||
f_ &= ~flagSkipBody;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const p = buffers.begin();
|
||||
auto const last = buffers.end();
|
||||
if(p == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return 0;
|
||||
}
|
||||
if(std::next(p) == last)
|
||||
{
|
||||
// single buffer
|
||||
auto const b = *p;
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buffer_cast<char const*>(b),
|
||||
buffer_size(b)}, ec);
|
||||
}
|
||||
auto const size = buffer_size(buffers);
|
||||
if(size <= max_stack_buffer)
|
||||
return put_from_stack(size, buffers, ec);
|
||||
if(size > buf_len_)
|
||||
{
|
||||
// reallocate
|
||||
buf_ = boost::make_unique_noinit<char[]>(size);
|
||||
buf_len_ = size;
|
||||
}
|
||||
// flatten
|
||||
buffer_copy(boost::asio::buffer(
|
||||
buf_.get(), buf_len_), buffers);
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buf_.get(), buf_len_}, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(state_ != state::complete);
|
||||
using boost::asio::buffer_size;
|
||||
auto p = boost::asio::buffer_cast<
|
||||
char const*>(*buffer.begin());
|
||||
auto n = buffer_size(*buffer.begin());
|
||||
auto const p0 = p;
|
||||
auto const p1 = p0 + n;
|
||||
ec.assign(0, ec.category());
|
||||
loop:
|
||||
switch(state_)
|
||||
{
|
||||
case state::nothing_yet:
|
||||
if(n == 0)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return 0;
|
||||
}
|
||||
state_ = state::start_line;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case state::start_line:
|
||||
{
|
||||
maybe_need_more(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
parse_start_line(p, p + std::min<std::size_t>(
|
||||
header_limit_, n), ec, is_request{});
|
||||
if(ec)
|
||||
{
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
if(n >= header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
goto done;
|
||||
}
|
||||
if(p + 3 <= p1)
|
||||
skip_ = static_cast<
|
||||
std::size_t>(p1 - p - 3);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
BOOST_ASSERT(! is_done());
|
||||
n = static_cast<std::size_t>(p1 - p);
|
||||
if(p >= p1)
|
||||
{
|
||||
ec = error::need_more;
|
||||
goto done;
|
||||
}
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case state::fields:
|
||||
maybe_need_more(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
parse_fields(p, p + std::min<std::size_t>(
|
||||
header_limit_, n), ec);
|
||||
if(ec)
|
||||
{
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
if(n >= header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
goto done;
|
||||
}
|
||||
if(p + 3 <= p1)
|
||||
skip_ = static_cast<
|
||||
std::size_t>(p1 - p - 3);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
finish_header(ec, is_request{});
|
||||
break;
|
||||
|
||||
case state::body0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
impl().on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case state::body:
|
||||
BOOST_ASSERT(! skip_);
|
||||
parse_body(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::body_to_eof0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
impl().on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body_to_eof;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case state::body_to_eof:
|
||||
BOOST_ASSERT(! skip_);
|
||||
parse_body_to_eof(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::chunk_header0:
|
||||
impl().on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::chunk_header;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case state::chunk_header:
|
||||
parse_chunk_header(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::chunk_body:
|
||||
parse_chunk_body(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::complete:
|
||||
ec.assign(0, ec.category());
|
||||
goto done;
|
||||
}
|
||||
if(p < p1 && ! is_done() && eager())
|
||||
{
|
||||
n = static_cast<std::size_t>(p1 - p);
|
||||
goto loop;
|
||||
}
|
||||
done:
|
||||
return static_cast<std::size_t>(p - p0);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
put_eof(error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(got_some());
|
||||
if( state_ == state::start_line ||
|
||||
state_ == state::fields)
|
||||
{
|
||||
ec = error::partial_message;
|
||||
return;
|
||||
}
|
||||
if(f_ & (flagContentLength | flagChunked))
|
||||
{
|
||||
if(state_ != state::complete)
|
||||
{
|
||||
ec = error::partial_message;
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put_from_stack(std::size_t size,
|
||||
ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
char buf[max_stack_buffer];
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
buffer_copy(buffer(buf, sizeof(buf)), buffers);
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buf, size}, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
maybe_need_more(
|
||||
char const* p, std::size_t n,
|
||||
error_code& ec)
|
||||
{
|
||||
if(skip_ == 0)
|
||||
return;
|
||||
if( n > header_limit_)
|
||||
n = header_limit_;
|
||||
if(n < skip_ + 4)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
auto const term =
|
||||
find_eom(p + skip_, p + n);
|
||||
if(! term)
|
||||
{
|
||||
skip_ = n - 3;
|
||||
if(skip_ + 4 > header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
return;
|
||||
}
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
skip_ = 0;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_start_line(
|
||||
char const*& in, char const* last,
|
||||
error_code& ec, std::true_type)
|
||||
{
|
||||
/*
|
||||
request-line = method SP request-target SP HTTP-version CRLF
|
||||
method = token
|
||||
*/
|
||||
auto p = in;
|
||||
|
||||
string_view method;
|
||||
parse_method(p, last, method, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
string_view target;
|
||||
parse_target(p, last, target, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
int version = 0;
|
||||
parse_version(p, last, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(version < 10 || version > 11)
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
if(p + 2 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(p[0] != '\r' || p[1] != '\n')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
impl().on_request_impl(string_to_verb(method),
|
||||
method, target, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_start_line(
|
||||
char const*& in, char const* last,
|
||||
error_code& ec, std::false_type)
|
||||
{
|
||||
/*
|
||||
status-line = HTTP-version SP status-code SP reason-phrase CRLF
|
||||
status-code = 3*DIGIT
|
||||
reason-phrase = *( HTAB / SP / VCHAR / obs-text )
|
||||
*/
|
||||
auto p = in;
|
||||
|
||||
int version = 0;
|
||||
parse_version(p, last, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(version < 10 || version > 11)
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
// SP
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*p++ != ' ')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
parse_status(p, last, status_, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
// parse reason CRLF
|
||||
string_view reason;
|
||||
parse_reason(p, last, reason, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
impl().on_response_impl(
|
||||
status_, reason, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_fields(char const*& in,
|
||||
char const* last, error_code& ec)
|
||||
{
|
||||
string_view name;
|
||||
string_view value;
|
||||
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
||||
static_string<max_obs_fold> buf;
|
||||
auto p = in;
|
||||
for(;;)
|
||||
{
|
||||
if(p + 2 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(p[0] == '\r')
|
||||
{
|
||||
if(p[1] != '\n')
|
||||
ec = error::bad_line_ending;
|
||||
in = p + 2;
|
||||
return;
|
||||
}
|
||||
parse_field(p, last, name, value, buf, ec);
|
||||
if(ec)
|
||||
return;
|
||||
auto const f = string_to_field(name);
|
||||
do_field(f, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
impl().on_field_impl(f, name, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
in = p;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
finish_header(error_code& ec, std::true_type)
|
||||
{
|
||||
// RFC 7230 section 3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
if(f_ & flagSkipBody)
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
else if(f_ & flagContentLength)
|
||||
{
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
len_ = 0;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
impl().on_header_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
finish_header(error_code& ec, std::false_type)
|
||||
{
|
||||
// RFC 7230 section 3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
|
||||
status_ / 100 == 1 || // 1xx e.g. Continue
|
||||
status_ == 204 || // No Content
|
||||
status_ == 304) // Not Modified
|
||||
{
|
||||
state_ = state::complete;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
f_ |= flagNeedEOF;
|
||||
state_ = state::body_to_eof0;
|
||||
}
|
||||
|
||||
impl().on_header_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
n = impl().on_body_impl(string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(ec)
|
||||
return;
|
||||
if(len_ > 0)
|
||||
return;
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
if(n > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
body_limit_ = body_limit_ - n;
|
||||
n = impl().on_body_impl(string_view{p, n}, ec);
|
||||
p += n;
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_chunk_header(char const*& p0,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunked-body = *chunk last-chunk trailer-part CRLF
|
||||
|
||||
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
|
||||
last-chunk = 1*("0") [ chunk-ext ] CRLF
|
||||
trailer-part = *( header-field CRLF )
|
||||
|
||||
chunk-size = 1*HEXDIG
|
||||
chunk-data = 1*OCTET ; a sequence of chunk-size octets
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
*/
|
||||
|
||||
auto p = p0;
|
||||
auto const pend = p + n;
|
||||
char const* eol;
|
||||
|
||||
if(! (f_ & flagFinalChunk))
|
||||
{
|
||||
if(n < skip_ + 2)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(f_ & flagExpectCRLF)
|
||||
{
|
||||
// Treat the last CRLF in a chunk as
|
||||
// part of the next chunk, so p can
|
||||
// be parsed in one call instead of two.
|
||||
if(! parse_crlf(p))
|
||||
{
|
||||
ec = error::bad_chunk;
|
||||
return;
|
||||
}
|
||||
}
|
||||
eol = find_eol(p0 + skip_, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! eol)
|
||||
{
|
||||
ec = error::need_more;
|
||||
skip_ = n - 1;
|
||||
return;
|
||||
}
|
||||
skip_ = static_cast<
|
||||
std::size_t>(eol - 2 - p0);
|
||||
|
||||
std::uint64_t size;
|
||||
if(! parse_hex(p, size))
|
||||
{
|
||||
ec = error::bad_chunk;
|
||||
return;
|
||||
}
|
||||
if(size != 0)
|
||||
{
|
||||
if(size > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
body_limit_ -= size;
|
||||
auto const start = p;
|
||||
parse_chunk_extensions(p, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(p != eol -2 )
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
auto const ext = make_string(start, p);
|
||||
impl().on_chunk_header_impl(size, ext, ec);
|
||||
if(ec)
|
||||
return;
|
||||
len_ = size;
|
||||
skip_ = 2;
|
||||
p0 = eol;
|
||||
f_ |= flagExpectCRLF;
|
||||
state_ = state::chunk_body;
|
||||
return;
|
||||
}
|
||||
|
||||
f_ |= flagFinalChunk;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(n >= 5);
|
||||
if(f_ & flagExpectCRLF)
|
||||
BOOST_VERIFY(parse_crlf(p));
|
||||
std::uint64_t size;
|
||||
BOOST_VERIFY(parse_hex(p, size));
|
||||
eol = find_eol(p, pend, ec);
|
||||
BOOST_ASSERT(! ec);
|
||||
}
|
||||
|
||||
auto eom = find_eom(p0 + skip_, pend);
|
||||
if(! eom)
|
||||
{
|
||||
BOOST_ASSERT(n >= 3);
|
||||
skip_ = n - 3;
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
|
||||
auto const start = p;
|
||||
parse_chunk_extensions(p, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(p != eol - 2)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
auto const ext = make_string(start, p);
|
||||
impl().on_chunk_header_impl(0, ext, ec);
|
||||
if(ec)
|
||||
return;
|
||||
p = eol;
|
||||
parse_fields(p, eom, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p == eom);
|
||||
p0 = eom;
|
||||
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
n = impl().on_chunk_body_impl(
|
||||
len_, string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(len_ == 0)
|
||||
state_ = state::chunk_header;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
do_field(field f,
|
||||
string_view value, error_code& ec)
|
||||
{
|
||||
// Connection
|
||||
if(f == field::connection ||
|
||||
f == field::proxy_connection)
|
||||
{
|
||||
auto const list = opt_token_list{value};
|
||||
if(! validate_list(list))
|
||||
{
|
||||
// VFALCO Should this be a field specific error?
|
||||
ec = error::bad_value;
|
||||
return;
|
||||
}
|
||||
for(auto const& s : list)
|
||||
{
|
||||
if(iequals({"close", 5}, s))
|
||||
{
|
||||
f_ |= flagConnectionClose;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(iequals({"keep-alive", 10}, s))
|
||||
{
|
||||
f_ |= flagConnectionKeepAlive;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(iequals({"upgrade", 7}, s))
|
||||
{
|
||||
f_ |= flagConnectionUpgrade;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
|
||||
// Content-Length
|
||||
if(f == field::content_length)
|
||||
{
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
// duplicate
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagChunked)
|
||||
{
|
||||
// conflicting field
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
std::uint64_t v;
|
||||
if(! parse_dec(
|
||||
value.begin(), value.end(), v))
|
||||
{
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
if(v > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
len_ = v;
|
||||
f_ |= flagContentLength;
|
||||
return;
|
||||
}
|
||||
|
||||
// Transfer-Encoding
|
||||
if(f == field::transfer_encoding)
|
||||
{
|
||||
if(f_ & flagChunked)
|
||||
{
|
||||
// duplicate
|
||||
ec = error::bad_transfer_encoding;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
// conflicting field
|
||||
ec = error::bad_transfer_encoding;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
auto const v = token_list{value};
|
||||
auto const p = std::find_if(v.begin(), v.end(),
|
||||
[&](typename token_list::value_type const& s)
|
||||
{
|
||||
return iequals({"chunked", 7}, s);
|
||||
});
|
||||
if(p == v.end())
|
||||
return;
|
||||
if(std::next(p) != v.end())
|
||||
return;
|
||||
len_ = 0;
|
||||
f_ |= flagChunked;
|
||||
return;
|
||||
}
|
||||
|
||||
// Upgrade
|
||||
if(f == field::upgrade)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
f_ |= flagUpgrade;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,707 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP
|
||||
|
||||
#include <boost/beast/core/detail/varint.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
inline
|
||||
chunk_header::
|
||||
chunk_header(std::size_t size)
|
||||
: view_(
|
||||
size,
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{})
|
||||
{
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
inline
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
string_view extensions)
|
||||
: view_(
|
||||
size,
|
||||
boost::asio::const_buffers_1{
|
||||
extensions.data(), extensions.size()},
|
||||
chunk_crlf{})
|
||||
{
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
template<class ChunkExtensions, class>
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions)
|
||||
: exts_(std::make_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
size,
|
||||
exts_->str(),
|
||||
chunk_crlf{})
|
||||
{
|
||||
static_assert(
|
||||
detail::is_chunk_extensions<ChunkExtensions>::value,
|
||||
"ChunkExtensions requirements not met");
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
template<class ChunkExtensions, class Allocator, class>
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator)
|
||||
: exts_(std::allocate_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(allocator,
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
size,
|
||||
exts_->str(),
|
||||
chunk_crlf{})
|
||||
{
|
||||
static_assert(
|
||||
detail::is_chunk_extensions<ChunkExtensions>::value,
|
||||
"ChunkExtensions requirements not met");
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(ConstBufferSequence const& buffers)
|
||||
: view_(
|
||||
boost::asio::buffer_size(buffers),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
string_view extensions)
|
||||
: view_(
|
||||
boost::asio::buffer_size(buffers),
|
||||
boost::asio::const_buffers_1{
|
||||
extensions.data(), extensions.size()},
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
template<class ChunkExtensions, class>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions)
|
||||
: exts_(std::make_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
boost::asio::buffer_size(buffers),
|
||||
exts_->str(),
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
template<class ChunkExtensions, class Allocator, class>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator)
|
||||
: exts_(std::allocate_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(allocator,
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
boost::asio::buffer_size(buffers),
|
||||
exts_->str(),
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Trailer>
|
||||
template<class Allocator>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, Allocator const& allocator) ->
|
||||
buffers_type
|
||||
{
|
||||
auto sp = std::allocate_shared<typename
|
||||
Trailer::reader>(allocator, trailer);
|
||||
sp_ = sp;
|
||||
return sp->get();
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, std::true_type) ->
|
||||
buffers_type
|
||||
{
|
||||
auto sp = std::make_shared<
|
||||
typename Trailer::reader>(trailer);
|
||||
sp_ = sp;
|
||||
return sp->get();
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, std::false_type) ->
|
||||
buffers_type
|
||||
{
|
||||
return trailer;
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last()
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
Trailer{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last(Trailer const& trailer)
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
prepare(trailer, is_fields<Trailer>{}))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
template<class DeducedTrailer, class Allocator, class>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last(
|
||||
DeducedTrailer const& trailer, Allocator const& allocator)
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
prepare(trailer, allocator))
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
class basic_chunk_extensions<Allocator>::const_iterator
|
||||
{
|
||||
friend class basic_chunk_extensions;
|
||||
|
||||
using iter_type = char const*;
|
||||
|
||||
iter_type it_;
|
||||
typename basic_chunk_extensions::value_type value_;
|
||||
|
||||
explicit
|
||||
const_iterator(iter_type it)
|
||||
: it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
increment();
|
||||
|
||||
public:
|
||||
using value_type = typename
|
||||
basic_chunk_extensions::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*();
|
||||
|
||||
pointer
|
||||
operator->()
|
||||
{
|
||||
return &(**this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
increment();
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
const_iterator::
|
||||
increment()
|
||||
{
|
||||
using beast::detail::varint_read;
|
||||
auto n = varint_read(it_);
|
||||
it_ += n;
|
||||
n = varint_read(it_);
|
||||
it_ += n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
const_iterator::
|
||||
operator*() ->
|
||||
reference
|
||||
{
|
||||
using beast::detail::varint_read;
|
||||
auto it = it_;
|
||||
auto n = varint_read(it);
|
||||
value_.first = string_view{it, n};
|
||||
it += n;
|
||||
n = varint_read(it);
|
||||
value_.second = string_view{it, n};
|
||||
return value_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
template<class FwdIt>
|
||||
FwdIt
|
||||
basic_chunk_extensions<Allocator>::
|
||||
do_parse(FwdIt it, FwdIt last, error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
|
||||
BWS = *( SP / HTAB ) ; "Bad White Space"
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
token = 1*tchar
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
|
||||
https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
|
||||
*/
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
using CharT = char;
|
||||
using Traits = std::char_traits<CharT>;
|
||||
range_.reserve(static_cast<std::size_t>(
|
||||
std::distance(it, last) * 1.2));
|
||||
range_.resize(0);
|
||||
auto const emit_string =
|
||||
[this](FwdIt from, FwdIt to)
|
||||
{
|
||||
auto const len =
|
||||
std::distance(from, to);
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len);
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
Traits::copy(dest, from, len);
|
||||
};
|
||||
auto const emit_string_plus_empty =
|
||||
[this](FwdIt from, FwdIt to)
|
||||
{
|
||||
auto const len =
|
||||
std::distance(from, to);
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len +
|
||||
varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
Traits::copy(dest, from, len);
|
||||
dest += len;
|
||||
varint_write(dest, 0);
|
||||
};
|
||||
auto const emit_empty_string =
|
||||
[this]
|
||||
{
|
||||
auto const offset = range_.size();
|
||||
range_.resize(offset + varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, 0);
|
||||
};
|
||||
loop:
|
||||
if(it == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return it;
|
||||
}
|
||||
// BWS
|
||||
if(*it == ' ' || *it == '\t')
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ';'
|
||||
if(*it != ';')
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
semi:
|
||||
++it; // skip ';'
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-name
|
||||
{
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
auto const first = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
emit_string_plus_empty(first, it);
|
||||
return it;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
emit_string(first, it);
|
||||
}
|
||||
// BWS [ ";" / "=" ]
|
||||
for(;;)
|
||||
{
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
}
|
||||
if(*it == ';')
|
||||
{
|
||||
emit_empty_string();
|
||||
goto semi;
|
||||
}
|
||||
if(*it != '=')
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
++it; // skip '='
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-val
|
||||
if(*it != '"')
|
||||
{
|
||||
// token
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
auto const first = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
break;
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
emit_string(first, it);
|
||||
if(it == last)
|
||||
return it;
|
||||
}
|
||||
else
|
||||
{
|
||||
// quoted-string
|
||||
auto const first = ++it; // skip DQUOTE
|
||||
// first pass, count chars
|
||||
std::size_t len = 0;
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
}
|
||||
++len;
|
||||
++it;
|
||||
}
|
||||
// now build the string
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len);
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
it = first;
|
||||
for(;;)
|
||||
{
|
||||
BOOST_ASSERT(it != last);
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
BOOST_ASSERT(it != last);
|
||||
}
|
||||
Traits::assign(*dest++, *it++);
|
||||
}
|
||||
++it; // skip DQUOTE
|
||||
}
|
||||
goto loop;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
do_insert(string_view name, string_view value)
|
||||
{
|
||||
/*
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
token = 1*tchar
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
*/
|
||||
if(value.empty())
|
||||
{
|
||||
s_.reserve(1 + name.size());
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_token = true;
|
||||
for(auto const c : value)
|
||||
{
|
||||
if(! detail::is_token_char(c))
|
||||
{
|
||||
is_token = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(is_token)
|
||||
{
|
||||
// token
|
||||
s_.reserve(1 + name.size() + 1 + value.size());
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
s_.push_back('=');
|
||||
s_.append(value.data(), value.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// quoted-string
|
||||
s_.reserve(
|
||||
1 + name.size() + 1 +
|
||||
1 + value.size() + 20 + 1);
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
s_.append("=\"", 2);
|
||||
for(auto const c : value)
|
||||
{
|
||||
if(c == '\\')
|
||||
s_.append(R"(\\)", 2);
|
||||
else if(c == '\"')
|
||||
s_.append(R"(\")", 2);
|
||||
else
|
||||
s_.push_back(c);
|
||||
}
|
||||
s_.push_back('"');
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
parse(string_view s, error_code& ec)
|
||||
{
|
||||
do_parse(s.data(), s.data() + s.size(), ec);
|
||||
if(! ec)
|
||||
{
|
||||
s_.clear();
|
||||
for(auto const& v : *this)
|
||||
do_insert(v.first, v.second);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
insert(string_view name)
|
||||
{
|
||||
do_insert(name, {});
|
||||
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(name.size()) +
|
||||
name.size() +
|
||||
varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, name.size());
|
||||
std::memcpy(dest, name.data(), name.size());
|
||||
dest += name.size();
|
||||
varint_write(dest, 0);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
insert(string_view name, string_view value)
|
||||
{
|
||||
do_insert(name, value);
|
||||
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(name.size()) +
|
||||
name.size() +
|
||||
varint_size(value.size()) +
|
||||
value.size());
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, name.size());
|
||||
std::memcpy(dest, name.data(), name.size());
|
||||
dest += name.size();
|
||||
varint_write(dest, value.size());
|
||||
std::memcpy(dest, value.data(), value.size());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{range_.data()};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{
|
||||
range_.data() + range_.size()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_ERROR_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_ERROR_IPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::http::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class http_error_category : public error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "beast.http";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
case error::end_of_stream: return "end of stream";
|
||||
case error::partial_message: return "partial message";
|
||||
case error::need_more: return "need more";
|
||||
case error::unexpected_body: return "unexpected body";
|
||||
case error::need_buffer: return "need buffer";
|
||||
case error::end_of_chunk: return "end of chunk";
|
||||
case error::buffer_overflow: return "buffer overflow";
|
||||
case error::header_limit: return "header limit exceeded";
|
||||
case error::body_limit: return "body limit exceeded";
|
||||
case error::bad_alloc: return "bad alloc";
|
||||
case error::bad_line_ending: return "bad line ending";
|
||||
case error::bad_method: return "bad method";
|
||||
case error::bad_target: return "bad target";
|
||||
case error::bad_version: return "bad version";
|
||||
case error::bad_status: return "bad status";
|
||||
case error::bad_reason: return "bad reason";
|
||||
case error::bad_field: return "bad field";
|
||||
case error::bad_value: return "bad value";
|
||||
case error::bad_content_length: return "bad Content-Length";
|
||||
case error::bad_transfer_encoding: return "bad Transfer-Encoding";
|
||||
case error::bad_chunk: return "bad chunk";
|
||||
case error::bad_chunk_extension: return "bad chunk extension";
|
||||
case error::bad_obs_fold: return "bad obs-fold";
|
||||
|
||||
default:
|
||||
return "beast.http error";
|
||||
}
|
||||
}
|
||||
|
||||
error_condition
|
||||
default_error_condition(
|
||||
int ev) const noexcept override
|
||||
{
|
||||
return error_condition{ev, *this};
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
error_condition const& condition
|
||||
) const noexcept override
|
||||
{
|
||||
return condition.value() == ev &&
|
||||
&condition.category() == this;
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(error_code const& error,
|
||||
int ev) const noexcept override
|
||||
{
|
||||
return error.value() == ev &&
|
||||
&error.category() == this;
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
error_category const&
|
||||
get_http_error_category()
|
||||
{
|
||||
static http_error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_error_code(error ev)
|
||||
{
|
||||
return error_code{
|
||||
static_cast<std::underlying_type<error>::type>(ev),
|
||||
detail::get_http_error_category()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,561 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_FIELD_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FIELD_IPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct field_table
|
||||
{
|
||||
using array_type =
|
||||
std::array<string_view, 352>;
|
||||
|
||||
struct hash
|
||||
{
|
||||
std::size_t
|
||||
operator()(string_view s) const
|
||||
{
|
||||
auto const n = s.size();
|
||||
return
|
||||
beast::detail::ascii_tolower(s[0]) *
|
||||
beast::detail::ascii_tolower(s[n/2]) ^
|
||||
beast::detail::ascii_tolower(s[n-1]); // hist[] = 331, 10, max_load_factor = 0.15f
|
||||
}
|
||||
};
|
||||
|
||||
struct iequal
|
||||
{
|
||||
// assumes inputs have equal length
|
||||
bool
|
||||
operator()(
|
||||
string_view lhs,
|
||||
string_view rhs) const
|
||||
{
|
||||
auto p1 = lhs.data();
|
||||
auto p2 = rhs.data();
|
||||
auto pend = lhs.end();
|
||||
char a, b;
|
||||
while(p1 < pend)
|
||||
{
|
||||
a = *p1++;
|
||||
b = *p2++;
|
||||
if(a != b)
|
||||
goto slow;
|
||||
}
|
||||
return true;
|
||||
|
||||
while(p1 < pend)
|
||||
{
|
||||
slow:
|
||||
if( beast::detail::ascii_tolower(a) !=
|
||||
beast::detail::ascii_tolower(b))
|
||||
return false;
|
||||
a = *p1++;
|
||||
b = *p2++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using map_type = std::unordered_map<
|
||||
string_view, field, hash, iequal>;
|
||||
|
||||
array_type by_name_;
|
||||
std::vector<map_type> by_size_;
|
||||
/*
|
||||
From:
|
||||
|
||||
https://www.iana.org/assignments/message-headers/message-headers.xhtml
|
||||
*/
|
||||
field_table()
|
||||
: by_name_({{
|
||||
"<unknown-field>",
|
||||
"A-IM",
|
||||
"Accept",
|
||||
"Accept-Additions",
|
||||
"Accept-Charset",
|
||||
"Accept-Datetime",
|
||||
"Accept-Encoding",
|
||||
"Accept-Features",
|
||||
"Accept-Language",
|
||||
"Accept-Patch",
|
||||
"Accept-Post",
|
||||
"Accept-Ranges",
|
||||
"Access-Control",
|
||||
"Access-Control-Allow-Credentials",
|
||||
"Access-Control-Allow-Headers",
|
||||
"Access-Control-Allow-Methods",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Access-Control-Max-Age",
|
||||
"Access-Control-Request-Headers",
|
||||
"Access-Control-Request-Method",
|
||||
"Age",
|
||||
"Allow",
|
||||
"ALPN",
|
||||
"Also-Control",
|
||||
"Alt-Svc",
|
||||
"Alt-Used",
|
||||
"Alternate-Recipient",
|
||||
"Alternates",
|
||||
"Apparently-To",
|
||||
"Apply-To-Redirect-Ref",
|
||||
"Approved",
|
||||
"Archive",
|
||||
"Archived-At",
|
||||
"Article-Names",
|
||||
"Article-Updates",
|
||||
"Authentication-Control",
|
||||
"Authentication-Info",
|
||||
"Authentication-Results",
|
||||
"Authorization",
|
||||
"Auto-Submitted",
|
||||
"Autoforwarded",
|
||||
"Autosubmitted",
|
||||
"Base",
|
||||
"Bcc",
|
||||
"Body",
|
||||
"C-Ext",
|
||||
"C-Man",
|
||||
"C-Opt",
|
||||
"C-PEP",
|
||||
"C-PEP-Info",
|
||||
"Cache-Control",
|
||||
"CalDAV-Timezones",
|
||||
"Cancel-Key",
|
||||
"Cancel-Lock",
|
||||
"Cc",
|
||||
"Close",
|
||||
"Comments",
|
||||
"Compliance",
|
||||
"Connection",
|
||||
"Content-Alternative",
|
||||
"Content-Base",
|
||||
"Content-Description",
|
||||
"Content-Disposition",
|
||||
"Content-Duration",
|
||||
"Content-Encoding",
|
||||
"Content-features",
|
||||
"Content-ID",
|
||||
"Content-Identifier",
|
||||
"Content-Language",
|
||||
"Content-Length",
|
||||
"Content-Location",
|
||||
"Content-MD5",
|
||||
"Content-Range",
|
||||
"Content-Return",
|
||||
"Content-Script-Type",
|
||||
"Content-Style-Type",
|
||||
"Content-Transfer-Encoding",
|
||||
"Content-Type",
|
||||
"Content-Version",
|
||||
"Control",
|
||||
"Conversion",
|
||||
"Conversion-With-Loss",
|
||||
"Cookie",
|
||||
"Cookie2",
|
||||
"Cost",
|
||||
"DASL",
|
||||
"Date",
|
||||
"Date-Received",
|
||||
"DAV",
|
||||
"Default-Style",
|
||||
"Deferred-Delivery",
|
||||
"Delivery-Date",
|
||||
"Delta-Base",
|
||||
"Depth",
|
||||
"Derived-From",
|
||||
"Destination",
|
||||
"Differential-ID",
|
||||
"Digest",
|
||||
"Discarded-X400-IPMS-Extensions",
|
||||
"Discarded-X400-MTS-Extensions",
|
||||
"Disclose-Recipients",
|
||||
"Disposition-Notification-Options",
|
||||
"Disposition-Notification-To",
|
||||
"Distribution",
|
||||
"DKIM-Signature",
|
||||
"DL-Expansion-History",
|
||||
"Downgraded-Bcc",
|
||||
"Downgraded-Cc",
|
||||
"Downgraded-Disposition-Notification-To",
|
||||
"Downgraded-Final-Recipient",
|
||||
"Downgraded-From",
|
||||
"Downgraded-In-Reply-To",
|
||||
"Downgraded-Mail-From",
|
||||
"Downgraded-Message-Id",
|
||||
"Downgraded-Original-Recipient",
|
||||
"Downgraded-Rcpt-To",
|
||||
"Downgraded-References",
|
||||
"Downgraded-Reply-To",
|
||||
"Downgraded-Resent-Bcc",
|
||||
"Downgraded-Resent-Cc",
|
||||
"Downgraded-Resent-From",
|
||||
"Downgraded-Resent-Reply-To",
|
||||
"Downgraded-Resent-Sender",
|
||||
"Downgraded-Resent-To",
|
||||
"Downgraded-Return-Path",
|
||||
"Downgraded-Sender",
|
||||
"Downgraded-To",
|
||||
"EDIINT-Features",
|
||||
"Eesst-Version",
|
||||
"Encoding",
|
||||
"Encrypted",
|
||||
"Errors-To",
|
||||
"ETag",
|
||||
"Expect",
|
||||
"Expires",
|
||||
"Expiry-Date",
|
||||
"Ext",
|
||||
"Followup-To",
|
||||
"Forwarded",
|
||||
"From",
|
||||
"Generate-Delivery-Report",
|
||||
"GetProfile",
|
||||
"Hobareg",
|
||||
"Host",
|
||||
"HTTP2-Settings",
|
||||
"If",
|
||||
"If-Match",
|
||||
"If-Modified-Since",
|
||||
"If-None-Match",
|
||||
"If-Range",
|
||||
"If-Schedule-Tag-Match",
|
||||
"If-Unmodified-Since",
|
||||
"IM",
|
||||
"Importance",
|
||||
"In-Reply-To",
|
||||
"Incomplete-Copy",
|
||||
"Injection-Date",
|
||||
"Injection-Info",
|
||||
"Jabber-ID",
|
||||
"Keep-Alive",
|
||||
"Keywords",
|
||||
"Label",
|
||||
"Language",
|
||||
"Last-Modified",
|
||||
"Latest-Delivery-Time",
|
||||
"Lines",
|
||||
"Link",
|
||||
"List-Archive",
|
||||
"List-Help",
|
||||
"List-ID",
|
||||
"List-Owner",
|
||||
"List-Post",
|
||||
"List-Subscribe",
|
||||
"List-Unsubscribe",
|
||||
"List-Unsubscribe-Post",
|
||||
"Location",
|
||||
"Lock-Token",
|
||||
"Man",
|
||||
"Max-Forwards",
|
||||
"Memento-Datetime",
|
||||
"Message-Context",
|
||||
"Message-ID",
|
||||
"Message-Type",
|
||||
"Meter",
|
||||
"Method-Check",
|
||||
"Method-Check-Expires",
|
||||
"MIME-Version",
|
||||
"MMHS-Acp127-Message-Identifier",
|
||||
"MMHS-Authorizing-Users",
|
||||
"MMHS-Codress-Message-Indicator",
|
||||
"MMHS-Copy-Precedence",
|
||||
"MMHS-Exempted-Address",
|
||||
"MMHS-Extended-Authorisation-Info",
|
||||
"MMHS-Handling-Instructions",
|
||||
"MMHS-Message-Instructions",
|
||||
"MMHS-Message-Type",
|
||||
"MMHS-Originator-PLAD",
|
||||
"MMHS-Originator-Reference",
|
||||
"MMHS-Other-Recipients-Indicator-CC",
|
||||
"MMHS-Other-Recipients-Indicator-To",
|
||||
"MMHS-Primary-Precedence",
|
||||
"MMHS-Subject-Indicator-Codes",
|
||||
"MT-Priority",
|
||||
"Negotiate",
|
||||
"Newsgroups",
|
||||
"NNTP-Posting-Date",
|
||||
"NNTP-Posting-Host",
|
||||
"Non-Compliance",
|
||||
"Obsoletes",
|
||||
"Opt",
|
||||
"Optional",
|
||||
"Optional-WWW-Authenticate",
|
||||
"Ordering-Type",
|
||||
"Organization",
|
||||
"Origin",
|
||||
"Original-Encoded-Information-Types",
|
||||
"Original-From",
|
||||
"Original-Message-ID",
|
||||
"Original-Recipient",
|
||||
"Original-Sender",
|
||||
"Original-Subject",
|
||||
"Originator-Return-Address",
|
||||
"Overwrite",
|
||||
"P3P",
|
||||
"Path",
|
||||
"PEP",
|
||||
"Pep-Info",
|
||||
"PICS-Label",
|
||||
"Position",
|
||||
"Posting-Version",
|
||||
"Pragma",
|
||||
"Prefer",
|
||||
"Preference-Applied",
|
||||
"Prevent-NonDelivery-Report",
|
||||
"Priority",
|
||||
"Privicon",
|
||||
"ProfileObject",
|
||||
"Protocol",
|
||||
"Protocol-Info",
|
||||
"Protocol-Query",
|
||||
"Protocol-Request",
|
||||
"Proxy-Authenticate",
|
||||
"Proxy-Authentication-Info",
|
||||
"Proxy-Authorization",
|
||||
"Proxy-Connection",
|
||||
"Proxy-Features",
|
||||
"Proxy-Instruction",
|
||||
"Public",
|
||||
"Public-Key-Pins",
|
||||
"Public-Key-Pins-Report-Only",
|
||||
"Range",
|
||||
"Received",
|
||||
"Received-SPF",
|
||||
"Redirect-Ref",
|
||||
"References",
|
||||
"Referer",
|
||||
"Referer-Root",
|
||||
"Relay-Version",
|
||||
"Reply-By",
|
||||
"Reply-To",
|
||||
"Require-Recipient-Valid-Since",
|
||||
"Resent-Bcc",
|
||||
"Resent-Cc",
|
||||
"Resent-Date",
|
||||
"Resent-From",
|
||||
"Resent-Message-ID",
|
||||
"Resent-Reply-To",
|
||||
"Resent-Sender",
|
||||
"Resent-To",
|
||||
"Resolution-Hint",
|
||||
"Resolver-Location",
|
||||
"Retry-After",
|
||||
"Return-Path",
|
||||
"Safe",
|
||||
"Schedule-Reply",
|
||||
"Schedule-Tag",
|
||||
"Sec-WebSocket-Accept",
|
||||
"Sec-WebSocket-Extensions",
|
||||
"Sec-WebSocket-Key",
|
||||
"Sec-WebSocket-Protocol",
|
||||
"Sec-WebSocket-Version",
|
||||
"Security-Scheme",
|
||||
"See-Also",
|
||||
"Sender",
|
||||
"Sensitivity",
|
||||
"Server",
|
||||
"Set-Cookie",
|
||||
"Set-Cookie2",
|
||||
"SetProfile",
|
||||
"SIO-Label",
|
||||
"SIO-Label-History",
|
||||
"SLUG",
|
||||
"SoapAction",
|
||||
"Solicitation",
|
||||
"Status-URI",
|
||||
"Strict-Transport-Security",
|
||||
"Subject",
|
||||
"SubOK",
|
||||
"Subst",
|
||||
"Summary",
|
||||
"Supersedes",
|
||||
"Surrogate-Capability",
|
||||
"Surrogate-Control",
|
||||
"TCN",
|
||||
"TE",
|
||||
"Timeout",
|
||||
"Title",
|
||||
"To",
|
||||
"Topic",
|
||||
"Trailer",
|
||||
"Transfer-Encoding",
|
||||
"TTL",
|
||||
"UA-Color",
|
||||
"UA-Media",
|
||||
"UA-Pixels",
|
||||
"UA-Resolution",
|
||||
"UA-Windowpixels",
|
||||
"Upgrade",
|
||||
"Urgency",
|
||||
"URI",
|
||||
"User-Agent",
|
||||
"Variant-Vary",
|
||||
"Vary",
|
||||
"VBR-Info",
|
||||
"Version",
|
||||
"Via",
|
||||
"Want-Digest",
|
||||
"Warning",
|
||||
"WWW-Authenticate",
|
||||
"X-Archived-At",
|
||||
"X-Device-Accept",
|
||||
"X-Device-Accept-Charset",
|
||||
"X-Device-Accept-Encoding",
|
||||
"X-Device-Accept-Language",
|
||||
"X-Device-User-Agent",
|
||||
"X-Frame-Options",
|
||||
"X-Mittente",
|
||||
"X-PGP-Sig",
|
||||
"X-Ricevuta",
|
||||
"X-Riferimento-Message-ID",
|
||||
"X-TipoRicevuta",
|
||||
"X-Trasporto",
|
||||
"X-VerificaSicurezza",
|
||||
"X400-Content-Identifier",
|
||||
"X400-Content-Return",
|
||||
"X400-Content-Type",
|
||||
"X400-MTS-Identifier",
|
||||
"X400-Originator",
|
||||
"X400-Received",
|
||||
"X400-Recipients",
|
||||
"X400-Trace",
|
||||
"Xref"
|
||||
}})
|
||||
{
|
||||
// find the longest field length
|
||||
std::size_t high = 0;
|
||||
for(auto const& s : by_name_)
|
||||
if(high < s.size())
|
||||
high = s.size();
|
||||
// build by_size map
|
||||
// skip field::unknown
|
||||
by_size_.resize(high + 1);
|
||||
for(auto& map : by_size_)
|
||||
map.max_load_factor(.15f);
|
||||
for(std::size_t i = 1;
|
||||
i < by_name_.size(); ++i)
|
||||
{
|
||||
auto const& s = by_name_[i];
|
||||
by_size_[s.size()].emplace(
|
||||
s, static_cast<field>(i));
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This snippet calculates the performance
|
||||
// of the hash function and map settings
|
||||
{
|
||||
std::vector<std::size_t> hist;
|
||||
for(auto const& map : by_size_)
|
||||
{
|
||||
for(std::size_t i = 0; i < map.bucket_count(); ++i)
|
||||
{
|
||||
auto const n = map.bucket_size(i);
|
||||
if(n > 0)
|
||||
{
|
||||
if(hist.size() < n)
|
||||
hist.resize(n);
|
||||
++hist[n-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
field
|
||||
string_to_field(string_view s) const
|
||||
{
|
||||
if(s.size() >= by_size_.size())
|
||||
return field::unknown;
|
||||
auto const& map = by_size_[s.size()];
|
||||
if(map.empty())
|
||||
return field::unknown;
|
||||
auto it = map.find(s);
|
||||
if(it == map.end())
|
||||
return field::unknown;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//
|
||||
// Deprecated
|
||||
//
|
||||
|
||||
using const_iterator =
|
||||
array_type::const_iterator;
|
||||
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return by_name_.size();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return by_name_.begin();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return by_name_.end();
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
field_table const&
|
||||
get_field_table()
|
||||
{
|
||||
static field_table const tab;
|
||||
return tab;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
string_view
|
||||
to_string(field f)
|
||||
{
|
||||
auto const& v = get_field_table();
|
||||
BOOST_ASSERT(static_cast<unsigned>(f) < v.size());
|
||||
return v.begin()[static_cast<unsigned>(f)];
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
string_view
|
||||
to_string(field f)
|
||||
{
|
||||
return detail::to_string(f);
|
||||
}
|
||||
|
||||
inline
|
||||
field
|
||||
string_to_field(string_view s)
|
||||
{
|
||||
return detail::get_field_table().string_to_field(s);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,585 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
|
||||
|
||||
#if BOOST_BEAST_USE_WIN32_FILE
|
||||
|
||||
#include <boost/beast/core/async_result.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <boost/asio/basic_stream_socket.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/asio/windows/overlapped_ptr.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
#include <boost/smart_ptr/make_shared_array.hpp>
|
||||
#include <boost/detail/winapi/basic_types.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
template<class, class, bool, class>
|
||||
class write_some_win32_op;
|
||||
} // detail
|
||||
|
||||
template<>
|
||||
struct basic_file_body<file_win32>
|
||||
{
|
||||
using file_type = file_win32;
|
||||
|
||||
class reader;
|
||||
class writer;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class value_type
|
||||
{
|
||||
friend class reader;
|
||||
friend class writer;
|
||||
friend struct basic_file_body<file_win32>;
|
||||
|
||||
template<class, class, bool, class>
|
||||
friend class detail::write_some_win32_op;
|
||||
template<
|
||||
class Protocol, bool isRequest, class Fields>
|
||||
friend
|
||||
void
|
||||
write_some(
|
||||
boost::asio::basic_stream_socket<Protocol>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
file_win32 file_;
|
||||
std::uint64_t size_ = 0; // cached file size
|
||||
std::uint64_t first_; // starting offset of the range
|
||||
std::uint64_t last_; // ending offset of the range
|
||||
|
||||
public:
|
||||
~value_type() = default;
|
||||
value_type() = default;
|
||||
value_type(value_type&& other) = default;
|
||||
value_type& operator=(value_type&& other) = default;
|
||||
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
void
|
||||
close();
|
||||
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
void
|
||||
reset(file_win32&& file, error_code& ec);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class reader
|
||||
{
|
||||
template<class, class, bool, class>
|
||||
friend class detail::write_some_win32_op;
|
||||
template<
|
||||
class Protocol, bool isRequest, class Fields>
|
||||
friend
|
||||
void
|
||||
write_some(
|
||||
boost::asio::basic_stream_socket<Protocol>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
value_type& body_; // The body we are reading from
|
||||
std::uint64_t pos_; // The current position in the file
|
||||
char buf_[4096]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
reader(message<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&)
|
||||
{
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
pos_ = body_.first_;
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
std::size_t const n = (std::min)(sizeof(buf_),
|
||||
beast::detail::clamp(body_.last_ - pos_));
|
||||
if(n == 0)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
auto const nread = body_.file_.read(buf_, n, ec);
|
||||
if(ec)
|
||||
return boost::none;
|
||||
BOOST_ASSERT(nread != 0);
|
||||
pos_ += nread;
|
||||
ec.assign(0, ec.category());
|
||||
return {{
|
||||
{buf_, nread}, // buffer to return.
|
||||
pos_ < body_.last_}}; // `true` if there are more buffers.
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, basic_file_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
// VFALCO We could reserve space in the file
|
||||
boost::ignore_unused(content_length);
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
std::size_t nwritten = 0;
|
||||
for(boost::asio::const_buffer buffer : buffers)
|
||||
{
|
||||
nwritten += body_.file_.write(
|
||||
boost::asio::buffer_cast<void const*>(buffer),
|
||||
boost::asio::buffer_size(buffer),
|
||||
ec);
|
||||
if(ec)
|
||||
return nwritten;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body)
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
close()
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
file_.open(path, mode, ec);
|
||||
if(ec)
|
||||
return;
|
||||
size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
first_ = 0;
|
||||
last_ = size_;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
reset(file_win32&& file, error_code& ec)
|
||||
{
|
||||
if(file_.is_open())
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
file_ = std::move(file);
|
||||
if(file_.is_open())
|
||||
{
|
||||
size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
first_ = 0;
|
||||
last_ = size_;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Unsigned>
|
||||
inline
|
||||
boost::detail::winapi::DWORD_
|
||||
lowPart(Unsigned n)
|
||||
{
|
||||
return static_cast<
|
||||
boost::detail::winapi::DWORD_>(
|
||||
n & 0xffffffff);
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
inline
|
||||
boost::detail::winapi::DWORD_
|
||||
highPart(Unsigned n, std::true_type)
|
||||
{
|
||||
return static_cast<
|
||||
boost::detail::winapi::DWORD_>(
|
||||
(n>>32)&0xffffffff);
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
inline
|
||||
boost::detail::winapi::DWORD_
|
||||
highPart(Unsigned, std::false_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
inline
|
||||
boost::detail::winapi::DWORD_
|
||||
highPart(Unsigned n)
|
||||
{
|
||||
return highPart(n, std::integral_constant<
|
||||
bool, (sizeof(Unsigned)>4)>{});
|
||||
}
|
||||
|
||||
class null_lambda
|
||||
{
|
||||
public:
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code&,
|
||||
ConstBufferSequence const&) const
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
|
||||
|
||||
template<
|
||||
class Protocol, class Handler,
|
||||
bool isRequest, class Fields>
|
||||
class write_some_win32_op
|
||||
{
|
||||
boost::asio::basic_stream_socket<Protocol>& sock_;
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr_;
|
||||
bool header_ = false;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
write_some_win32_op(write_some_win32_op&&) = default;
|
||||
write_some_win32_op(write_some_win32_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_some_win32_op(
|
||||
DeducedHandler&& h,
|
||||
boost::asio::basic_stream_socket<Protocol>& s,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>,Fields>& sr)
|
||||
: sock_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()();
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_some_win32_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_some_win32_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_some_win32_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_some_win32_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class Protocol, class Handler,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
write_some_win32_op<
|
||||
Protocol, Handler, isRequest, Fields>::
|
||||
operator()()
|
||||
{
|
||||
if(! sr_.is_header_done())
|
||||
{
|
||||
header_ = true;
|
||||
sr_.split(true);
|
||||
return detail::async_write_some(
|
||||
sock_, sr_, std::move(*this));
|
||||
}
|
||||
if(sr_.chunked())
|
||||
{
|
||||
return detail::async_write_some(
|
||||
sock_, sr_, std::move(*this));
|
||||
}
|
||||
auto& r = sr_.reader_impl();
|
||||
boost::detail::winapi::DWORD_ const nNumberOfBytesToWrite =
|
||||
std::min<boost::detail::winapi::DWORD_>(
|
||||
beast::detail::clamp(std::min<std::uint64_t>(
|
||||
r.body_.last_ - r.pos_, sr_.limit())),
|
||||
2147483646);
|
||||
boost::asio::windows::overlapped_ptr overlapped{
|
||||
sock_.get_io_service(), *this};
|
||||
auto& ov = *overlapped.get();
|
||||
ov.Offset = lowPart(r.pos_);
|
||||
ov.OffsetHigh = highPart(r.pos_);
|
||||
auto const bSuccess = ::TransmitFile(
|
||||
sock_.native_handle(),
|
||||
sr_.get().body.file_.native_handle(),
|
||||
nNumberOfBytesToWrite,
|
||||
0,
|
||||
overlapped.get(),
|
||||
nullptr,
|
||||
0);
|
||||
auto const dwError = ::GetLastError();
|
||||
if(! bSuccess && dwError !=
|
||||
boost::detail::winapi::ERROR_IO_PENDING_)
|
||||
{
|
||||
// completed immediately
|
||||
overlapped.complete(error_code{static_cast<int>(
|
||||
boost::detail::winapi::GetLastError()),
|
||||
system_category()}, 0);
|
||||
return;
|
||||
}
|
||||
overlapped.release();
|
||||
}
|
||||
|
||||
template<
|
||||
class Protocol, class Handler,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
write_some_win32_op<
|
||||
Protocol, Handler, isRequest, Fields>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
{
|
||||
if(header_)
|
||||
{
|
||||
header_ = false;
|
||||
return (*this)();
|
||||
}
|
||||
auto& r = sr_.reader_impl();
|
||||
r.pos_ += bytes_transferred;
|
||||
BOOST_ASSERT(r.pos_ <= r.body_.last_);
|
||||
if(r.pos_ >= r.body_.last_)
|
||||
{
|
||||
sr_.next(ec, null_lambda{});
|
||||
BOOST_ASSERT(! ec);
|
||||
BOOST_ASSERT(sr_.is_done());
|
||||
if(! sr_.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
}
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Protocol, bool isRequest, class Fields>
|
||||
void
|
||||
write_some(
|
||||
boost::asio::basic_stream_socket<Protocol>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
if(! sr.is_header_done())
|
||||
{
|
||||
sr.split(true);
|
||||
detail::write_some(sock, sr, ec);
|
||||
if(ec)
|
||||
return;
|
||||
return;
|
||||
}
|
||||
if(sr.chunked())
|
||||
{
|
||||
detail::write_some(sock, sr, ec);
|
||||
if(ec)
|
||||
return;
|
||||
return;
|
||||
}
|
||||
auto& r = sr.reader_impl();
|
||||
r.body_.file_.seek(r.pos_, ec);
|
||||
if(ec)
|
||||
return;
|
||||
boost::detail::winapi::DWORD_ const nNumberOfBytesToWrite =
|
||||
std::min<boost::detail::winapi::DWORD_>(
|
||||
beast::detail::clamp(std::min<std::uint64_t>(
|
||||
r.body_.last_ - r.pos_, sr.limit())),
|
||||
2147483646);
|
||||
auto const bSuccess = ::TransmitFile(
|
||||
sock.native_handle(),
|
||||
r.body_.file_.native_handle(),
|
||||
nNumberOfBytesToWrite,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0);
|
||||
if(! bSuccess)
|
||||
{
|
||||
ec.assign(static_cast<int>(
|
||||
boost::detail::winapi::GetLastError()),
|
||||
system_category());
|
||||
return;
|
||||
}
|
||||
r.pos_ += nNumberOfBytesToWrite;
|
||||
BOOST_ASSERT(r.pos_ <= r.body_.last_);
|
||||
if(r.pos_ < r.body_.last_)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
else
|
||||
{
|
||||
sr.next(ec, detail::null_lambda{});
|
||||
BOOST_ASSERT(! ec);
|
||||
BOOST_ASSERT(sr.is_done());
|
||||
if(! sr.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
}
|
||||
|
||||
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
|
||||
|
||||
template<
|
||||
class Protocol,
|
||||
bool isRequest, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_some(
|
||||
boost::asio::basic_stream_socket<Protocol>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_some_win32_op<Protocol, handler_type<
|
||||
WriteHandler, void(error_code)>, isRequest, Fields>{
|
||||
init.completion_handler, sock, sr}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,419 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Fields>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
header<true, Fields>::
|
||||
header(Arg1&& arg1, ArgN&&... argn)
|
||||
: Fields(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
verb
|
||||
header<true, Fields>::
|
||||
method() const
|
||||
{
|
||||
return method_;
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<true, Fields>::
|
||||
method(verb v)
|
||||
{
|
||||
if(v == verb::unknown)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument{"unknown method"});
|
||||
method_ = v;
|
||||
this->set_method_impl({});
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
string_view
|
||||
header<true, Fields>::
|
||||
method_string() const
|
||||
{
|
||||
if(method_ != verb::unknown)
|
||||
return to_string(method_);
|
||||
return this->get_method_impl();
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<true, Fields>::
|
||||
method_string(string_view s)
|
||||
{
|
||||
method_ = string_to_verb(s);
|
||||
if(method_ != verb::unknown)
|
||||
this->set_method_impl({});
|
||||
else
|
||||
this->set_method_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
string_view
|
||||
header<true, Fields>::
|
||||
target() const
|
||||
{
|
||||
return this->get_target_impl();
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
void
|
||||
header<true, Fields>::
|
||||
target(string_view s)
|
||||
{
|
||||
this->set_target_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<true, Fields>& h1,
|
||||
header<true, Fields>& h2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<Fields&>(h1),
|
||||
static_cast<Fields&>(h2));
|
||||
swap(h1.version, h2.version);
|
||||
swap(h1.method_, h2.method_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Fields>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
header<false, Fields>::
|
||||
header(Arg1&& arg1, ArgN&&... argn)
|
||||
: Fields(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<class Fields>
|
||||
template<class... Args>
|
||||
header<false, Fields>::
|
||||
header(status result, unsigned version_, Args&&... args)
|
||||
: Fields(std::forward<Args>(args)...)
|
||||
, version(version_)
|
||||
, result_(result)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
status
|
||||
header<false, Fields>::
|
||||
result() const
|
||||
{
|
||||
return int_to_status(
|
||||
static_cast<int>(result_));
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
void
|
||||
header<false, Fields>::
|
||||
result(status v)
|
||||
{
|
||||
result_ = v;
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
void
|
||||
header<false, Fields>::
|
||||
result(unsigned v)
|
||||
{
|
||||
if(v > 999)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument{
|
||||
"invalid status-code"});
|
||||
result_ = static_cast<status>(v);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
unsigned
|
||||
header<false, Fields>::
|
||||
result_int() const
|
||||
{
|
||||
return static_cast<unsigned>(result_);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
string_view
|
||||
header<false, Fields>::
|
||||
reason() const
|
||||
{
|
||||
auto const s = this->get_reason_impl();
|
||||
if(! s.empty())
|
||||
return s;
|
||||
return obsolete_reason(result_);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
void
|
||||
header<false, Fields>::
|
||||
reason(string_view s)
|
||||
{
|
||||
this->set_reason_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<false, Fields>& h1,
|
||||
header<false, Fields>& h2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<Fields&>(h1),
|
||||
static_cast<Fields&>(h2));
|
||||
swap(h1.version, h2.version);
|
||||
swap(h1.result_, h2.result_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(header_type&& h, BodyArgs&&... body_args)
|
||||
: header_type(std::move(h))
|
||||
, body(std::forward<BodyArgs>(body_args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(header_type const& h, BodyArgs&&... body_args)
|
||||
: header_type(h)
|
||||
, body(std::forward<BodyArgs>(body_args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(verb method, string_view target, Version version)
|
||||
: header_type(method, target, version)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(verb method, string_view target,
|
||||
Version version, BodyArg&& body_arg)
|
||||
: header_type(method, target, version)
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class FieldsArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(
|
||||
verb method, string_view target, Version version,
|
||||
BodyArg&& body_arg,
|
||||
FieldsArg&& fields_arg)
|
||||
: header_type(method, target, version,
|
||||
std::forward<FieldsArg>(fields_arg))
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version)
|
||||
: header_type(result, version)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version,
|
||||
BodyArg&& body_arg)
|
||||
: header_type(result, version)
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class FieldsArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version,
|
||||
BodyArg&& body_arg, FieldsArg&& fields_arg)
|
||||
: header_type(result, version,
|
||||
std::forward<FieldsArg>(fields_arg))
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...> body_args)
|
||||
: message(std::piecewise_construct,
|
||||
body_args,
|
||||
beast::detail::make_index_sequence<
|
||||
sizeof...(BodyArgs)>{})
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs, class... FieldsArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...> body_args,
|
||||
std::tuple<FieldsArgs...> fields_args)
|
||||
: message(std::piecewise_construct,
|
||||
body_args,
|
||||
fields_args,
|
||||
beast::detail::make_index_sequence<
|
||||
sizeof...(BodyArgs)>{},
|
||||
beast::detail::make_index_sequence<
|
||||
sizeof...(FieldsArgs)>{})
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
chunked(bool value)
|
||||
{
|
||||
this->set_chunked_impl(value);
|
||||
this->set_content_length_impl(boost::none);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
content_length(
|
||||
boost::optional<std::uint64_t> const& value)
|
||||
{
|
||||
this->set_content_length_impl(value);
|
||||
this->set_chunked_impl(false);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
boost::optional<std::uint64_t>
|
||||
message<isRequest, Body, Fields>::
|
||||
payload_size() const
|
||||
{
|
||||
return payload_size(detail::is_body_sized<Body>{});
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_payload(std::true_type)
|
||||
{
|
||||
auto const n = payload_size();
|
||||
if(this->method() == verb::trace && (! n || *n > 0))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid request body"});
|
||||
if(n)
|
||||
{
|
||||
if(*n > 0 ||
|
||||
this->method() == verb::options ||
|
||||
this->method() == verb::put ||
|
||||
this->method() == verb::post)
|
||||
{
|
||||
this->content_length(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->chunked(false);
|
||||
}
|
||||
}
|
||||
else if(this->version >= 11)
|
||||
{
|
||||
this->chunked(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->chunked(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_payload(std::false_type)
|
||||
{
|
||||
auto const n = payload_size();
|
||||
if((status_class(this->result()) == status_class::informational ||
|
||||
this->result() == status::no_content ||
|
||||
this->result() == status::not_modified))
|
||||
{
|
||||
if(! n || *n > 0)
|
||||
// The response body MUST BE empty for this case
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid response body"});
|
||||
}
|
||||
if(n)
|
||||
this->content_length(n);
|
||||
else
|
||||
this->chunked(true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
swap(
|
||||
message<isRequest, Body, Fields>& m1,
|
||||
message<isRequest, Body, Fields>& m2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<header<isRequest, Fields>&>(m1),
|
||||
static_cast<header<isRequest, Fields>&>(m2));
|
||||
swap(m1.body, m2.body);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_PARSER_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_PARSER_IPP
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser()
|
||||
: wr_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser(Arg1&& arg1, ArgN&&... argn)
|
||||
: m_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
, wr_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
template<class OtherBody, class... Args, class>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser(parser<isRequest, OtherBody, Allocator>&& other,
|
||||
Args&&... args)
|
||||
: base_type(std::move(other))
|
||||
, m_(other.release(), std::forward<Args>(args)...)
|
||||
, wr_(m_)
|
||||
{
|
||||
if(other.wr_inited_)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"moved-from parser has a body"});
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,796 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/read_size.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
class read_some_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest, Derived>& p_;
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> mb_;
|
||||
std::size_t used_ = 0;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_some_op(read_some_op&&) = default;
|
||||
read_some_op(read_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
read_some_op(DeducedHandler&& h, Stream& s,
|
||||
DynamicBuffer& b, basic_parser<isRequest, Derived>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 2 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
void
|
||||
read_some_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
state_ = 1;
|
||||
if(b_.size() == 0)
|
||||
goto do_read;
|
||||
goto do_parse;
|
||||
|
||||
case 1:
|
||||
state_ = 2;
|
||||
case 2:
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(p_.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec.assign(0, ec.category());
|
||||
p_.put_eof(ec);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
BOOST_ASSERT(p_.is_done());
|
||||
goto upcall;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
goto upcall;
|
||||
}
|
||||
if(ec)
|
||||
goto upcall;
|
||||
b_.commit(bytes_transferred);
|
||||
|
||||
do_parse:
|
||||
{
|
||||
auto const used = p_.put(b_.data(), ec);
|
||||
used_ += used;
|
||||
b_.consume(used);
|
||||
if(! ec || ec != http::error::need_more)
|
||||
goto do_upcall;
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
do_read:
|
||||
try
|
||||
{
|
||||
mb_.emplace(b_.prepare(
|
||||
read_size_or_throw(b_, 65536)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
goto do_upcall;
|
||||
}
|
||||
return s_.async_read_some(*mb_, std::move(*this));
|
||||
|
||||
do_upcall:
|
||||
if(state_ >= 2)
|
||||
goto upcall;
|
||||
state_ = 3;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
upcall:
|
||||
h_(ec, used_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct parser_is_done
|
||||
{
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
operator()(basic_parser<
|
||||
isRequest, Derived> const& p) const
|
||||
{
|
||||
return p.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct parser_is_header_done
|
||||
{
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
operator()(basic_parser<
|
||||
isRequest, Derived> const& p) const
|
||||
{
|
||||
return p.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Condition,
|
||||
class Handler>
|
||||
class read_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest, Derived>& p_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
read_op(DeducedHandler&& h, Stream& s,
|
||||
DynamicBuffer& b, basic_parser<isRequest,
|
||||
Derived>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_used = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 3 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Condition,
|
||||
class Handler>
|
||||
void
|
||||
read_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Condition, Handler>::
|
||||
operator()(error_code ec, std::size_t)
|
||||
{
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
if(Condition{}(p_))
|
||||
{
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec));
|
||||
}
|
||||
state_ = 2;
|
||||
|
||||
do_read:
|
||||
return async_read_some(
|
||||
s_, b_, p_, std::move(*this));
|
||||
|
||||
case 1:
|
||||
goto upcall;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(Condition{}(p_))
|
||||
goto upcall;
|
||||
state_ = 3;
|
||||
goto do_read;
|
||||
}
|
||||
upcall:
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
class Handler>
|
||||
class read_msg_op
|
||||
{
|
||||
using parser_type =
|
||||
parser<isRequest, Body, Allocator>;
|
||||
|
||||
using message_type =
|
||||
typename parser_type::value_type;
|
||||
|
||||
struct data
|
||||
{
|
||||
int state = 0;
|
||||
Stream& s;
|
||||
DynamicBuffer& b;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
|
||||
data(Handler&, Stream& s_,
|
||||
DynamicBuffer& b_, message_type& m_)
|
||||
: s(s_)
|
||||
, b(b_)
|
||||
, m(m_)
|
||||
, p(std::move(m))
|
||||
{
|
||||
p.eager(true);
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_msg_op(read_msg_op&&) = default;
|
||||
read_msg_op(read_msg_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_used = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->d_->state >= 2 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
class Handler>
|
||||
void
|
||||
read_msg_op<Stream, DynamicBuffer,
|
||||
isRequest, Body, Allocator, Handler>::
|
||||
operator()(error_code ec, std::size_t)
|
||||
{
|
||||
auto& d = *d_;
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
|
||||
do_read:
|
||||
return async_read_some(
|
||||
d.s, d.b, d.p, std::move(*this));
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(d.p.is_done())
|
||||
{
|
||||
d.m = d.p.release();
|
||||
goto upcall;
|
||||
}
|
||||
d.state = 2;
|
||||
goto do_read;
|
||||
}
|
||||
upcall:
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
error_code ec;
|
||||
auto const bytes_used = read_some(
|
||||
stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_used;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
std::size_t bytes_used = 0;
|
||||
if(buffer.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
// invoke parser
|
||||
{
|
||||
auto const used = parser.put(buffer.data(), ec);
|
||||
bytes_used += used;
|
||||
buffer.consume(used);
|
||||
if(! ec)
|
||||
break;
|
||||
if(ec != http::error::need_more)
|
||||
break;
|
||||
}
|
||||
do_read:
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> b;
|
||||
try
|
||||
{
|
||||
b.emplace(buffer.prepare(
|
||||
read_size_or_throw(buffer, 65536)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return bytes_used;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*b, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(parser.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
parser.put_eof(ec);
|
||||
if(ec)
|
||||
break;
|
||||
BOOST_ASSERT(parser.is_done());
|
||||
break;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
break;
|
||||
buffer.commit(bytes_transferred);
|
||||
}
|
||||
return bytes_used;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code, std::size_t)>
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
async_completion<ReadHandler, void(error_code)> init{handler};
|
||||
detail::read_some_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, Derived, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
error_code ec;
|
||||
read_header(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(false);
|
||||
if(parser.is_header_done())
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
read_some(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
while(! parser.is_header_done());
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(false);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Derived, detail::parser_is_header_done,
|
||||
handler_type<ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
error_code ec;
|
||||
read(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(true);
|
||||
if(parser.is_done())
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
read_some(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
while(! parser.is_done());
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(true);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Derived, detail::parser_is_done,
|
||||
handler_type<ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
error_code ec;
|
||||
read(stream, buffer, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
parser<isRequest, Body, Allocator> p{std::move(msg)};
|
||||
p.eager(true);
|
||||
read(stream, buffer, p.base(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
msg = p.release();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
class ReadHandler>
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_msg_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Body, Allocator, handler_type<
|
||||
ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, msg}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,572 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <iterator>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class param_list::const_iterator
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
std::string s_;
|
||||
detail::param_iter pi_;
|
||||
|
||||
public:
|
||||
using value_type = param_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.pi_.it == pi_.it &&
|
||||
other.pi_.last == pi_.last &&
|
||||
other.pi_.first == pi_.first;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return pi_.v;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class param_list;
|
||||
|
||||
const_iterator(iter_type first, iter_type last)
|
||||
{
|
||||
pi_.it = first;
|
||||
pi_.first = first;
|
||||
pi_.last = last;
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
static
|
||||
std::string
|
||||
unquote(string_view sr);
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class>
|
||||
std::string
|
||||
param_list::const_iterator::
|
||||
unquote(string_view sr)
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(sr.size());
|
||||
auto it = sr.begin() + 1;
|
||||
auto end = sr.end() - 1;
|
||||
while(it != end)
|
||||
{
|
||||
if(*it == '\\')
|
||||
++it;
|
||||
s.push_back(*it);
|
||||
++it;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
param_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
s_.clear();
|
||||
pi_.increment();
|
||||
if(pi_.empty())
|
||||
{
|
||||
pi_.it = pi_.last;
|
||||
pi_.first = pi_.last;
|
||||
}
|
||||
else if(! pi_.v.second.empty() &&
|
||||
pi_.v.second.front() == '"')
|
||||
{
|
||||
s_ = unquote(pi_.v.second);
|
||||
pi_.v.second = string_view{
|
||||
s_.data(), s_.size()};
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ext_list::const_iterator
|
||||
{
|
||||
ext_list::value_type v_;
|
||||
iter_type it_;
|
||||
iter_type first_;
|
||||
iter_type last_;
|
||||
|
||||
public:
|
||||
using value_type = ext_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.it_ == it_ &&
|
||||
other.first_ == first_ &&
|
||||
other.last_ == last_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ext_list;
|
||||
|
||||
const_iterator(iter_type begin, iter_type end)
|
||||
{
|
||||
it_ = begin;
|
||||
first_ = begin;
|
||||
last_ = end;
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
auto
|
||||
ext_list::
|
||||
find(T const& s) ->
|
||||
const_iterator
|
||||
{
|
||||
return std::find_if(begin(), end(),
|
||||
[&s](value_type const& v)
|
||||
{
|
||||
return iequals(s, v.first);
|
||||
});
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool
|
||||
ext_list::
|
||||
exists(T const& s)
|
||||
{
|
||||
return find(s) != end();
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
ext_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-list
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS "=" OWS ( token / quoted-string )
|
||||
|
||||
chunked;a=b;i=j,gzip;windowBits=12
|
||||
x,y
|
||||
,,,,,chameleon
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_.first = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_.first = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
detail::param_iter pi;
|
||||
pi.it = it_;
|
||||
pi.first = it_;
|
||||
pi.last = last_;
|
||||
for(;;)
|
||||
{
|
||||
pi.increment();
|
||||
if(pi.empty())
|
||||
break;
|
||||
}
|
||||
v_.second = param_list{string_view{&*it_,
|
||||
static_cast<std::size_t>(pi.it - it_)}};
|
||||
it_ = pi.it;
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class token_list::const_iterator
|
||||
{
|
||||
token_list::value_type v_;
|
||||
iter_type it_;
|
||||
iter_type first_;
|
||||
iter_type last_;
|
||||
|
||||
public:
|
||||
using value_type = token_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.it_ == it_ &&
|
||||
other.first_ == first_ &&
|
||||
other.last_ == last_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class token_list;
|
||||
|
||||
const_iterator(iter_type begin, iter_type end)
|
||||
{
|
||||
it_ = begin;
|
||||
first_ = begin;
|
||||
last_ = end;
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
token_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_ = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_ = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool
|
||||
token_list::
|
||||
exists(T const& s)
|
||||
{
|
||||
return std::find_if(begin(), end(),
|
||||
[&s](value_type const& v)
|
||||
{
|
||||
return iequals(s, v);
|
||||
}
|
||||
) != end();
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
bool
|
||||
validate_list(detail::basic_parsed_list<
|
||||
Policy> const& list)
|
||||
{
|
||||
auto const last = list.end();
|
||||
auto it = list.begin();
|
||||
if(it.error())
|
||||
return false;
|
||||
while(it != last)
|
||||
{
|
||||
++it;
|
||||
if(it.error())
|
||||
return false;
|
||||
if(it == last)
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP
|
||||
|
||||
#include <boost/beast/core/detail/buffers_ref.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
frdinit(std::true_type)
|
||||
{
|
||||
frd_.emplace(m_, m_.version, m_.method());
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
frdinit(std::false_type)
|
||||
{
|
||||
frd_.emplace(m_, m_.version, m_.result_int());
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
template<class T1, class T2, class Visit>
|
||||
inline
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
do_visit(error_code& ec, Visit& visit)
|
||||
{
|
||||
// VFALCO work-around for missing variant::emplace
|
||||
pv_.~variant();
|
||||
new(&pv_) decltype(pv_){
|
||||
T1{limit_, boost::get<T2>(v_)}};
|
||||
visit(ec, beast::detail::make_buffers_ref(
|
||||
boost::get<T1>(pv_)));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
serializer<isRequest, Body, Fields>::
|
||||
serializer(value_type& m)
|
||||
: m_(m)
|
||||
, rd_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
template<class Visit>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
next(error_code& ec, Visit&& visit)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using beast::detail::make_buffers_ref;
|
||||
switch(s_)
|
||||
{
|
||||
case do_construct:
|
||||
{
|
||||
frdinit(std::integral_constant<bool,
|
||||
isRequest>{});
|
||||
keep_alive_ = m_.keep_alive();
|
||||
chunked_ = m_.chunked();
|
||||
if(chunked_)
|
||||
goto go_init_c;
|
||||
s_ = do_init;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_init:
|
||||
{
|
||||
rd_.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(split_)
|
||||
goto go_header_only;
|
||||
auto result = rd_.get(ec);
|
||||
if(ec == error::need_more)
|
||||
goto go_header_only;
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_header_only;
|
||||
more_ = result->second;
|
||||
v_ = cb2_t{
|
||||
boost::in_place_init,
|
||||
frd_->get(),
|
||||
result->first};
|
||||
s_ = do_header;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_header:
|
||||
do_visit<pcb2_t, cb2_t>(ec, visit);
|
||||
break;
|
||||
|
||||
go_header_only:
|
||||
v_ = cb1_t{frd_->get()};
|
||||
s_ = do_header_only;
|
||||
case do_header_only:
|
||||
do_visit<pcb1_t, cb1_t>(ec, visit);
|
||||
break;
|
||||
|
||||
case do_body:
|
||||
s_ = do_body + 1;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case do_body + 1:
|
||||
{
|
||||
auto result = rd_.get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_complete;
|
||||
more_ = result->second;
|
||||
v_ = cb3_t{result->first};
|
||||
s_ = do_body + 2;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_body + 2:
|
||||
do_visit<pcb3_t, cb3_t>(ec, visit);
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_init_c:
|
||||
s_ = do_init_c;
|
||||
case do_init_c:
|
||||
{
|
||||
rd_.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(split_)
|
||||
goto go_header_only_c;
|
||||
auto result = rd_.get(ec);
|
||||
if(ec == error::need_more)
|
||||
goto go_header_only_c;
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_header_only_c;
|
||||
more_ = result->second;
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
if(! more_)
|
||||
{
|
||||
// do it all in one buffer
|
||||
v_ = cb7_t{
|
||||
boost::in_place_init,
|
||||
frd_->get(),
|
||||
buffer_size(result->first),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{},
|
||||
detail::chunk_last(),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{}};
|
||||
goto go_all_c;
|
||||
}
|
||||
#endif
|
||||
v_ = cb4_t{
|
||||
boost::in_place_init,
|
||||
frd_->get(),
|
||||
buffer_size(result->first),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{}};
|
||||
s_ = do_header_c;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_header_c:
|
||||
do_visit<pcb4_t, cb4_t>(ec, visit);
|
||||
break;
|
||||
|
||||
go_header_only_c:
|
||||
v_ = cb1_t{frd_->get()};
|
||||
s_ = do_header_only_c;
|
||||
case do_header_only_c:
|
||||
do_visit<pcb1_t, cb1_t>(ec, visit);
|
||||
break;
|
||||
|
||||
case do_body_c:
|
||||
s_ = do_body_c + 1;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case do_body_c + 1:
|
||||
{
|
||||
auto result = rd_.get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_final_c;
|
||||
more_ = result->second;
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
if(! more_)
|
||||
{
|
||||
// do it all in one buffer
|
||||
v_ = cb6_t{
|
||||
boost::in_place_init,
|
||||
buffer_size(result->first),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{},
|
||||
detail::chunk_last(),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{}};
|
||||
goto go_body_final_c;
|
||||
}
|
||||
#endif
|
||||
v_ = cb5_t{
|
||||
boost::in_place_init,
|
||||
buffer_size(result->first),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{}};
|
||||
s_ = do_body_c + 2;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
do_visit<pcb5_t, cb5_t>(ec, visit);
|
||||
break;
|
||||
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
go_body_final_c:
|
||||
s_ = do_body_final_c;
|
||||
case do_body_final_c:
|
||||
do_visit<pcb6_t, cb6_t>(ec, visit);
|
||||
break;
|
||||
|
||||
go_all_c:
|
||||
s_ = do_all_c;
|
||||
case do_all_c:
|
||||
do_visit<pcb7_t, cb7_t>(ec, visit);
|
||||
break;
|
||||
#endif
|
||||
|
||||
go_final_c:
|
||||
case do_final_c:
|
||||
v_ = cb8_t{
|
||||
boost::in_place_init,
|
||||
detail::chunk_last(),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{}};
|
||||
s_ = do_final_c + 1;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case do_final_c + 1:
|
||||
do_visit<pcb8_t, cb8_t>(ec, visit);
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
case do_complete:
|
||||
BOOST_ASSERT(false);
|
||||
break;
|
||||
|
||||
go_complete:
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
switch(s_)
|
||||
{
|
||||
case do_header:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb2_t>(v_)));
|
||||
boost::get<cb2_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb2_t>(v_)) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_ = boost::blank{};
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
|
||||
case do_header_only:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb1_t>(v_)));
|
||||
boost::get<cb1_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb1_t>(v_)) > 0)
|
||||
break;
|
||||
frd_ = boost::none;
|
||||
header_done_ = true;
|
||||
if(! split_)
|
||||
goto go_complete;
|
||||
s_ = do_body;
|
||||
break;
|
||||
|
||||
case do_body + 2:
|
||||
{
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb3_t>(v_)));
|
||||
boost::get<cb3_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb3_t>(v_)) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_header_c:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb4_t>(v_)));
|
||||
boost::get<cb4_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb4_t>(v_)) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_ = boost::blank{};
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
case do_header_only_c:
|
||||
{
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb1_t>(v_)));
|
||||
boost::get<cb1_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb1_t>(v_)) > 0)
|
||||
break;
|
||||
frd_ = boost::none;
|
||||
header_done_ = true;
|
||||
if(! split_)
|
||||
{
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
}
|
||||
s_ = do_body_c;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb5_t>(v_)));
|
||||
boost::get<cb5_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb5_t>(v_)) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
case do_body_final_c:
|
||||
{
|
||||
auto& b = boost::get<cb6_t>(v_);
|
||||
BOOST_ASSERT(n <= buffer_size(b));
|
||||
b.consume(n);
|
||||
if(buffer_size(b) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_all_c:
|
||||
{
|
||||
auto& b = boost::get<cb7_t>(v_);
|
||||
BOOST_ASSERT(n <= buffer_size(b));
|
||||
b.consume(n);
|
||||
if(buffer_size(b) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_ = boost::blank{};
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case do_final_c + 1:
|
||||
BOOST_ASSERT(buffer_size(
|
||||
boost::get<cb8_t>(v_)));
|
||||
boost::get<cb8_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb8_t>(v_)) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
goto go_complete;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
BOOST_ASSERT(false);
|
||||
case do_complete:
|
||||
break;
|
||||
|
||||
go_complete:
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,252 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_STATUS_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_STATUS_IPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
status
|
||||
int_to_status(unsigned v)
|
||||
{
|
||||
switch(static_cast<status>(v))
|
||||
{
|
||||
// 1xx
|
||||
case status::continue_:
|
||||
case status::switching_protocols:
|
||||
case status::processing:
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
// 2xx
|
||||
case status::ok:
|
||||
case status::created:
|
||||
case status::accepted:
|
||||
case status::non_authoritative_information:
|
||||
case status::no_content:
|
||||
case status::reset_content:
|
||||
case status::partial_content:
|
||||
case status::multi_status:
|
||||
case status::already_reported:
|
||||
case status::im_used:
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
// 3xx
|
||||
case status::multiple_choices:
|
||||
case status::moved_permanently:
|
||||
case status::found:
|
||||
case status::see_other:
|
||||
case status::not_modified:
|
||||
case status::use_proxy:
|
||||
case status::temporary_redirect:
|
||||
case status::permanent_redirect:
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
// 4xx
|
||||
case status::bad_request:
|
||||
case status::unauthorized:
|
||||
case status::payment_required:
|
||||
case status::forbidden:
|
||||
case status::not_found:
|
||||
case status::method_not_allowed:
|
||||
case status::not_acceptable:
|
||||
case status::proxy_authentication_required:
|
||||
case status::request_timeout:
|
||||
case status::conflict:
|
||||
case status::gone:
|
||||
case status::length_required:
|
||||
case status::precondition_failed:
|
||||
case status::payload_too_large:
|
||||
case status::uri_too_long:
|
||||
case status::unsupported_media_type:
|
||||
case status::range_not_satisfiable:
|
||||
case status::expectation_failed:
|
||||
case status::misdirected_request:
|
||||
case status::unprocessable_entity:
|
||||
case status::locked:
|
||||
case status::failed_dependency:
|
||||
case status::upgrade_required:
|
||||
case status::precondition_required:
|
||||
case status::too_many_requests:
|
||||
case status::request_header_fields_too_large:
|
||||
case status::connection_closed_without_response:
|
||||
case status::unavailable_for_legal_reasons:
|
||||
case status::client_closed_request:
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
// 5xx
|
||||
case status::internal_server_error:
|
||||
case status::not_implemented:
|
||||
case status::bad_gateway:
|
||||
case status::service_unavailable:
|
||||
case status::gateway_timeout:
|
||||
case status::http_version_not_supported:
|
||||
case status::variant_also_negotiates:
|
||||
case status::insufficient_storage:
|
||||
case status::loop_detected:
|
||||
case status::not_extended:
|
||||
case status::network_authentication_required:
|
||||
case status::network_connect_timeout_error:
|
||||
return static_cast<status>(v);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return status::unknown;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
string_view
|
||||
status_to_string(unsigned v)
|
||||
{
|
||||
switch(static_cast<status>(v))
|
||||
{
|
||||
// 1xx
|
||||
case status::continue_: return "Continue";
|
||||
case status::switching_protocols: return "Switching Protocols";
|
||||
case status::processing: return "Processing";
|
||||
|
||||
// 2xx
|
||||
case status::ok: return "OK";
|
||||
case status::created: return "Created";
|
||||
case status::accepted: return "Accepted";
|
||||
case status::non_authoritative_information: return "Non-Authoritative Information";
|
||||
case status::no_content: return "No Content";
|
||||
case status::reset_content: return "Reset Content";
|
||||
case status::partial_content: return "Partial Content";
|
||||
case status::multi_status: return "Multi-Status";
|
||||
case status::already_reported: return "Already Reported";
|
||||
case status::im_used: return "IM Used";
|
||||
|
||||
// 3xx
|
||||
case status::multiple_choices: return "Multiple Choices";
|
||||
case status::moved_permanently: return "Moved Permanently";
|
||||
case status::found: return "Found";
|
||||
case status::see_other: return "See Other";
|
||||
case status::not_modified: return "Not Modified";
|
||||
case status::use_proxy: return "Use Proxy";
|
||||
case status::temporary_redirect: return "Temporary Redirect";
|
||||
case status::permanent_redirect: return "Permanent Redirect";
|
||||
|
||||
// 4xx
|
||||
case status::bad_request: return "Bad Request";
|
||||
case status::unauthorized: return "Unauthorized";
|
||||
case status::payment_required: return "Payment Required";
|
||||
case status::forbidden: return "Forbidden";
|
||||
case status::not_found: return "Not Found";
|
||||
case status::method_not_allowed: return "Method Not Allowed";
|
||||
case status::not_acceptable: return "Not Acceptable";
|
||||
case status::proxy_authentication_required: return "Proxy Authentication Required";
|
||||
case status::request_timeout: return "Request Timeout";
|
||||
case status::conflict: return "Conflict";
|
||||
case status::gone: return "Gone";
|
||||
case status::length_required: return "Length Required";
|
||||
case status::precondition_failed: return "Precondition Failed";
|
||||
case status::payload_too_large: return "Payload Too Large";
|
||||
case status::uri_too_long: return "URI Too Long";
|
||||
case status::unsupported_media_type: return "Unsupported Media Type";
|
||||
case status::range_not_satisfiable: return "Range Not Satisfiable";
|
||||
case status::expectation_failed: return "Expectation Failed";
|
||||
case status::misdirected_request: return "Misdirected Request";
|
||||
case status::unprocessable_entity: return "Unprocessable Entity";
|
||||
case status::locked: return "Locked";
|
||||
case status::failed_dependency: return "Failed Dependency";
|
||||
case status::upgrade_required: return "Upgrade Required";
|
||||
case status::precondition_required: return "Precondition Required";
|
||||
case status::too_many_requests: return "Too Many Requests";
|
||||
case status::request_header_fields_too_large: return "Request Header Fields Too Large";
|
||||
case status::connection_closed_without_response: return "Connection Closed Without Response";
|
||||
case status::unavailable_for_legal_reasons: return "Unavailable For Legal Reasons";
|
||||
case status::client_closed_request: return "Client Closed Request";
|
||||
// 5xx
|
||||
case status::internal_server_error: return "Internal Server Error";
|
||||
case status::not_implemented: return "Not Implemented";
|
||||
case status::bad_gateway: return "Bad Gateway";
|
||||
case status::service_unavailable: return "Service Unavailable";
|
||||
case status::gateway_timeout: return "Gateway Timeout";
|
||||
case status::http_version_not_supported: return "HTTP Version Not Supported";
|
||||
case status::variant_also_negotiates: return "Variant Also Negotiates";
|
||||
case status::insufficient_storage: return "Insufficient Storage";
|
||||
case status::loop_detected: return "Loop Detected";
|
||||
case status::not_extended: return "Not Extended";
|
||||
case status::network_authentication_required: return "Network Authentication Required";
|
||||
case status::network_connect_timeout_error: return "Network Connect Timeout Error";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<unknown-status>";
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
status_class
|
||||
to_status_class(unsigned v)
|
||||
{
|
||||
switch(v / 100)
|
||||
{
|
||||
case 1: return status_class::informational;
|
||||
case 2: return status_class::successful;
|
||||
case 3: return status_class::redirection;
|
||||
case 4: return status_class::client_error;
|
||||
case 5: return status_class::server_error;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return status_class::unknown;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
status
|
||||
int_to_status(unsigned v)
|
||||
{
|
||||
return detail::int_to_status(v);
|
||||
}
|
||||
|
||||
inline
|
||||
status_class
|
||||
to_status_class(unsigned v)
|
||||
{
|
||||
return detail::to_status_class(v);
|
||||
}
|
||||
|
||||
inline
|
||||
status_class
|
||||
to_status_class(status v)
|
||||
{
|
||||
return to_status_class(static_cast<int>(v));
|
||||
}
|
||||
|
||||
inline
|
||||
string_view
|
||||
obsolete_reason(status v)
|
||||
{
|
||||
return detail::status_to_string(
|
||||
static_cast<unsigned>(v));
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, status v)
|
||||
{
|
||||
return os << obsolete_reason(v);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,337 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_VERB_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_VERB_IPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
inline
|
||||
string_view
|
||||
verb_to_string(verb v)
|
||||
{
|
||||
switch(v)
|
||||
{
|
||||
case verb::delete_: return "DELETE";
|
||||
case verb::get: return "GET";
|
||||
case verb::head: return "HEAD";
|
||||
case verb::post: return "POST";
|
||||
case verb::put: return "PUT";
|
||||
case verb::connect: return "CONNECT";
|
||||
case verb::options: return "OPTIONS";
|
||||
case verb::trace: return "TRACE";
|
||||
|
||||
case verb::copy: return "COPY";
|
||||
case verb::lock: return "LOCK";
|
||||
case verb::mkcol: return "MKCOL";
|
||||
case verb::move: return "MOVE";
|
||||
case verb::propfind: return "PROPFIND";
|
||||
case verb::proppatch: return "PROPPATCH";
|
||||
case verb::search: return "SEARCH";
|
||||
case verb::unlock: return "UNLOCK";
|
||||
case verb::bind: return "BIND";
|
||||
case verb::rebind: return "REBIND";
|
||||
case verb::unbind: return "UNBIND";
|
||||
case verb::acl: return "ACL";
|
||||
|
||||
case verb::report: return "REPORT";
|
||||
case verb::mkactivity: return "MKACTIVITY";
|
||||
case verb::checkout: return "CHECKOUT";
|
||||
case verb::merge: return "MERGE";
|
||||
|
||||
case verb::msearch: return "M-SEARCH";
|
||||
case verb::notify: return "NOTIFY";
|
||||
case verb::subscribe: return "SUBSCRIBE";
|
||||
case verb::unsubscribe: return "UNSUBSCRIBE";
|
||||
|
||||
case verb::patch: return "PATCH";
|
||||
case verb::purge: return "PURGE";
|
||||
|
||||
case verb::mkcalendar: return "MKCALENDAR";
|
||||
|
||||
case verb::link: return "LINK";
|
||||
case verb::unlink: return "UNLINK";
|
||||
|
||||
case verb::unknown:
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"});
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
verb
|
||||
string_to_verb(string_view v)
|
||||
{
|
||||
/*
|
||||
ACL
|
||||
BIND
|
||||
CHECKOUT
|
||||
CONNECT
|
||||
COPY
|
||||
DELETE
|
||||
GET
|
||||
HEAD
|
||||
LINK
|
||||
LOCK
|
||||
M-SEARCH
|
||||
MERGE
|
||||
MKACTIVITY
|
||||
MKCALENDAR
|
||||
MKCOL
|
||||
MOVE
|
||||
NOTIFY
|
||||
OPTIONS
|
||||
PATCH
|
||||
POST
|
||||
PROPFIND
|
||||
PROPPATCH
|
||||
PURGE
|
||||
PUT
|
||||
REBIND
|
||||
REPORT
|
||||
SEARCH
|
||||
SUBSCRIBE
|
||||
TRACE
|
||||
UNBIND
|
||||
UNLINK
|
||||
UNLOCK
|
||||
UNSUBSCRIBE
|
||||
*/
|
||||
if(v.size() < 3)
|
||||
return verb::unknown;
|
||||
// s must be null terminated
|
||||
auto const eq =
|
||||
[](string_view sv, char const* s)
|
||||
{
|
||||
auto p = sv.data();
|
||||
for(;;)
|
||||
{
|
||||
if(*s != *p)
|
||||
return false;
|
||||
++s;
|
||||
++p;
|
||||
if(! *s)
|
||||
return p == sv.end();
|
||||
}
|
||||
};
|
||||
auto c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'A':
|
||||
if(v == "CL")
|
||||
return verb::acl;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
if(v == "IND")
|
||||
return verb::bind;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'H':
|
||||
if(eq(v, "ECKOUT"))
|
||||
return verb::checkout;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(eq(v, "NNECT"))
|
||||
return verb::connect;
|
||||
if(eq(v, "PY"))
|
||||
return verb::copy;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
if(eq(v, "ELETE"))
|
||||
return verb::delete_;
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
if(eq(v, "ET"))
|
||||
return verb::get;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
if(eq(v, "EAD"))
|
||||
return verb::head;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
if(eq(v, "INK"))
|
||||
return verb::link;
|
||||
if(eq(v, "OCK"))
|
||||
return verb::lock;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case '-':
|
||||
if(eq(v, "SEARCH"))
|
||||
return verb::msearch;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
if(eq(v, "RGE"))
|
||||
return verb::merge;
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
if(eq(v, "ACTIVITY"))
|
||||
return verb::mkactivity;
|
||||
if(v[0] == 'C')
|
||||
{
|
||||
v.remove_prefix(1);
|
||||
if(eq(v, "ALENDAR"))
|
||||
return verb::mkcalendar;
|
||||
if(eq(v, "OL"))
|
||||
return verb::mkcol;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(eq(v, "VE"))
|
||||
return verb::move;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
if(eq(v, "OTIFY"))
|
||||
return verb::notify;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(eq(v, "PTIONS"))
|
||||
return verb::options;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'A':
|
||||
if(eq(v, "TCH"))
|
||||
return verb::patch;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(eq(v, "ST"))
|
||||
return verb::post;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if(eq(v, "OPFIND"))
|
||||
return verb::propfind;
|
||||
if(eq(v, "OPPATCH"))
|
||||
return verb::proppatch;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
if(eq(v, "RGE"))
|
||||
return verb::purge;
|
||||
if(eq(v, "T"))
|
||||
return verb::put;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if(v[0] != 'E')
|
||||
break;
|
||||
v.remove_prefix(1);
|
||||
if(eq(v, "BIND"))
|
||||
return verb::rebind;
|
||||
if(eq(v, "PORT"))
|
||||
return verb::report;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if(eq(v, "EARCH"))
|
||||
return verb::search;
|
||||
if(eq(v, "UBSCRIBE"))
|
||||
return verb::subscribe;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if(eq(v, "RACE"))
|
||||
return verb::trace;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
if(v[0] != 'N')
|
||||
break;
|
||||
v.remove_prefix(1);
|
||||
if(eq(v, "BIND"))
|
||||
return verb::unbind;
|
||||
if(eq(v, "LINK"))
|
||||
return verb::unlink;
|
||||
if(eq(v, "LOCK"))
|
||||
return verb::unlock;
|
||||
if(eq(v, "SUBSCRIBE"))
|
||||
return verb::unsubscribe;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return verb::unknown;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
string_view
|
||||
to_string(verb v)
|
||||
{
|
||||
return detail::verb_to_string(v);
|
||||
}
|
||||
|
||||
inline
|
||||
verb
|
||||
string_to_verb(string_view s)
|
||||
{
|
||||
return detail::string_to_verb(s);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,890 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_WRITE_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_WRITE_IPP
|
||||
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/ostream.hpp>
|
||||
#include <boost/beast/core/handler_alloc.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_some_op
|
||||
{
|
||||
Stream& s_;
|
||||
serializer<isRequest,Body, Fields>& sr_;
|
||||
Handler h_;
|
||||
|
||||
class lambda
|
||||
{
|
||||
write_some_op& op_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
|
||||
explicit
|
||||
lambda(write_some_op& op)
|
||||
: op_(op)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
invoked = true;
|
||||
ec.assign(0, ec.category());
|
||||
return op_.s_.async_write_some(
|
||||
buffers, std::move(op_));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
write_some_op(write_some_op&&) = default;
|
||||
write_some_op(write_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_some_op(DeducedHandler&& h, Stream& s,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
: s_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()();
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some_op<
|
||||
Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()()
|
||||
{
|
||||
error_code ec;
|
||||
if(! sr_.is_done())
|
||||
{
|
||||
lambda f{*this};
|
||||
sr_.next(ec, f);
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
if(f.invoked)
|
||||
{
|
||||
// *this has been moved from,
|
||||
// cannot access members here.
|
||||
return;
|
||||
}
|
||||
// What else could it be?
|
||||
BOOST_ASSERT(sr_.is_done());
|
||||
}
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
template<
|
||||
class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some_op<
|
||||
Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
{
|
||||
sr_.consume(bytes_transferred);
|
||||
if(sr_.is_done())
|
||||
if(! sr_.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct serializer_is_header_done
|
||||
{
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool
|
||||
operator()(
|
||||
serializer<isRequest, Body, Fields>& sr) const
|
||||
{
|
||||
return sr.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct serializer_is_done
|
||||
{
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool
|
||||
operator()(
|
||||
serializer<isRequest, Body, Fields>& sr) const
|
||||
{
|
||||
return sr.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Stream, class Handler, class Predicate,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields>& sr_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_op(DeducedHandler&& h, Stream& s,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
: s_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 3 ||
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class Stream, class Handler, class Predicate,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_op<Stream, Handler, Predicate,
|
||||
isRequest, Body, Fields>::
|
||||
operator()(error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
goto upcall;
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if(Predicate{}(sr_))
|
||||
{
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec));
|
||||
}
|
||||
state_ = 2;
|
||||
return beast::http::async_write_some(
|
||||
s_, sr_, std::move(*this));
|
||||
}
|
||||
|
||||
case 1:
|
||||
goto upcall;
|
||||
|
||||
case 2:
|
||||
state_ = 3;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case 3:
|
||||
{
|
||||
if(Predicate{}(sr_))
|
||||
goto upcall;
|
||||
return beast::http::async_write_some(
|
||||
s_, sr_, std::move(*this));
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_msg_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
serializer<isRequest, Body, Fields> sr;
|
||||
|
||||
data(Handler&, Stream& s_, message<
|
||||
isRequest, Body, Fields>& m_)
|
||||
: s(s_)
|
||||
, sr(m_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_msg_op(write_msg_op&&) = default;
|
||||
write_msg_op(write_msg_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()();
|
||||
|
||||
void
|
||||
operator()(error_code ec);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_msg_op<
|
||||
Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
return async_write(d.s, d.sr, std::move(*this));
|
||||
}
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_msg_op<
|
||||
Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()(error_code ec)
|
||||
{
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream>
|
||||
class write_some_lambda
|
||||
{
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_some_lambda(Stream& stream)
|
||||
: stream_(stream)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred =
|
||||
stream_.write_some(buffers, ec);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream>
|
||||
class write_lambda
|
||||
{
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_lambda(Stream& stream)
|
||||
: stream_(stream)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred = boost::asio::write(
|
||||
stream_, buffers, ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
if(! sr.is_done())
|
||||
{
|
||||
write_some_lambda<SyncWriteStream> f{stream};
|
||||
sr.next(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
if(f.invoked)
|
||||
sr.consume(f.bytes_transferred);
|
||||
if(sr.is_done())
|
||||
if(! sr.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
return;
|
||||
}
|
||||
if(! sr.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_some(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_some_op<AsyncWriteStream,
|
||||
handler_type<WriteHandler, void(error_code)>,
|
||||
isRequest, Body, Fields>{
|
||||
init.completion_handler, stream, sr}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
detail::write_some(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_some(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
return detail::async_write_some(stream, sr,
|
||||
std::forward<WriteHandler>(handler));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_header(SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write_header(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_header(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(true);
|
||||
if(! sr.is_header_done())
|
||||
{
|
||||
detail::write_lambda<SyncWriteStream> f{stream};
|
||||
do
|
||||
{
|
||||
sr.next(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(f.invoked);
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
while(! sr.is_header_done());
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_header(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(true);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, handler_type<
|
||||
WriteHandler, void(error_code)>,
|
||||
detail::serializer_is_header_done,
|
||||
isRequest, Body, Fields>{
|
||||
init.completion_handler, stream, sr}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
error_code ec;
|
||||
write(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
sr.split(false);
|
||||
for(;;)
|
||||
{
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(false);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, handler_type<
|
||||
WriteHandler, void(error_code)>,
|
||||
detail::serializer_is_done, isRequest, Body, Fields>{
|
||||
init.completion_handler, stream, sr}(error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(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_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
write(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<
|
||||
WriteHandler, void(error_code)>
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_msg_op<AsyncWriteStream, handler_type<
|
||||
WriteHandler, void(error_code)>, isRequest,
|
||||
Body, Fields>{init.completion_handler,
|
||||
stream, msg}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Serializer>
|
||||
class write_ostream_lambda
|
||||
{
|
||||
std::ostream& os_;
|
||||
Serializer& sr_;
|
||||
|
||||
public:
|
||||
write_ostream_lambda(std::ostream& os,
|
||||
Serializer& sr)
|
||||
: os_(os)
|
||||
, sr_(sr)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers) const
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
if(os_.fail())
|
||||
return;
|
||||
std::size_t bytes_transferred = 0;
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for(auto it = buffers.begin();
|
||||
it != buffers.end(); ++it)
|
||||
{
|
||||
boost::asio::const_buffer b = *it;
|
||||
auto const n = buffer_size(b);
|
||||
os_.write(buffer_cast<char const*>(b), n);
|
||||
if(os_.fail())
|
||||
return;
|
||||
bytes_transferred += n;
|
||||
}
|
||||
sr_.consume(bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<true, Fields> const& h)
|
||||
{
|
||||
typename Fields::reader fr{
|
||||
h, h.version, h.method()};
|
||||
return os << buffers(fr.get());
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<false, Fields> const& h)
|
||||
{
|
||||
typename Fields::reader fr{
|
||||
h, h.version, h.result_int()};
|
||||
return os << buffers(fr.get());
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
error_code ec;
|
||||
detail::write_ostream_lambda<decltype(sr)> f{os, sr};
|
||||
do
|
||||
{
|
||||
sr.next(ec, f);
|
||||
if(os.fail())
|
||||
break;
|
||||
if(ec == error::end_of_stream)
|
||||
ec.assign(0, ec.category());
|
||||
if(ec)
|
||||
{
|
||||
os.setstate(std::ios::failbit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(! sr.is_done());
|
||||
return os;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,845 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_MESSAGE_HPP
|
||||
#define BOOST_BEAST_HTTP_MESSAGE_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/http/fields.hpp>
|
||||
#include <boost/beast/http/verb.hpp>
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/detail/integer_sequence.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A container for an HTTP request or response header.
|
||||
|
||||
This container is derived from the `Fields` template type.
|
||||
To understand all of the members of this class it is necessary
|
||||
to view the declaration for the `Fields` type. When using
|
||||
the default fields container, those declarations are in
|
||||
@ref fields.
|
||||
|
||||
Newly constructed header objects have version set to
|
||||
HTTP/1.1. Newly constructed response objects also have
|
||||
result code set to @ref status::ok.
|
||||
|
||||
A `header` includes the start-line and header-fields.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<bool isRequest, class Fields = fields>
|
||||
struct header : Fields
|
||||
|
||||
#else
|
||||
template<bool isRequest, class Fields = fields>
|
||||
struct header;
|
||||
|
||||
template<class Fields>
|
||||
struct header<true, Fields> : Fields
|
||||
#endif
|
||||
{
|
||||
static_assert(is_fields<Fields>::value,
|
||||
"Fields requirements not met");
|
||||
|
||||
/// Indicates if the header is a request or response.
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using is_request = std::integral_constant<bool, isRequest>;
|
||||
#else
|
||||
using is_request = std::true_type;
|
||||
#endif
|
||||
|
||||
/// The type representing the fields.
|
||||
using fields_type = Fields;
|
||||
|
||||
/** The HTTP-version.
|
||||
|
||||
This holds both the major and minor version numbers,
|
||||
using these formulas:
|
||||
@code
|
||||
unsigned major = version / 10;
|
||||
unsigned minor = version % 10;
|
||||
@endcode
|
||||
|
||||
Newly constructed headers will use HTTP/1.1 by default.
|
||||
*/
|
||||
unsigned version = 11;
|
||||
|
||||
/// 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;
|
||||
|
||||
/** Return the request-method verb.
|
||||
|
||||
If the request-method is not one of the recognized verbs,
|
||||
@ref verb::unknown is returned. Callers may use @ref method_string
|
||||
to retrieve the exact text.
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
|
||||
@see @ref method_string
|
||||
*/
|
||||
verb
|
||||
method() const;
|
||||
|
||||
/** Set the request-method.
|
||||
|
||||
This function will set the method for requests to a known verb.
|
||||
|
||||
@param v The request method verb to set.
|
||||
This may not be @ref verb::unknown.
|
||||
|
||||
@throws std::invalid_argument when `v == verb::unknown`.
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
*/
|
||||
void
|
||||
method(verb v);
|
||||
|
||||
/** Return the request-method as a string.
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
|
||||
@see @ref method
|
||||
*/
|
||||
string_view
|
||||
method_string() const;
|
||||
|
||||
/** Set the request-method.
|
||||
|
||||
This function will set the request-method a known verb
|
||||
if the string matches, otherwise it will store a copy of
|
||||
the passed string.
|
||||
|
||||
@param s A string representing the request-method.
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
*/
|
||||
void
|
||||
method_string(string_view s);
|
||||
|
||||
/** Returns the request-target string.
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
*/
|
||||
string_view
|
||||
target() const;
|
||||
|
||||
/** Set the request-target string.
|
||||
|
||||
@param s A string representing the request-target.
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
*/
|
||||
void
|
||||
target(string_view s);
|
||||
|
||||
// VFALCO Don't rearrange these declarations or
|
||||
// ifdefs, or else the documentation will break.
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param args Arguments forwarded to the `Fields`
|
||||
base class constructor.
|
||||
|
||||
@note This constructor participates in overload
|
||||
resolution if and only if the first parameter is
|
||||
not convertible to @ref header, @ref verb, or
|
||||
@ref status.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class... Args>
|
||||
explicit
|
||||
header(Args&&... args);
|
||||
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! std::is_convertible<typename
|
||||
std::decay<Arg1>::type, header>::value &&
|
||||
! std::is_convertible<typename
|
||||
std::decay<Arg1>::type, verb>::value &&
|
||||
! std::is_convertible<typename
|
||||
std::decay<Arg1>::type, header>::value
|
||||
>::type>
|
||||
explicit
|
||||
header(Arg1&& arg1, ArgN&&... argn);
|
||||
|
||||
private:
|
||||
template<bool, class, class>
|
||||
friend struct message;
|
||||
|
||||
template<class T>
|
||||
friend
|
||||
void
|
||||
swap(header<true, T>& m1, header<true, T>& m2);
|
||||
|
||||
template<class... FieldsArgs>
|
||||
header(
|
||||
verb method,
|
||||
string_view target_,
|
||||
unsigned version_,
|
||||
FieldsArgs&&... fields_args)
|
||||
: Fields(std::forward<FieldsArgs>(fields_args)...)
|
||||
, version(version_)
|
||||
, method_(method)
|
||||
{
|
||||
target(target_);
|
||||
}
|
||||
|
||||
verb method_ = verb::unknown;
|
||||
};
|
||||
|
||||
/** A container for an HTTP request or response header.
|
||||
|
||||
A `header` includes the start-line and header-fields.
|
||||
*/
|
||||
template<class Fields>
|
||||
struct header<false, Fields> : Fields
|
||||
{
|
||||
static_assert(is_fields<Fields>::value,
|
||||
"Fields requirements not met");
|
||||
|
||||
/// Indicates if the header is a request or response.
|
||||
using is_request = std::false_type;
|
||||
|
||||
/// The type representing the fields.
|
||||
using fields_type = Fields;
|
||||
|
||||
/** The HTTP version.
|
||||
|
||||
This holds both the major and minor version numbers,
|
||||
using these formulas:
|
||||
@code
|
||||
unsigned major = version / 10;
|
||||
unsigned minor = version % 10;
|
||||
@endcode
|
||||
|
||||
Newly constructed headers will use HTTP/1.1 by default
|
||||
unless otherwise specified.
|
||||
*/
|
||||
unsigned version = 11;
|
||||
|
||||
/// 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;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param args Arguments forwarded to the `Fields`
|
||||
base class constructor.
|
||||
|
||||
@note This constructor participates in overload
|
||||
resolution if and only if the first parameter is
|
||||
not convertible to @ref header, @ref verb, or
|
||||
@ref status.
|
||||
*/
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! std::is_convertible<typename
|
||||
std::decay<Arg1>::type, status>::value &&
|
||||
! std::is_convertible<typename
|
||||
std::decay<Arg1>::type, header>::value
|
||||
>::type>
|
||||
explicit
|
||||
header(Arg1&& arg1, ArgN&&... argn);
|
||||
#endif
|
||||
|
||||
/** The response status-code result.
|
||||
|
||||
If the actual status code is not a known code, this
|
||||
function returns @ref status::unknown. Use @ref result_int
|
||||
to return the raw status code as a number.
|
||||
|
||||
@note This member is only available when `isRequest == false`.
|
||||
*/
|
||||
status
|
||||
result() const;
|
||||
|
||||
/** Set the response status-code.
|
||||
|
||||
@param v The code to set.
|
||||
|
||||
@note This member is only available when `isRequest == false`.
|
||||
*/
|
||||
void
|
||||
result(status v);
|
||||
|
||||
/** Set the response status-code as an integer.
|
||||
|
||||
This sets the status code to the exact number passed in.
|
||||
If the number does not correspond to one of the known
|
||||
status codes, the function @ref result will return
|
||||
@ref status::unknown. Use @ref result_int to obtain the
|
||||
original raw status-code.
|
||||
|
||||
@param v The status-code integer to set.
|
||||
|
||||
@throws std::invalid_argument if `v > 999`.
|
||||
*/
|
||||
void
|
||||
result(unsigned v);
|
||||
|
||||
/** The response status-code expressed as an integer.
|
||||
|
||||
This returns the raw status code as an integer, even
|
||||
when that code is not in the list of known status codes.
|
||||
|
||||
@note This member is only available when `isRequest == false`.
|
||||
*/
|
||||
unsigned
|
||||
result_int() const;
|
||||
|
||||
/** Return the response reason-phrase.
|
||||
|
||||
The reason-phrase is obsolete as of rfc7230.
|
||||
|
||||
@note This function is only available when `isRequest == false`.
|
||||
*/
|
||||
string_view
|
||||
reason() const;
|
||||
|
||||
/** Set the response reason-phrase (deprecated)
|
||||
|
||||
This function sets a custom reason-phrase to a copy of
|
||||
the string passed in. Normally it is not necessary to set
|
||||
the reason phrase on an outgoing response object; the
|
||||
implementation will automatically use the standard reason
|
||||
text for the corresponding status code.
|
||||
|
||||
To clear a previously set custom phrase, pass an empty
|
||||
string. This will restore the default standard reason text
|
||||
based on the status code used when serializing.
|
||||
|
||||
The reason-phrase is obsolete as of rfc7230.
|
||||
|
||||
@param s The string to use for the reason-phrase.
|
||||
|
||||
@note This function is only available when `isRequest == false`.
|
||||
*/
|
||||
void
|
||||
reason(string_view s);
|
||||
|
||||
private:
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
template<bool, class, class>
|
||||
friend struct message;
|
||||
|
||||
template<class T>
|
||||
friend
|
||||
void
|
||||
swap(header<false, T>& m1, header<false, T>& m2);
|
||||
|
||||
template<class... FieldsArgs>
|
||||
header(
|
||||
status result,
|
||||
unsigned version_,
|
||||
FieldsArgs&&... fields_args)
|
||||
: Fields(std::forward<FieldsArgs>(fields_args)...)
|
||||
, version(version_)
|
||||
, result_(result)
|
||||
{
|
||||
}
|
||||
|
||||
status result_ = status::ok;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// A typical HTTP request header
|
||||
template<class Fields = fields>
|
||||
using request_header = header<true, Fields>;
|
||||
|
||||
/// A typical HTTP response header
|
||||
template<class Fields = fields>
|
||||
using response_header = header<false, Fields>;
|
||||
|
||||
/** A container for a complete HTTP message.
|
||||
|
||||
This container is derived from the `Fields` template type.
|
||||
To understand all of the members of this class it is necessary
|
||||
to view the declaration for the `Fields` type. When using
|
||||
the default fields container, those declarations are in
|
||||
@ref fields.
|
||||
|
||||
A message can be a request or response, depending on the
|
||||
`isRequest` template argument value. Requests and responses
|
||||
have different types; functions may be overloaded based on
|
||||
the type if desired.
|
||||
|
||||
The `Body` template argument type determines the model used
|
||||
to read or write the content body of the message.
|
||||
|
||||
Newly constructed messages objects have version set to
|
||||
HTTP/1.1. Newly constructed response objects also have
|
||||
result code set to @ref status::ok.
|
||||
|
||||
@tparam isRequest `true` if this represents a request,
|
||||
or `false` if this represents a response. Some class data
|
||||
members are conditionally present depending on this value.
|
||||
|
||||
@tparam Body A type meeting the requirements of Body.
|
||||
|
||||
@tparam Fields The type of container used to hold the
|
||||
field value pairs.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Fields = fields>
|
||||
struct message : header<isRequest, Fields>
|
||||
{
|
||||
/// The base class used to hold the header portion of the message.
|
||||
using header_type = header<isRequest, Fields>;
|
||||
|
||||
/** The type providing the body traits.
|
||||
|
||||
The @ref message::body member will be of type `body_type::value_type`.
|
||||
*/
|
||||
using body_type = Body;
|
||||
|
||||
/// A value representing the body.
|
||||
typename Body::value_type body;
|
||||
|
||||
/// Constructor
|
||||
message() = default;
|
||||
|
||||
/// Constructor
|
||||
message(message&&) = default;
|
||||
|
||||
/// Constructor
|
||||
message(message const&) = default;
|
||||
|
||||
/// Assignment
|
||||
message& operator=(message&&) = default;
|
||||
|
||||
/// Assignment
|
||||
message& operator=(message const&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param h The header to move construct from.
|
||||
|
||||
@param body_args Optional arguments forwarded
|
||||
to the `body` constructor.
|
||||
*/
|
||||
template<class... BodyArgs>
|
||||
explicit
|
||||
message(header_type&& h, BodyArgs&&... body_args);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param h The header to copy construct from.
|
||||
|
||||
@param body_args Optional arguments forwarded
|
||||
to the `body` constructor.
|
||||
*/
|
||||
template<class... BodyArgs>
|
||||
explicit
|
||||
message(header_type const& h, BodyArgs&&... body_args);
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param method The request-method to use
|
||||
|
||||
@param target The request-target.
|
||||
|
||||
@param version The HTTP-version
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
message(verb method, string_view target, unsigned version);
|
||||
#else
|
||||
template<class Version,
|
||||
class = typename std::enable_if<isRequest &&
|
||||
std::is_convertible<Version, unsigned>::value>::type>
|
||||
message(verb method, string_view target, Version version);
|
||||
#endif
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param method The request-method to use
|
||||
|
||||
@param target The request-target.
|
||||
|
||||
@param version The HTTP-version
|
||||
|
||||
@param body_arg An argument forwarded to the `body` constructor.
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class BodyArg>
|
||||
message(verb method, string_view target,
|
||||
unsigned version, BodyArg&& body_arg);
|
||||
#else
|
||||
template<class Version, class BodyArg,
|
||||
class = typename std::enable_if<isRequest &&
|
||||
std::is_convertible<Version, unsigned>::value>::type>
|
||||
message(verb method, string_view target,
|
||||
Version version, BodyArg&& body_arg);
|
||||
#endif
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param method The request-method to use
|
||||
|
||||
@param target The request-target.
|
||||
|
||||
@param version The HTTP-version
|
||||
|
||||
@param body_arg An argument forwarded to the `body` constructor.
|
||||
|
||||
@param fields_arg An argument forwarded to the `Fields` constructor.
|
||||
|
||||
@note This function is only available when `isRequest == true`.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class BodyArg, class FieldsArg>
|
||||
message(verb method, string_view target, unsigned version,
|
||||
BodyArg&& body_arg, FieldsArg&& fields_arg);
|
||||
#else
|
||||
template<class Version, class BodyArg, class FieldsArg,
|
||||
class = typename std::enable_if<isRequest &&
|
||||
std::is_convertible<Version, unsigned>::value>::type>
|
||||
message(verb method, string_view target, Version version,
|
||||
BodyArg&& body_arg, FieldsArg&& fields_arg);
|
||||
#endif
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param result The status-code for the response
|
||||
|
||||
@param version The HTTP-version
|
||||
|
||||
@note This member is only available when `isRequest == false`.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
message(status result, unsigned version);
|
||||
#else
|
||||
template<class Version,
|
||||
class = typename std::enable_if<! isRequest &&
|
||||
std::is_convertible<Version, unsigned>::value>::type>
|
||||
message(status result, Version version);
|
||||
#endif
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param result The status-code for the response
|
||||
|
||||
@param version The HTTP-version
|
||||
|
||||
@param body_arg An argument forwarded to the `body` constructor.
|
||||
|
||||
@note This member is only available when `isRequest == false`.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class BodyArg>
|
||||
message(status result, unsigned version, BodyArg&& body_arg);
|
||||
#else
|
||||
template<class Version, class BodyArg,
|
||||
class = typename std::enable_if<! isRequest &&
|
||||
std::is_convertible<Version, unsigned>::value>::type>
|
||||
message(status result, Version version, BodyArg&& body_arg);
|
||||
#endif
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param result The status-code for the response
|
||||
|
||||
@param version The HTTP-version
|
||||
|
||||
@param body_arg An argument forwarded to the `body` constructor.
|
||||
|
||||
@param fields_arg An argument forwarded to the `Fields` base class constructor.
|
||||
|
||||
@note This member is only available when `isRequest == false`.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class BodyArg, class FieldsArg>
|
||||
message(status result, unsigned version,
|
||||
BodyArg&& body_arg, FieldsArg&& fields_arg);
|
||||
#else
|
||||
template<class Version, class BodyArg, class FieldsArg,
|
||||
class = typename std::enable_if<! isRequest &&
|
||||
std::is_convertible<Version, unsigned>::value>::type>
|
||||
message(status result, Version version,
|
||||
BodyArg&& body_arg, FieldsArg&& fields_arg);
|
||||
#endif
|
||||
|
||||
/** Constructor
|
||||
|
||||
The header and body are default-constructed.
|
||||
*/
|
||||
explicit
|
||||
message(std::piecewise_construct_t);
|
||||
|
||||
/** Construct a message.
|
||||
|
||||
@param body_args A tuple forwarded as a parameter
|
||||
pack to the body constructor.
|
||||
*/
|
||||
template<class... BodyArgs>
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...> body_args);
|
||||
|
||||
/** Construct a message.
|
||||
|
||||
@param body_args A tuple forwarded as a parameter
|
||||
pack to the body constructor.
|
||||
|
||||
@param fields_args A tuple forwarded as a parameter
|
||||
pack to the `Fields` constructor.
|
||||
*/
|
||||
template<class... BodyArgs, class... FieldsArgs>
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...> body_args,
|
||||
std::tuple<FieldsArgs...> fields_args);
|
||||
|
||||
/// Returns the header portion of the message
|
||||
header_type const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns the header portion of the message
|
||||
header_type&
|
||||
base()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns `true` if the chunked Transfer-Encoding is specified
|
||||
bool
|
||||
chunked() const
|
||||
{
|
||||
return this->get_chunked_impl();
|
||||
}
|
||||
|
||||
/** Set or clear the chunked Transfer-Encoding
|
||||
|
||||
This function will set or removed the "chunked" transfer
|
||||
encoding as the last item in the list of encodings in the
|
||||
field.
|
||||
|
||||
If the result of removing the chunked token results in an
|
||||
empty string, the field is erased.
|
||||
|
||||
The Content-Length field is erased unconditionally.
|
||||
*/
|
||||
void
|
||||
chunked(bool value);
|
||||
|
||||
/** Set or clear the Content-Length field
|
||||
|
||||
This function adjusts the Content-Length field as follows:
|
||||
|
||||
@li If `value` specifies a value, the Content-Length field
|
||||
is set to the value. Otherwise
|
||||
|
||||
@li The Content-Length field is erased.
|
||||
|
||||
If "chunked" token appears as the last item in the
|
||||
Transfer-Encoding field it is unconditionally removed.
|
||||
|
||||
@param value The value to set for Content-Length.
|
||||
*/
|
||||
void
|
||||
content_length(boost::optional<std::uint64_t> const& value);
|
||||
|
||||
/** Returns `true` if the message semantics indicate keep-alive
|
||||
|
||||
The value depends on the version in the message, which must
|
||||
be set to the final value before this function is called or
|
||||
else the return value is unreliable.
|
||||
*/
|
||||
bool
|
||||
keep_alive() const
|
||||
{
|
||||
return this->get_keep_alive_impl(this->version);
|
||||
}
|
||||
|
||||
/** Set the keep-alive message semantic option
|
||||
|
||||
This function adjusts the Connection field to indicate
|
||||
whether or not the connection should be kept open after
|
||||
the corresponding response. The result depends on the
|
||||
version set on the message, which must be set to the
|
||||
final value before making this call.
|
||||
|
||||
@param value `true` if the connection should persist.
|
||||
*/
|
||||
void
|
||||
keep_alive(bool value)
|
||||
{
|
||||
this->set_keep_alive_impl(this->version, value);
|
||||
}
|
||||
|
||||
/** Returns the payload size of the body in octets if possible.
|
||||
|
||||
This function invokes the @b Body algorithm to measure
|
||||
the number of octets in the serialized body container. If
|
||||
there is no body, this will return zero. Otherwise, if the
|
||||
body exists but is not known ahead of time, `boost::none`
|
||||
is returned (usually indicating that a chunked Transfer-Encoding
|
||||
will be used).
|
||||
|
||||
@note The value of the Content-Length field in the message
|
||||
is not inspected.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
payload_size() const;
|
||||
|
||||
/** Prepare the message payload fields for the body.
|
||||
|
||||
This function will adjust the Content-Length and
|
||||
Transfer-Encoding field values based on the properties
|
||||
of the body.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
request<string_body> req{verb::post, "/"};
|
||||
req.set(field::user_agent, "Beast");
|
||||
req.body = "Hello, world!";
|
||||
req.prepare_payload();
|
||||
@endcode
|
||||
*/
|
||||
void
|
||||
prepare_payload()
|
||||
{
|
||||
prepare_payload(typename header_type::is_request{});
|
||||
}
|
||||
|
||||
private:
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
|
||||
template<
|
||||
class... BodyArgs,
|
||||
std::size_t... IBodyArgs>
|
||||
message(
|
||||
std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...>& body_args,
|
||||
beast::detail::index_sequence<IBodyArgs...>)
|
||||
: body(std::forward<BodyArgs>(
|
||||
std::get<IBodyArgs>(body_args))...)
|
||||
{
|
||||
boost::ignore_unused(body_args);
|
||||
}
|
||||
|
||||
template<
|
||||
class... BodyArgs,
|
||||
class... FieldsArgs,
|
||||
std::size_t... IBodyArgs,
|
||||
std::size_t... IFieldsArgs>
|
||||
message(
|
||||
std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...>& body_args,
|
||||
std::tuple<FieldsArgs...>& fields_args,
|
||||
beast::detail::index_sequence<IBodyArgs...>,
|
||||
beast::detail::index_sequence<IFieldsArgs...>)
|
||||
: header_type(std::forward<FieldsArgs>(
|
||||
std::get<IFieldsArgs>(fields_args))...)
|
||||
, body(std::forward<BodyArgs>(
|
||||
std::get<IBodyArgs>(body_args))...)
|
||||
{
|
||||
boost::ignore_unused(body_args);
|
||||
boost::ignore_unused(fields_args);
|
||||
}
|
||||
|
||||
boost::optional<std::uint64_t>
|
||||
payload_size(std::true_type) const
|
||||
{
|
||||
return Body::size(body);
|
||||
}
|
||||
|
||||
boost::optional<std::uint64_t>
|
||||
payload_size(std::false_type) const
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
void
|
||||
prepare_payload(std::true_type);
|
||||
|
||||
void
|
||||
prepare_payload(std::false_type);
|
||||
};
|
||||
|
||||
/// A typical HTTP request
|
||||
template<class Body, class Fields = fields>
|
||||
using request = message<true, Body, Fields>;
|
||||
|
||||
/// A typical HTTP response
|
||||
template<class Body, class Fields = fields>
|
||||
using response = message<false, Body, Fields>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
/** Swap two header objects.
|
||||
|
||||
@par Requirements
|
||||
`Fields` is @b Swappable.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
void
|
||||
swap(
|
||||
header<isRequest, Fields>& m1,
|
||||
header<isRequest, Fields>& m2);
|
||||
#endif
|
||||
|
||||
/** Swap two message objects.
|
||||
|
||||
@par Requirements:
|
||||
`Body::value_type` and `Fields` are @b Swappable.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
swap(
|
||||
message<isRequest, Body, Fields>& m1,
|
||||
message<isRequest, Body, Fields>& m2);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/message.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,436 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_PARSER_HPP
|
||||
#define BOOST_BEAST_HTTP_PARSER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/http/basic_parser.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** An HTTP/1 parser for producing a message.
|
||||
|
||||
This class uses the basic HTTP/1 wire format parser to convert
|
||||
a series of octets into a @ref message using the @ref basic_fields
|
||||
container to represent the fields.
|
||||
|
||||
@tparam isRequest Indicates whether a request or response
|
||||
will be parsed.
|
||||
|
||||
@tparam Body The type used to represent the body. This must
|
||||
meet the requirements of @b Body.
|
||||
|
||||
@tparam Allocator The type of allocator used with the
|
||||
@ref basic_fields container.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<
|
||||
bool isRequest,
|
||||
class Body,
|
||||
class Allocator = std::allocator<char>>
|
||||
class parser
|
||||
: public basic_parser<isRequest,
|
||||
parser<isRequest, Body, Allocator>>
|
||||
{
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
|
||||
template<bool, class, class>
|
||||
friend class parser;
|
||||
|
||||
using base_type = basic_parser<isRequest,
|
||||
parser<isRequest, Body, Allocator>>;
|
||||
|
||||
message<isRequest, Body, basic_fields<Allocator>> m_;
|
||||
typename Body::writer wr_;
|
||||
bool wr_inited_ = false;
|
||||
|
||||
std::function<void(
|
||||
std::uint64_t,
|
||||
string_view,
|
||||
error_code&)> cb_h_;
|
||||
|
||||
std::function<std::size_t(
|
||||
std::uint64_t,
|
||||
string_view,
|
||||
error_code&)> cb_b_;
|
||||
|
||||
public:
|
||||
/// The type of message returned by the parser
|
||||
using value_type =
|
||||
message<isRequest, Body, basic_fields<Allocator>>;
|
||||
|
||||
/// Destructor
|
||||
~parser() = default;
|
||||
|
||||
/// Constructor
|
||||
parser();
|
||||
|
||||
/// Constructor
|
||||
parser(parser const&) = delete;
|
||||
|
||||
/// Assignment
|
||||
parser& operator=(parser const&) = delete;
|
||||
|
||||
/** Constructor
|
||||
|
||||
After the move, the only valid operation
|
||||
on the moved-from object is destruction.
|
||||
*/
|
||||
parser(parser&& other) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param args Optional arguments forwarded to the
|
||||
@ref http::header constructor.
|
||||
|
||||
@note This function participates in overload
|
||||
resolution only if the first argument is not a
|
||||
@ref parser.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class... Args>
|
||||
explicit
|
||||
parser(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! detail::is_parser<typename
|
||||
std::decay<Arg1>::type>::value>::type>
|
||||
explicit
|
||||
parser(Arg1&& arg1, ArgN&&... argn);
|
||||
#endif
|
||||
|
||||
/** Construct a parser from another parser, changing the Body type.
|
||||
|
||||
This constructs a new parser by move constructing the
|
||||
header from another parser with a different body type. The
|
||||
constructed-from parser must not have any parsed body octets or
|
||||
initialized @b BodyWriter, otherwise an exception is generated.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
// Deferred body type commitment
|
||||
request_parser<empty_body> req0;
|
||||
...
|
||||
request_parser<string_body> req{std::move(req0)};
|
||||
@endcode
|
||||
|
||||
If an exception is thrown, the state of the constructed-from
|
||||
parser is undefined.
|
||||
|
||||
@param parser The other parser to construct from. After
|
||||
this call returns, the constructed-from parser may only
|
||||
be destroyed.
|
||||
|
||||
@param args Optional arguments forwarded to the message
|
||||
constructor.
|
||||
|
||||
@throws std::invalid_argument Thrown when the constructed-from
|
||||
parser has already initialized a body writer.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if the other parser uses a different body type.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class OtherBody, class... Args>
|
||||
#else
|
||||
template<class OtherBody, class... Args,
|
||||
class = typename std::enable_if<
|
||||
! std::is_same<Body, OtherBody>::value>::type>
|
||||
#endif
|
||||
explicit
|
||||
parser(parser<isRequest, OtherBody,
|
||||
Allocator>&& parser, Args&&... args);
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Depending on the parser's progress,
|
||||
parts of this object may be incomplete.
|
||||
*/
|
||||
value_type const&
|
||||
get() const
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Depending on the parser's progress,
|
||||
parts of this object may be incomplete.
|
||||
*/
|
||||
value_type&
|
||||
get()
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed message.
|
||||
|
||||
Ownership is transferred to the caller.
|
||||
Depending on the parser's progress,
|
||||
parts of this object may be incomplete.
|
||||
|
||||
@par Requires
|
||||
|
||||
@ref value_type is @b MoveConstructible
|
||||
*/
|
||||
value_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(m_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(m_);
|
||||
}
|
||||
|
||||
/** Set a callback to be invoked on each chunk header.
|
||||
|
||||
The callback will be invoked once for every chunk in the message
|
||||
payload, as well as once for the last chunk. The invocation
|
||||
happens after the chunk header is available but before any body
|
||||
octets have been parsed.
|
||||
|
||||
The extensions are provided in raw, validated form, use
|
||||
@ref chunk_extensions::parse to parse the extensions into a
|
||||
structured container for easier access.
|
||||
The implementation type-erases the callback without requiring
|
||||
a dynamic allocation. For this reason, the callback object is
|
||||
passed by a non-constant reference.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
auto callback =
|
||||
[](std::uint64_t size, string_view extensions, error_code& ec)
|
||||
{
|
||||
//...
|
||||
};
|
||||
parser.on_chunk_header(callback);
|
||||
@endcode
|
||||
|
||||
@param cb The function to set, which must be invocable with
|
||||
this equivalent signature:
|
||||
@code
|
||||
void
|
||||
on_chunk_header(
|
||||
std::uint64_t size, // Size of the chunk, zero for the last chunk
|
||||
string_view extensions, // The chunk-extensions in raw form
|
||||
error_code& ec); // May be set by the callback to indicate an error
|
||||
@endcode
|
||||
*/
|
||||
template<class Callback>
|
||||
void
|
||||
on_chunk_header(Callback& cb)
|
||||
{
|
||||
// Callback may not be constant, caller is responsible for
|
||||
// managing the lifetime of the callback. Copies are not made.
|
||||
BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
|
||||
|
||||
// Can't set the callback after receiving any chunk data!
|
||||
BOOST_ASSERT(! wr_inited_);
|
||||
|
||||
cb_h_ = std::ref(cb);
|
||||
}
|
||||
|
||||
/** Set a callback to be invoked on chunk body data
|
||||
|
||||
The provided function object will be invoked one or more times
|
||||
to provide buffers corresponding to the chunk body for the current
|
||||
chunk. The callback receives the number of octets remaining in this
|
||||
chunk body including the octets in the buffer provided.
|
||||
|
||||
The callback must return the number of octets actually consumed.
|
||||
Any octets not consumed will be presented again in a subsequent
|
||||
invocation of the callback.
|
||||
The implementation type-erases the callback without requiring
|
||||
a dynamic allocation. For this reason, the callback object is
|
||||
passed by a non-constant reference.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
auto callback =
|
||||
[](std::uint64_t remain, string_view body, error_code& ec)
|
||||
{
|
||||
//...
|
||||
};
|
||||
parser.on_chunk_body(callback);
|
||||
@endcode
|
||||
|
||||
@param cb The function to set, which must be invocable with
|
||||
this equivalent signature:
|
||||
@code
|
||||
std::size_t
|
||||
on_chunk_header(
|
||||
std::uint64_t remain, // Octets remaining in this chunk, includes `body`
|
||||
string_view body, // A buffer holding some or all of the remainder of the chunk body
|
||||
error_code& ec); // May be set by the callback to indicate an error
|
||||
@endcode
|
||||
*/
|
||||
template<class Callback>
|
||||
void
|
||||
on_chunk_body(Callback& cb)
|
||||
{
|
||||
// Callback may not be constant, caller is responsible for
|
||||
// managing the lifetime of the callback. Copies are not made.
|
||||
BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
|
||||
|
||||
// Can't set the callback after receiving any chunk data!
|
||||
BOOST_ASSERT(! wr_inited_);
|
||||
|
||||
cb_b_ = std::ref(cb);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser<isRequest, parser>;
|
||||
|
||||
void
|
||||
on_request_impl(
|
||||
verb method,
|
||||
string_view method_str,
|
||||
string_view target,
|
||||
int version,
|
||||
error_code& ec)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_.target(target);
|
||||
if(method != verb::unknown)
|
||||
m_.method(method);
|
||||
else
|
||||
m_.method_string(method_str);
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
catch(std::bad_alloc const&)
|
||||
{
|
||||
ec = error::bad_alloc;
|
||||
}
|
||||
m_.version = version;
|
||||
}
|
||||
|
||||
void
|
||||
on_response_impl(
|
||||
int code,
|
||||
string_view reason,
|
||||
int version,
|
||||
error_code& ec)
|
||||
{
|
||||
m_.result(code);
|
||||
m_.version = version;
|
||||
try
|
||||
{
|
||||
m_.reason(reason);
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
catch(std::bad_alloc const&)
|
||||
{
|
||||
ec = error::bad_alloc;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
on_field_impl(
|
||||
field name,
|
||||
string_view name_string,
|
||||
string_view value,
|
||||
error_code& ec)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_.insert(name, name_string, value);
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
catch(std::bad_alloc const&)
|
||||
{
|
||||
ec = error::bad_alloc;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
on_header_impl(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
void
|
||||
on_body_init_impl(
|
||||
boost::optional<std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
wr_.init(content_length, ec);
|
||||
wr_inited_ = true;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
on_body_impl(
|
||||
string_view body,
|
||||
error_code& ec)
|
||||
{
|
||||
return wr_.put(boost::asio::buffer(
|
||||
body.data(), body.size()), ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk_header_impl(
|
||||
std::uint64_t size,
|
||||
string_view extensions,
|
||||
error_code& ec)
|
||||
{
|
||||
if(cb_h_)
|
||||
return cb_h_(size, extensions, ec);
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
std::size_t
|
||||
on_chunk_body_impl(
|
||||
std::uint64_t remain,
|
||||
string_view body,
|
||||
error_code& ec)
|
||||
{
|
||||
if(cb_b_)
|
||||
return cb_b_(remain, body, ec);
|
||||
return wr_.put(boost::asio::buffer(
|
||||
body.data(), body.size()), ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_finish_impl(error_code& ec)
|
||||
{
|
||||
wr_.finish(ec);
|
||||
}
|
||||
};
|
||||
|
||||
/// An HTTP/1 parser for producing a request message.
|
||||
template<class Body, class Allocator = std::allocator<char>>
|
||||
using request_parser = parser<true, Body, Allocator>;
|
||||
|
||||
/// An HTTP/1 parser for producing a response message.
|
||||
template<class Body, class Allocator = std::allocator<char>>
|
||||
using response_parser = parser<false, Body, Allocator>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/parser.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,766 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_READ_HPP
|
||||
#define BOOST_BEAST_HTTP_READ_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/async_result.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/http/basic_parser.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Read part of a message from a stream using a parser.
|
||||
|
||||
This function is used to read part of a message from a stream into a
|
||||
subclass of @ref basic_parser.
|
||||
The call will block until one of the following conditions is true:
|
||||
|
||||
@li A call to @ref basic_parser::put with a non-empty buffer sequence
|
||||
is successful.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or
|
||||
more calls to the stream's `read_some` function.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@return The number of bytes consumed by the parser.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read part of a message from a stream using a parser.
|
||||
|
||||
This function is used to read part of a message from a stream into a
|
||||
subclass of @ref basic_parser.
|
||||
The call will block until one of the following conditions is true:
|
||||
|
||||
@li A call to @ref basic_parser::put with a non-empty buffer sequence
|
||||
is successful.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or
|
||||
more calls to the stream's `read_some` function.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
The function returns the number of bytes processed from the dynamic
|
||||
buffer. The caller should remove these bytes by calling `consume` on
|
||||
the dynamic buffer, regardless of any error.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes consumed by the parser.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read part of a message asynchronously from a stream using a parser.
|
||||
|
||||
This function is used to asynchronously read part of a message from
|
||||
a stream into a subclass of @ref basic_parser.
|
||||
The function call always returns immediately. The asynchronous operation
|
||||
will continue until one of the following conditions is true:
|
||||
|
||||
@li A call to @ref basic_parser::put with a non-empty buffer sequence
|
||||
is successful.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to
|
||||
the next layer's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other 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.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_used // the number of bytes consumed by the parser
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
The completion handler will receive as a parameter the number
|
||||
of octets processed from the dynamic buffer. The octets should
|
||||
be removed by calling `consume` on the dynamic buffer after
|
||||
the read completes, regardless of any error.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code, std::size_t)>
|
||||
#endif
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read a header from a stream using a parser.
|
||||
|
||||
This function is used to read a header from a stream into a subclass
|
||||
of @ref basic_parser.
|
||||
The call will block until one of the following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or
|
||||
more calls to the stream's `read_some` function.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read a header from a stream using a parser.
|
||||
|
||||
This function is used to read a header from a stream into a subclass
|
||||
of @ref basic_parser.
|
||||
The call will block until one of the following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or
|
||||
more calls to the stream's `read_some` function.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a header from a stream asynchronously using a parser.
|
||||
|
||||
This function is used to asynchronously read a header from a stream
|
||||
into a subclass of @ref basic_parser.
|
||||
The function call always returns immediately. The asynchronous operation
|
||||
will continue until one of the following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the stream's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other operations until this operation completes.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @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`.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
#endif
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read a complete message from a stream using a parser.
|
||||
|
||||
This function is used to read a complete message from a stream into a
|
||||
subclass of @ref basic_parser.
|
||||
The call will block until one of the following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or
|
||||
more calls to the stream's `read_some` function.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read a complete message from a stream using a parser.
|
||||
|
||||
This function is used to read a complete message from a stream into a
|
||||
subclass of @ref basic_parser.
|
||||
The call will block until one of the following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or
|
||||
more calls to the stream's `read_some` function.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a complete message from a stream asynchronously using a parser.
|
||||
|
||||
This function is used to asynchronously read a complete message from a
|
||||
stream into a subclass of @ref basic_parser.
|
||||
The function call always returns immediately. The asynchronous operation
|
||||
will continue until one of the following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the stream's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other operations until this operation completes.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @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`.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
#endif
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read a complete message from a stream.
|
||||
|
||||
This function is used to read a complete message from a stream using HTTP/1.
|
||||
The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is read.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or
|
||||
more calls to the stream's `read_some` function.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object in which to store the message contents.
|
||||
This object should not have previous contents, otherwise
|
||||
the behavior is undefined.
|
||||
The type must be @b MoveAssignable and @b MoveConstructible.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg);
|
||||
|
||||
/** Read a complete message from a stream.
|
||||
|
||||
This function is used to read a complete message from a stream using HTTP/1.
|
||||
The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is read.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or
|
||||
more calls to the stream's `read_some` function.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object in which to store the message contents.
|
||||
This object should not have previous contents, otherwise
|
||||
the behavior is undefined.
|
||||
The type must be @b MoveAssignable and @b MoveConstructible.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a complete message from a stream asynchronously.
|
||||
|
||||
This function is used to asynchronously read a complete message from a
|
||||
stream using HTTP/1.
|
||||
The function call always returns immediately. The asynchronous operation
|
||||
will continue until one of the following conditions is true:
|
||||
|
||||
@li The entire message is read.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the stream's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other operations until this operation completes.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being read. This additional data is stored
|
||||
in the dynamic buffer, which must be retained for subsequent reads.
|
||||
|
||||
If the stream returns the error `boost::asio::error::eof` indicating the
|
||||
end of file during a read, the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no octets were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any octets were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. A subsequent attempt to read will
|
||||
return @ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object in which to store the message contents.
|
||||
This object should not have previous contents, otherwise
|
||||
the behavior is undefined.
|
||||
The type must be @b MoveAssignable and @b MoveConstructible.
|
||||
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @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 Allocator,
|
||||
class ReadHandler>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
#endif
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
ReadHandler&& handler);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/read.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,329 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_RFC7230_HPP
|
||||
#define BOOST_BEAST_HTTP_RFC7230_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <boost/beast/http/detail/basic_parsed_list.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A list of parameters in an HTTP extension field value.
|
||||
|
||||
This container allows iteration of the parameter list in an HTTP
|
||||
extension. The parameter list is a series of name/value pairs
|
||||
with each pair starting with a semicolon. The value is optional.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS [ "=" OWS ( token / quoted-string ) ]
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use @ref begin and @ref end, or range-for to iterate each
|
||||
item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& param : param_list{";level=9;no_context_takeover;bits=15"})
|
||||
{
|
||||
std::cout << ";" << param.first;
|
||||
if(! param.second.empty())
|
||||
std::cout << "=" << param.second;
|
||||
std::cout << "\n";
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class param_list
|
||||
{
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/** The type of each element in the list.
|
||||
|
||||
The first string in the pair is the name of the parameter,
|
||||
and the second string in the pair is its value (which may
|
||||
be empty).
|
||||
*/
|
||||
using value_type =
|
||||
std::pair<string_view, string_view>;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
/// Default constructor.
|
||||
param_list() = default;
|
||||
|
||||
/** Construct a list.
|
||||
|
||||
@param s A string containing the list contents. The string
|
||||
must remain valid for the lifetime of the container.
|
||||
*/
|
||||
explicit
|
||||
param_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A list of extensions in a comma separated HTTP field value.
|
||||
|
||||
This container allows iteration of the extensions in an HTTP
|
||||
field value. The extension list is a comma separated list of
|
||||
token parameter list pairs.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-list
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS [ "=" OWS ( token / quoted-string ) ]
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use @ref begin and @ref end, or range-for to iterate each
|
||||
item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& ext : ext_list{"none, 7z;level=9, zip;no_context_takeover;bits=15"})
|
||||
{
|
||||
std::cout << ext.first << "\n";
|
||||
for(auto const& param : ext.second)
|
||||
{
|
||||
std::cout << ";" << param.first;
|
||||
if(! param.second.empty())
|
||||
std::cout << "=" << param.second;
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class ext_list
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/** The type of each element in the list.
|
||||
|
||||
The first element of the pair is the extension token, and the
|
||||
second element of the pair is an iterable container holding the
|
||||
extension's name/value parameters.
|
||||
*/
|
||||
using value_type = std::pair<string_view, param_list>;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
/** Construct a list.
|
||||
|
||||
@param s A string containing the list contents. The string
|
||||
must remain valid for the lifetime of the container.
|
||||
*/
|
||||
explicit
|
||||
ext_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
|
||||
/** Find a token in the list.
|
||||
|
||||
@param s The token to find. A case-insensitive comparison is used.
|
||||
|
||||
@return An iterator to the matching token, or `end()` if no
|
||||
token exists.
|
||||
*/
|
||||
template<class T>
|
||||
const_iterator
|
||||
find(T const& s);
|
||||
|
||||
/** Return `true` if a token is present in the list.
|
||||
|
||||
@param s The token to find. A case-insensitive comparison is used.
|
||||
*/
|
||||
template<class T>
|
||||
bool
|
||||
exists(T const& s);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A list of tokens in a comma separated HTTP field value.
|
||||
|
||||
This container allows iteration of a list of items in a
|
||||
header field value. The input is a comma separated list of
|
||||
tokens.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS token ] )
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use @ref begin and @ref end, or range-for to iterate each
|
||||
item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& token : token_list{"apple, pear, banana"})
|
||||
std::cout << token << "\n";
|
||||
@endcode
|
||||
*/
|
||||
class token_list
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/// The type of each element in the token list.
|
||||
using value_type = string_view;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
/** Construct a list.
|
||||
|
||||
@param s A string containing the list contents. The string
|
||||
must remain valid for the lifetime of the container.
|
||||
*/
|
||||
explicit
|
||||
token_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
|
||||
/** Return `true` if a token is present in the list.
|
||||
|
||||
@param s The token to find. A case-insensitive comparison is used.
|
||||
*/
|
||||
template<class T>
|
||||
bool
|
||||
exists(T const& s);
|
||||
};
|
||||
|
||||
/** A list of tokens in a comma separated HTTP field value.
|
||||
|
||||
This container allows iteration of a list of items in a
|
||||
header field value. The input is a comma separated list of
|
||||
tokens.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS token ] )
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use `begin` and `end`, or range-for to iterate each item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& token : token_list{"apple, pear, banana"})
|
||||
std::cout << token << "\n";
|
||||
@endcode
|
||||
*/
|
||||
using opt_token_list =
|
||||
detail::basic_parsed_list<
|
||||
detail::opt_token_list_policy>;
|
||||
|
||||
/** Returns `true` if a parsed list is parsed without errors.
|
||||
|
||||
This function iterates a single pass through a parsed list
|
||||
and returns `true` if there were no parsing errors, else
|
||||
returns `false`.
|
||||
*/
|
||||
template<class Policy>
|
||||
bool
|
||||
validate_list(detail::basic_parsed_list<
|
||||
Policy> const& list);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/rfc7230.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,415 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_SERIALIZER_HPP
|
||||
#define BOOST_BEAST_HTTP_SERIALIZER_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/buffer_cat.hpp>
|
||||
#include <boost/beast/core/buffer_prefix.hpp>
|
||||
#include <boost/beast/core/consuming_buffers.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/chunk_encode.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
# if defined(BOOST_GCC) && BOOST_GCC < 50000 && BOOST_VERSION < 106600
|
||||
# define BOOST_BEAST_NO_BIG_VARIANTS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Provides buffer oriented HTTP message serialization functionality.
|
||||
|
||||
An object of this type is used to serialize a complete
|
||||
HTTP message into a sequence of octets. To use this class,
|
||||
construct an instance with the message to be serialized.
|
||||
|
||||
The implementation will automatically perform chunk encoding
|
||||
if the contents of the message indicate that chunk encoding
|
||||
is required. If the semantics of the message indicate that
|
||||
the connection should be closed after the message is sent, the
|
||||
function @ref keep_alive will return `false`.
|
||||
|
||||
Chunked output produced by the serializer never contains chunk
|
||||
extensions or trailers, and the location of chunk boundaries
|
||||
is not specified. If callers require chunk extensions, trailers,
|
||||
or control over the exact contents of each chunk they should
|
||||
use the serializer to write just the message header, and then
|
||||
assume control over serializing the chunked payload by using
|
||||
the chunk buffer sequence types @ref chunk_body, @ref chunk_crlf,
|
||||
@ref chunk_header, and @ref chunk_last.
|
||||
|
||||
@tparam isRequest `true` if the message is a request.
|
||||
|
||||
@tparam Body The body type of the message.
|
||||
|
||||
@tparam Fields The type of fields in the message.
|
||||
*/
|
||||
template<
|
||||
bool isRequest,
|
||||
class Body,
|
||||
class Fields = fields>
|
||||
class serializer
|
||||
{
|
||||
public:
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
|
||||
/** The type of message this serializer uses
|
||||
|
||||
This may be const or non-const depending on the
|
||||
implementation of the corresponding @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = implementation_defined;
|
||||
#else
|
||||
using value_type =
|
||||
typename std::conditional<
|
||||
std::is_constructible<typename Body::reader,
|
||||
message<isRequest, Body, Fields>&>::value &&
|
||||
! std::is_constructible<typename Body::reader,
|
||||
message<isRequest, Body, Fields> const&>::value,
|
||||
message<isRequest, Body, Fields>,
|
||||
message<isRequest, Body, Fields> const>::type;
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
do_construct = 0,
|
||||
|
||||
do_init = 10,
|
||||
do_header_only = 20,
|
||||
do_header = 30,
|
||||
do_body = 40,
|
||||
|
||||
do_init_c = 50,
|
||||
do_header_only_c = 60,
|
||||
do_header_c = 70,
|
||||
do_body_c = 80,
|
||||
do_final_c = 90,
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
do_body_final_c = 100,
|
||||
do_all_c = 110,
|
||||
#endif
|
||||
|
||||
do_complete = 120
|
||||
};
|
||||
|
||||
void frdinit(std::true_type);
|
||||
void frdinit(std::false_type);
|
||||
|
||||
template<class T1, class T2, class Visit>
|
||||
void
|
||||
do_visit(error_code& ec, Visit& visit);
|
||||
|
||||
using reader = typename Body::reader;
|
||||
|
||||
using cb1_t = consuming_buffers<typename
|
||||
Fields::reader::const_buffers_type>; // header
|
||||
using pcb1_t = buffer_prefix_view<cb1_t const&>;
|
||||
|
||||
using cb2_t = consuming_buffers<buffer_cat_view<
|
||||
typename Fields::reader::const_buffers_type,// header
|
||||
typename reader::const_buffers_type>>; // body
|
||||
using pcb2_t = buffer_prefix_view<cb2_t const&>;
|
||||
|
||||
using cb3_t = consuming_buffers<
|
||||
typename reader::const_buffers_type>; // body
|
||||
using pcb3_t = buffer_prefix_view<cb3_t const&>;
|
||||
|
||||
using cb4_t = consuming_buffers<buffer_cat_view<
|
||||
typename Fields::reader::const_buffers_type,// header
|
||||
detail::chunk_size, // chunk-size
|
||||
boost::asio::const_buffers_1, // chunk-ext
|
||||
chunk_crlf, // crlf
|
||||
typename reader::const_buffers_type, // body
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb4_t = buffer_prefix_view<cb4_t const&>;
|
||||
|
||||
using cb5_t = consuming_buffers<buffer_cat_view<
|
||||
detail::chunk_size, // chunk-header
|
||||
boost::asio::const_buffers_1, // chunk-ext
|
||||
chunk_crlf, // crlf
|
||||
typename reader::const_buffers_type, // body
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb5_t = buffer_prefix_view<cb5_t const&>;
|
||||
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
using cb6_t = consuming_buffers<buffer_cat_view<
|
||||
detail::chunk_size, // chunk-header
|
||||
boost::asio::const_buffers_1, // chunk-size
|
||||
chunk_crlf, // crlf
|
||||
typename reader::const_buffers_type, // body
|
||||
chunk_crlf, // crlf
|
||||
boost::asio::const_buffers_1, // chunk-final
|
||||
boost::asio::const_buffers_1, // trailers
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb6_t = buffer_prefix_view<cb6_t const&>;
|
||||
|
||||
using cb7_t = consuming_buffers<buffer_cat_view<
|
||||
typename Fields::reader::const_buffers_type,// header
|
||||
detail::chunk_size, // chunk-size
|
||||
boost::asio::const_buffers_1, // chunk-ext
|
||||
chunk_crlf, // crlf
|
||||
typename reader::const_buffers_type, // body
|
||||
chunk_crlf, // crlf
|
||||
boost::asio::const_buffers_1, // chunk-final
|
||||
boost::asio::const_buffers_1, // trailers
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb7_t = buffer_prefix_view<cb7_t const&>;
|
||||
#endif
|
||||
|
||||
using cb8_t = consuming_buffers<buffer_cat_view<
|
||||
boost::asio::const_buffers_1, // chunk-final
|
||||
boost::asio::const_buffers_1, // trailers
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb8_t = buffer_prefix_view<cb8_t const&>;
|
||||
|
||||
value_type& m_;
|
||||
reader rd_;
|
||||
boost::optional<typename Fields::reader> frd_;
|
||||
boost::variant<boost::blank,
|
||||
cb1_t, cb2_t, cb3_t, cb4_t, cb5_t
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
,cb6_t, cb7_t
|
||||
#endif
|
||||
, cb8_t> v_;
|
||||
boost::variant<boost::blank,
|
||||
pcb1_t, pcb2_t, pcb3_t, pcb4_t, pcb5_t
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
,pcb6_t, pcb7_t
|
||||
#endif
|
||||
, pcb8_t> pv_;
|
||||
std::size_t limit_ =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
int s_ = do_construct;
|
||||
bool split_ = false;
|
||||
bool header_done_ = false;
|
||||
bool chunked_;
|
||||
bool keep_alive_;
|
||||
bool more_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
The implementation guarantees that the message passed on
|
||||
construction will not be accessed until the first call to
|
||||
@ref next. This allows the message to be lazily created.
|
||||
For example, if the header is filled in before serialization.
|
||||
|
||||
@param msg A reference to the message to serialize, which must
|
||||
remain valid for the lifetime of the serializer. Depending on
|
||||
the type of Body used, this may or may not be a `const` reference.
|
||||
|
||||
@note This function participates in overload resolution only if
|
||||
Body::reader is constructible from a `const` message reference.
|
||||
*/
|
||||
explicit
|
||||
serializer(value_type& msg);
|
||||
|
||||
/// Returns the message being serialized
|
||||
value_type&
|
||||
get()
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/// Returns the serialized buffer size limit
|
||||
std::size_t
|
||||
limit()
|
||||
{
|
||||
return limit_;
|
||||
}
|
||||
|
||||
/** Set the serialized buffer size limit
|
||||
|
||||
This function adjusts the limit on the maximum size of the
|
||||
buffers passed to the visitor. The new size limit takes effect
|
||||
in the following call to @ref next.
|
||||
|
||||
The default is no buffer size limit.
|
||||
|
||||
@param limit The new buffer size limit. If this number
|
||||
is zero, the size limit is removed.
|
||||
*/
|
||||
void
|
||||
limit(std::size_t limit)
|
||||
{
|
||||
limit_ = limit > 0 ? limit :
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
}
|
||||
|
||||
/** Returns `true` if we will pause after writing the complete header.
|
||||
*/
|
||||
bool
|
||||
split()
|
||||
{
|
||||
return split_;
|
||||
}
|
||||
|
||||
/** Set whether the header and body are written separately.
|
||||
|
||||
When the split feature is enabled, the implementation will
|
||||
write only the octets corresponding to the serialized header
|
||||
first. If the header has already been written, this function
|
||||
will have no effect on output.
|
||||
*/
|
||||
void
|
||||
split(bool v)
|
||||
{
|
||||
split_ = v;
|
||||
}
|
||||
|
||||
/** Return `true` if serialization of the header is complete.
|
||||
|
||||
This function indicates whether or not all buffers containing
|
||||
serialized header octets have been retrieved.
|
||||
*/
|
||||
bool
|
||||
is_header_done()
|
||||
{
|
||||
return header_done_;
|
||||
}
|
||||
|
||||
/** Return `true` if serialization is complete.
|
||||
|
||||
The operation is complete when all octets corresponding
|
||||
to the serialized representation of the message have been
|
||||
successfully retrieved.
|
||||
*/
|
||||
bool
|
||||
is_done()
|
||||
{
|
||||
return s_ == do_complete;
|
||||
}
|
||||
|
||||
/** Return `true` if the serializer will apply chunk-encoding.
|
||||
|
||||
This function may only be called if @ref is_header_done
|
||||
would return `true`.
|
||||
*/
|
||||
bool
|
||||
chunked()
|
||||
{
|
||||
return chunked_;
|
||||
}
|
||||
|
||||
/** Return `true` if Connection: keep-alive semantic is indicated.
|
||||
|
||||
This function returns `true` if the semantics of the
|
||||
message indicate that the connection should be kept open
|
||||
after the serialized message has been transmitted. The
|
||||
value depends on the HTTP version of the message,
|
||||
the tokens in the Connection header, and the metadata
|
||||
describing the payload body.
|
||||
|
||||
Depending on the payload body, the end of the message may
|
||||
be indicated by connection closuire. In order for the
|
||||
recipient (if any) to receive a complete message, the
|
||||
underlying stream or network connection must be closed
|
||||
when this function returns `false`.
|
||||
|
||||
This function may only be called if @ref is_header_done
|
||||
would return `true`.
|
||||
*/
|
||||
bool
|
||||
keep_alive()
|
||||
{
|
||||
return keep_alive_;
|
||||
}
|
||||
|
||||
/** Returns the next set of buffers in the serialization.
|
||||
|
||||
This function will attempt to call the `visit` function
|
||||
object with a @b ConstBufferSequence of unspecified type
|
||||
representing the next set of buffers in the serialization
|
||||
of the message represented by this object.
|
||||
|
||||
If there are no more buffers in the serialization, the
|
||||
visit function will not be called. In this case, no error
|
||||
will be indicated, and the function @ref is_done will
|
||||
return `true`.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@param visit The function to call. The equivalent function
|
||||
signature of this object must be:
|
||||
@code
|
||||
template<class ConstBufferSequence>
|
||||
void visit(error_code&, ConstBufferSequence const&);
|
||||
@endcode
|
||||
The function is not copied, if no error occurs it will be
|
||||
invoked before the call to @ref next returns.
|
||||
|
||||
*/
|
||||
template<class Visit>
|
||||
void
|
||||
next(error_code& ec, Visit&& visit);
|
||||
|
||||
/** Consume buffer octets in the serialization.
|
||||
|
||||
This function should be called after one or more octets
|
||||
contained in the buffers provided in the prior call
|
||||
to @ref next have been used.
|
||||
|
||||
After a call to @ref consume, callers should check the
|
||||
return value of @ref is_done to determine if the entire
|
||||
message has been serialized.
|
||||
|
||||
@param n The number of octets to consume. This number must
|
||||
be greater than zero and no greater than the number of
|
||||
octets in the buffers provided in the prior call to @ref next.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n);
|
||||
|
||||
/** Provides low-level access to the associated @b BodyReader
|
||||
|
||||
This function provides access to the instance of the reader
|
||||
associated with the body and created by the serializer
|
||||
upon construction. The behavior of accessing this object
|
||||
is defined by the specification of the particular reader
|
||||
and its associated body.
|
||||
|
||||
@return A reference to the reader.
|
||||
*/
|
||||
reader&
|
||||
reader_impl()
|
||||
{
|
||||
return rd_;
|
||||
}
|
||||
};
|
||||
|
||||
/// A serializer for HTTP/1 requests
|
||||
template<class Body, class Fields = fields>
|
||||
using request_serializer = serializer<true, Body, Fields>;
|
||||
|
||||
/// A serializer for HTTP/1 responses
|
||||
template<class Body, class Fields = fields>
|
||||
using response_serializer = serializer<false, Body, Fields>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/serializer.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_SPAN_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_SPAN_BODY_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/span.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A @b Body using @ref span
|
||||
|
||||
This body uses @ref span as a memory-based container for
|
||||
holding message payloads. The container represents a
|
||||
non-owning reference to a continguous area of memory.
|
||||
Messages using this body type may be serialized and
|
||||
parsed.
|
||||
|
||||
Unlike @ref buffer_body, only one buffer may be provided
|
||||
during a parse or serialize operation.
|
||||
*/
|
||||
template<class T>
|
||||
struct span_body
|
||||
{
|
||||
private:
|
||||
static_assert(std::is_pod<T>::value,
|
||||
"POD requirements not met");
|
||||
|
||||
public:
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
using value_type = span<T>;
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body)
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
span_body, Fields> const& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return {{
|
||||
{ body_.data(),
|
||||
body_.size() * sizeof(typename
|
||||
value_type::value_type)},
|
||||
false}};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
span_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const& length, error_code& ec)
|
||||
{
|
||||
if(length && *length > body_.size())
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const n = buffer_size(buffers);
|
||||
auto const len = body_.size();
|
||||
if(n > len)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
buffer_copy(boost::asio::buffer(
|
||||
body_.data(), n), buffers);
|
||||
body_ = value_type{
|
||||
body_.data() + n, body_.size() - n};
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_STATUS_HPP
|
||||
#define BOOST_BEAST_HTTP_STATUS_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class status : unsigned
|
||||
{
|
||||
/** An unknown status-code.
|
||||
|
||||
This value indicates that the value for the status code
|
||||
is not in the list of commonly recognized status codes.
|
||||
Callers interested in the exactly value should use the
|
||||
interface which provides the raw integer.
|
||||
*/
|
||||
unknown = 0,
|
||||
|
||||
continue_ = 100,
|
||||
switching_protocols = 101,
|
||||
processing = 102,
|
||||
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
non_authoritative_information = 203,
|
||||
no_content = 204,
|
||||
reset_content = 205,
|
||||
partial_content = 206,
|
||||
multi_status = 207,
|
||||
already_reported = 208,
|
||||
im_used = 226,
|
||||
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
found = 302,
|
||||
see_other = 303,
|
||||
not_modified = 304,
|
||||
use_proxy = 305,
|
||||
temporary_redirect = 307,
|
||||
permanent_redirect = 308,
|
||||
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
payment_required = 402,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
method_not_allowed = 405,
|
||||
not_acceptable = 406,
|
||||
proxy_authentication_required = 407,
|
||||
request_timeout = 408,
|
||||
conflict = 409,
|
||||
gone = 410,
|
||||
length_required = 411,
|
||||
precondition_failed = 412,
|
||||
payload_too_large = 413,
|
||||
uri_too_long = 414,
|
||||
unsupported_media_type = 415,
|
||||
range_not_satisfiable = 416,
|
||||
expectation_failed = 417,
|
||||
misdirected_request = 421,
|
||||
unprocessable_entity = 422,
|
||||
locked = 423,
|
||||
failed_dependency = 424,
|
||||
upgrade_required = 426,
|
||||
precondition_required = 428,
|
||||
too_many_requests = 429,
|
||||
request_header_fields_too_large = 431,
|
||||
connection_closed_without_response = 444,
|
||||
unavailable_for_legal_reasons = 451,
|
||||
client_closed_request = 499,
|
||||
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503,
|
||||
gateway_timeout = 504,
|
||||
http_version_not_supported = 505,
|
||||
variant_also_negotiates = 506,
|
||||
insufficient_storage = 507,
|
||||
loop_detected = 508,
|
||||
not_extended = 510,
|
||||
network_authentication_required = 511,
|
||||
network_connect_timeout_error = 599
|
||||
};
|
||||
|
||||
/** Represents the class of a status-code.
|
||||
*/
|
||||
enum class status_class : unsigned
|
||||
{
|
||||
/// Unknown status-class
|
||||
unknown = 0,
|
||||
|
||||
/// The request was received, continuing processing.
|
||||
informational = 1,
|
||||
|
||||
/// The request was successfully received, understood, and accepted.
|
||||
successful = 2,
|
||||
|
||||
/// Further action needs to be taken in order to complete the request.
|
||||
redirection = 3,
|
||||
|
||||
/// The request contains bad syntax or cannot be fulfilled.
|
||||
client_error = 4,
|
||||
|
||||
/// The server failed to fulfill an apparently valid request.
|
||||
server_error = 5,
|
||||
};
|
||||
|
||||
/** Converts the integer to a known status-code.
|
||||
|
||||
If the integer does not match a known status code,
|
||||
@ref status::unknown is returned.
|
||||
*/
|
||||
status
|
||||
int_to_status(unsigned v);
|
||||
|
||||
/** Convert an integer to a status_class.
|
||||
|
||||
@param v The integer representing a status code.
|
||||
|
||||
@return The status class. If the integer does not match
|
||||
a known status class, @ref status_class::unknown is returned.
|
||||
*/
|
||||
status_class
|
||||
to_status_class(unsigned v);
|
||||
|
||||
/** Convert a status_code to a status_class.
|
||||
|
||||
@param v The status code to convert.
|
||||
|
||||
@return The status class.
|
||||
*/
|
||||
status_class
|
||||
to_status_class(status v);
|
||||
|
||||
/** Returns the obsolete reason-phrase text for a status code.
|
||||
|
||||
@param v The status code to use.
|
||||
*/
|
||||
string_view
|
||||
obsolete_reason(status v);
|
||||
|
||||
/// Outputs the standard reason phrase of a status code to a stream.
|
||||
std::ostream&
|
||||
operator<<(std::ostream&, status);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/status.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_STRING_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_STRING_BODY_HPP
|
||||
|
||||
#include <boost/beast/config.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A @b Body using `std::basic_string`
|
||||
|
||||
This body uses `std::basic_string` as a memory-based container
|
||||
for holding message payloads. Messages using this body type
|
||||
may be serialized and parsed.
|
||||
*/
|
||||
template<
|
||||
class CharT,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
struct basic_string_body
|
||||
{
|
||||
private:
|
||||
static_assert(
|
||||
std::is_integral<CharT>::value &&
|
||||
sizeof(CharT) == 1,
|
||||
"CharT requirements not met");
|
||||
|
||||
public:
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
using value_type =
|
||||
std::basic_string<CharT, Traits, Allocator>;
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body)
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
basic_string_body, Fields> const& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return {{const_buffers_type{
|
||||
body_.data(), body_.size()}, false}};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
basic_string_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const& length, error_code& ec)
|
||||
{
|
||||
if(length)
|
||||
{
|
||||
if(*length > (
|
||||
std::numeric_limits<std::size_t>::max)())
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
body_.reserve(
|
||||
static_cast<std::size_t>(*length));
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const extra = buffer_size(buffers);
|
||||
auto const size = body_.size();
|
||||
try
|
||||
{
|
||||
body_.resize(size + extra);
|
||||
}
|
||||
catch(std::exception const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
CharT* dest = &body_[size];
|
||||
for(boost::asio::const_buffer b : buffers)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
auto const len = boost::asio::buffer_size(b);
|
||||
Traits::copy(
|
||||
dest, buffer_cast<CharT const*>(b), len);
|
||||
dest += len;
|
||||
}
|
||||
return extra;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
/// A @b Body using `std::string`
|
||||
using string_body = basic_string_body<char>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
Loaded 100 of 375 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user