Boost prep

This commit is contained in:
Vinnie Falco committed 2017-07-23 17:34:13 -07:00
1 parent 3777cb8b9c
commit 7139dd639a
375 files changed
+5253 -3884

No files matched your search

+21
View File
@@ -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
+35
View File
@@ -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
+42
View File
@@ -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
+209
View File
@@ -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
+79
View File
@@ -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
+119
View File
@@ -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
+240
View File
@@ -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
+251
View File
@@ -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
+59
View File
@@ -0,0 +1,59 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// 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
+322
View File
@@ -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
+313
View File
@@ -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
+127
View File
@@ -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
+58
View File
@@ -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
+45
View File
@@ -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
+89
View File
@@ -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
+175
View File
@@ -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
+158
View File
@@ -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
+177
View File
@@ -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
+345
View File
@@ -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
+162
View File
@@ -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
+213
View File
@@ -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
+323
View File
@@ -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
+103
View File
@@ -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
+64
View File
@@ -0,0 +1,64 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// 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
+215
View File
@@ -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
+226
View File
@@ -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
+155
View File
@@ -0,0 +1,155 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// 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
+130
View File
@@ -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
+637
View File
@@ -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
+38
View File
@@ -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
+611
View File
@@ -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
+228
View File
@@ -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
+736
View File
@@ -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
+473
View File
@@ -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
+30
View File
@@ -0,0 +1,30 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// 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
+132
View File
@@ -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
+161
View File
@@ -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
+409
View File
@@ -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
+737
View File
@@ -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
+35
View File
@@ -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
+121
View File
@@ -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
+561
View File
@@ -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
+419
View File
@@ -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
+55
View File
@@ -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
+796
View File
@@ -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
+572
View File
@@ -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
+252
View File
@@ -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
+337
View File
@@ -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
+890
View File
@@ -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
+845
View File
@@ -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
+436
View File
@@ -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
+766
View File
@@ -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
+329
View File
@@ -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
+415
View File
@@ -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
+171
View File
@@ -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
+167
View File
@@ -0,0 +1,167 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// 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
+202
View File
@@ -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