forked from boostorg/beast
Truncate history, version 1.0.0-b2
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_ASYNC_COMPLETION_HPP
|
||||
#define BEAST_ASYNC_COMPLETION_HPP
|
||||
|
||||
#include <beast/type_check.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Helper for customizing the return type of asynchronous initiation functions.
|
||||
|
||||
This class template is used to transform caller-provided completion
|
||||
tokens in calls to asynchronous initiation functions. The transformation
|
||||
allows customization of the return type of the initiating function, and the
|
||||
function signature of the final handler.
|
||||
|
||||
@tparam CompletionToken A CompletionHandler, or a user defined type
|
||||
with specializations for customizing the return type (for example,
|
||||
`boost::asio::use_future` or `boost::asio::yield_context`).
|
||||
|
||||
@tparam Signature The callable signature of the final completion handler.
|
||||
|
||||
Example:
|
||||
@code
|
||||
...
|
||||
template<class CompletionToken>
|
||||
typename async_completion<CompletionToken,
|
||||
void(boost::system::error_code)>::result_type
|
||||
async_initfn(..., CompletionToken&& token)
|
||||
{
|
||||
async_completion<CompletionToken,
|
||||
void(boost::system::error_code)> completion(token);
|
||||
...
|
||||
return completion.result.get();
|
||||
}
|
||||
@endcode
|
||||
|
||||
See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
|
||||
Library Foundations For Asynchronous Operations</a>
|
||||
*/
|
||||
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 handler_type =
|
||||
typename boost::asio::handler_type<
|
||||
CompletionToken, Signature>::type;
|
||||
|
||||
/// The type of the value returned by the asynchronous initiation function.
|
||||
using result_type = typename
|
||||
boost::asio::async_result<handler_type>::type;
|
||||
|
||||
/** Construct the helper.
|
||||
|
||||
@param token The completion token. Copies will be made as
|
||||
required. If `CompletionToken` is movable, it may also be moved.
|
||||
*/
|
||||
async_completion(typename std::remove_reference<CompletionToken>::type& token)
|
||||
: handler(std::forward<CompletionToken>(token))
|
||||
, result(handler)
|
||||
{
|
||||
static_assert(is_Handler<handler_type, Signature>::value,
|
||||
"Handler requirements not met");
|
||||
}
|
||||
|
||||
/// The final completion handler, callable with the specified signature.
|
||||
handler_type handler;
|
||||
|
||||
/// The return value of the asynchronous initiation function.
|
||||
boost::asio::async_result<handler_type> result;
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,298 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BASIC_STREAMBUF_HPP
|
||||
#define BEAST_BASIC_STREAMBUF_HPP
|
||||
|
||||
#include <beast/detail/empty_base_optimization.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A `Streambuf` 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.
|
||||
|
||||
@tparam Allocator The allocator to use for managing memory.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_streambuf
|
||||
#if ! GENERATING_DOCS
|
||||
: private detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<std::uint8_t>>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<std::uint8_t>;
|
||||
|
||||
private:
|
||||
// Storage for the list of buffers representing the input
|
||||
// and output sequences. The allocation for each element
|
||||
// contains `element` followed by raw storage bytes.
|
||||
class element;
|
||||
|
||||
using alloc_traits = std::allocator_traits<allocator_type>;
|
||||
using list_type = typename boost::intrusive::make_list<element,
|
||||
boost::intrusive::constant_time_size<true>>::type;
|
||||
using iterator = typename list_type::iterator;
|
||||
using const_iterator = typename list_type::const_iterator;
|
||||
|
||||
using size_type = typename std::allocator_traits<Allocator>::size_type;
|
||||
using const_buffer = boost::asio::const_buffer;
|
||||
using mutable_buffer = boost::asio::mutable_buffer;
|
||||
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<iterator>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<const_iterator>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
list_type list_; // list of allocated buffers
|
||||
iterator out_; // element that contains out_pos_
|
||||
size_type alloc_size_; // min amount to allocate
|
||||
size_type in_size_ = 0; // size of the input sequence
|
||||
size_type in_pos_ = 0; // input offset in list_.front()
|
||||
size_type out_pos_ = 0; // output offset in *out_
|
||||
size_type out_end_ = 0; // output end offset in list_.back()
|
||||
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_buffers_type;
|
||||
|
||||
class mutable_buffers_type;
|
||||
|
||||
#endif
|
||||
|
||||
/// Destructor.
|
||||
~basic_streambuf();
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The output sequence of this object will be empty.
|
||||
|
||||
After the move, the moved-from object will have an
|
||||
empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
|
||||
@param other The stream buffer to move from.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf&& other);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The output sequence of this object will be empty.
|
||||
|
||||
After the move, the moved-from object will have an
|
||||
empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
|
||||
@param other The stream buffer to move from.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf&& other,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The output sequence of this object will be empty.
|
||||
|
||||
After the move, the moved-from object will have an
|
||||
empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
|
||||
@param other The stream buffer to move from.
|
||||
*/
|
||||
basic_streambuf&
|
||||
operator=(basic_streambuf&& other);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_streambuf(basic_streambuf const& other);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
The output sequence of this object will be empty.
|
||||
|
||||
@param other The stream buffer to copy.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf const& other,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Copy assignment.
|
||||
|
||||
The output sequence of this object will be empty.
|
||||
|
||||
@param other The stream buffer to copy.
|
||||
*/
|
||||
basic_streambuf& operator=(basic_streambuf const& other);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
The output sequence of this object will be empty.
|
||||
|
||||
@param other The stream buffer to copy.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf(basic_streambuf<OtherAlloc> const& other);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
The output sequence of this object will be empty.
|
||||
|
||||
@param other The stream buffer to copy.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf(basic_streambuf<OtherAlloc> const& other,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Copy assignment.
|
||||
|
||||
The output sequence of this object will be empty.
|
||||
|
||||
@param other The stream buffer to copy.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const& other);
|
||||
|
||||
/** Default constructor.
|
||||
|
||||
@param alloc_size The size of buffer to allocate. This is a soft
|
||||
limit, calls to prepare for buffers exceeding this size will allocate
|
||||
the larger size.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_streambuf(std::size_t alloc_size = 1024,
|
||||
Allocator const& alloc = allocator_type{});
|
||||
|
||||
/// Get the associated allocator
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return this->member();
|
||||
}
|
||||
|
||||
/// Get the maximum size of the basic_streambuf.
|
||||
size_type
|
||||
max_size() const
|
||||
{
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
}
|
||||
|
||||
/// Get the size of the input sequence.
|
||||
size_type
|
||||
size() const
|
||||
{
|
||||
return in_size_;
|
||||
}
|
||||
|
||||
/// Get a list of buffers that represents the output sequence, with the given size.
|
||||
mutable_buffers_type
|
||||
prepare(size_type n);
|
||||
|
||||
/// Move bytes from the output sequence to the input sequence.
|
||||
void
|
||||
commit(size_type n);
|
||||
|
||||
/// Get a list of buffers that represents the input sequence.
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(size_type n);
|
||||
|
||||
/// Clear everything.
|
||||
void
|
||||
clear();
|
||||
|
||||
// Helper for read_until
|
||||
template<class OtherAllocator>
|
||||
friend
|
||||
std::size_t
|
||||
read_size_helper(basic_streambuf<
|
||||
OtherAllocator> const& streambuf, std::size_t max_size);
|
||||
|
||||
private:
|
||||
void
|
||||
move_assign(basic_streambuf& other, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_streambuf& other, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_streambuf const& other, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_streambuf const& other, std::true_type);
|
||||
|
||||
void
|
||||
delete_list();
|
||||
|
||||
std::size_t
|
||||
prepare_size() const;
|
||||
|
||||
void
|
||||
debug_check() const;
|
||||
};
|
||||
|
||||
/** Format output to a stream buffer.
|
||||
|
||||
@param streambuf The streambuf to write to.
|
||||
|
||||
@param t The object to write.
|
||||
|
||||
@return The stream buffer.
|
||||
*/
|
||||
template<class Alloc, class T>
|
||||
basic_streambuf<Alloc>&
|
||||
operator<<(basic_streambuf<Alloc>& streambuf, T const& t);
|
||||
|
||||
/** Convert the entire basic_streambuf to a string.
|
||||
|
||||
@param streambuf The streambuf to convert.
|
||||
|
||||
@return A string representing the contents of the input sequence.
|
||||
*/
|
||||
template<class Allocator>
|
||||
std::string
|
||||
to_string(basic_streambuf<Allocator> const& streambuf);
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/impl/basic_streambuf.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BIND_HANDLER_HPP
|
||||
#define BEAST_BIND_HANDLER_HPP
|
||||
|
||||
#include <beast/detail/integer_sequence.hpp>
|
||||
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
||||
#include <boost/asio/detail/handler_cont_helpers.hpp>
|
||||
#include <boost/asio/detail/handler_invoke_helpers.hpp>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/* Nullary handler that calls Handler with bound arguments.
|
||||
|
||||
The bound handler provides the same io_service execution
|
||||
guarantees as the original handler.
|
||||
*/
|
||||
template<class Handler, class... Args>
|
||||
class bound_handler
|
||||
{
|
||||
private:
|
||||
using args_type = std::tuple<typename std::decay<Args>::type...>;
|
||||
|
||||
Handler h_;
|
||||
args_type args_;
|
||||
|
||||
template<class Tuple, std::size_t... S>
|
||||
static void invoke(Handler& h, Tuple& args,
|
||||
index_sequence<S...>)
|
||||
{
|
||||
h(std::get<S>(args)...);
|
||||
}
|
||||
|
||||
public:
|
||||
using result_type = void;
|
||||
|
||||
template<class DeducedHandler>
|
||||
explicit
|
||||
bound_handler(DeducedHandler&& handler, Args&&... args)
|
||||
: h_(std::forward<DeducedHandler>(handler))
|
||||
, args_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
invoke(h_, args_,
|
||||
index_sequence_for<Args...> ());
|
||||
}
|
||||
|
||||
void
|
||||
operator()() const
|
||||
{
|
||||
invoke(h_, args_,
|
||||
index_sequence_for<Args...> ());
|
||||
}
|
||||
|
||||
friend
|
||||
void*
|
||||
asio_handler_allocate(
|
||||
std::size_t size, bound_handler* h)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, h->h_);
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size, bound_handler* h)
|
||||
{
|
||||
boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, h->h_);
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
asio_handler_is_continuation(bound_handler* h)
|
||||
{
|
||||
return boost_asio_handler_cont_helpers::
|
||||
is_continuation (h->h_);
|
||||
}
|
||||
|
||||
template<class F>
|
||||
friend
|
||||
void
|
||||
asio_handler_invoke(F&& f, bound_handler* h)
|
||||
{
|
||||
boost_asio_handler_invoke_helpers::
|
||||
invoke(f, h->h_);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Bind parameters to a completion handler, creating a wrapped handler.
|
||||
|
||||
This function creates a new handler which invoked with no parameters
|
||||
calls the original handler with the list of bound arguments. The passed
|
||||
handler and arguments are forwarded into the returned handler, which
|
||||
provides the same `io_service` execution guarantees as the original
|
||||
handler.
|
||||
|
||||
Unlike `io_service::wrap`, the returned handler can be used in a
|
||||
subsequent call to `io_service::post` instead of `io_service::dispatch`,
|
||||
to ensure that the handler will not be invoked immediately by the
|
||||
calling function.
|
||||
|
||||
Example:
|
||||
@code
|
||||
template<class AsyncReadStream, class ReadHandler>
|
||||
void
|
||||
do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
|
||||
{
|
||||
stream.get_io_service().post(
|
||||
bind_handler(std::forward<ReadHandler>(handler),
|
||||
boost::asio::error::operation_aborted, 0));
|
||||
}
|
||||
@endcode
|
||||
|
||||
@param handler The handler to wrap.
|
||||
|
||||
@param args A list of arguments to bind to the handler. The
|
||||
arguments are forwarded into the returned object.
|
||||
*/
|
||||
template<class CompletionHandler, class... Args>
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
detail::bound_handler<
|
||||
typename std::decay<CompletionHandler>::type, Args...>
|
||||
#endif
|
||||
bind_handler(CompletionHandler&& handler, Args&&... args)
|
||||
{
|
||||
return detail::bound_handler<typename std::decay<
|
||||
CompletionHandler>::type, Args...>(std::forward<
|
||||
CompletionHandler>(handler),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
namespace std {
|
||||
template<class Handler, class... Args>
|
||||
void bind(beast::detail::bound_handler<
|
||||
Handler, Args...>, ...) = delete;
|
||||
} // std
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,506 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BUFFER_CAT_HPP
|
||||
#define BEAST_BUFFER_CAT_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
class buffer_cat_helper
|
||||
{
|
||||
std::tuple<Bs...> bs_;
|
||||
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
buffer_cat_helper(buffer_cat_helper&&) = default;
|
||||
buffer_cat_helper(buffer_cat_helper const&) = default;
|
||||
buffer_cat_helper& operator=(buffer_cat_helper&&) = default;
|
||||
buffer_cat_helper& operator=(buffer_cat_helper const&) = default;
|
||||
|
||||
explicit
|
||||
buffer_cat_helper(Bs const&... bs)
|
||||
: bs_(bs...)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
template<class U>
|
||||
std::size_t constexpr
|
||||
max_sizeof()
|
||||
{
|
||||
return sizeof(U);
|
||||
}
|
||||
|
||||
template<class U0, class U1, class... Us>
|
||||
std::size_t constexpr
|
||||
max_sizeof()
|
||||
{
|
||||
return
|
||||
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
|
||||
max_sizeof<U0>() : max_sizeof<U1, Us...>();
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
class buffer_cat_helper<
|
||||
ValueType, Bs...>::const_iterator
|
||||
{
|
||||
std::size_t n_;
|
||||
std::tuple<Bs...> const* bs_;
|
||||
std::array<std::uint8_t,
|
||||
max_sizeof<typename Bs::const_iterator...>()> buf_;
|
||||
|
||||
friend class buffer_cat_helper<ValueType, Bs...>;
|
||||
|
||||
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<Bs...>>::type::const_iterator;
|
||||
|
||||
template<std::size_t I>
|
||||
iter_t<I>&
|
||||
iter()
|
||||
{
|
||||
return *reinterpret_cast<
|
||||
iter_t<I>*>(buf_.data());
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
iter_t<I> const&
|
||||
iter() const
|
||||
{
|
||||
return *reinterpret_cast<
|
||||
iter_t<I> const*>(buf_.data());
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
~const_iterator();
|
||||
const_iterator();
|
||||
const_iterator(const_iterator&& other);
|
||||
const_iterator(const_iterator const& other);
|
||||
const_iterator& operator=(const_iterator&& other);
|
||||
const_iterator& operator=(const_iterator const& other);
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const;
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const;
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++();
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--();
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
const_iterator(
|
||||
std::tuple<Bs...> const& bs, bool at_end);
|
||||
|
||||
void
|
||||
construct(C<sizeof...(Bs)>)
|
||||
{
|
||||
auto constexpr I = sizeof...(Bs);
|
||||
n_ = I;
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
construct(C<I>)
|
||||
{
|
||||
if(std::get<I>(*bs_).begin() !=
|
||||
std::get<I>(*bs_).end())
|
||||
{
|
||||
n_ = I;
|
||||
new(buf_.data()) iter_t<I>{
|
||||
std::get<I>(*bs_).begin()};
|
||||
return;
|
||||
}
|
||||
construct(C<I+1>{});
|
||||
}
|
||||
|
||||
void
|
||||
destroy(C<sizeof...(Bs)>)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
destroy(C<I>)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
using Iter = iter_t<I>;
|
||||
iter<I>().~Iter();
|
||||
return;
|
||||
}
|
||||
destroy(C<I+1>{});
|
||||
}
|
||||
|
||||
void
|
||||
move(C<sizeof...(Bs)>, const_iterator&&)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
move(C<I>, const_iterator&& other)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
new(buf_.data()) iter_t<I>{
|
||||
std::move(other.iter<I>())};
|
||||
return;
|
||||
}
|
||||
move(C<I+1>{}, std::move(other));
|
||||
}
|
||||
|
||||
void
|
||||
copy(C<sizeof...(Bs)>, const_iterator const&)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
copy(C<I>, const_iterator const& other)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
new(buf_.data()) iter_t<I>{
|
||||
other.iter<I>()};
|
||||
return;
|
||||
}
|
||||
copy(C<I+1>{}, other);
|
||||
}
|
||||
|
||||
bool
|
||||
equal(C<sizeof...(Bs)>,
|
||||
const_iterator const&) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
bool
|
||||
equal(C<I>, const_iterator const& other) const
|
||||
{
|
||||
if(n_ == I)
|
||||
return iter<I>() == other.iter<I>();
|
||||
return equal(C<I+1>{}, other);
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
reference
|
||||
dereference(C<sizeof...(Bs)>) const
|
||||
{
|
||||
throw std::logic_error("invalid iterator");
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
reference
|
||||
dereference(C<I>) const
|
||||
{
|
||||
if(n_ == I)
|
||||
return *iter<I>();
|
||||
return dereference(C<I+1>{});
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void
|
||||
increment(C<sizeof...(Bs)>)
|
||||
{
|
||||
throw std::logic_error("invalid iterator");
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
increment(C<I>)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
if(++iter<I>() !=
|
||||
std::get<I>(*bs_).end())
|
||||
return;
|
||||
using Iter = iter_t<I>;
|
||||
iter<I>().~Iter();
|
||||
return construct(C<I+1>{});
|
||||
}
|
||||
increment(C<I+1>{});
|
||||
}
|
||||
|
||||
void
|
||||
decrement(C<sizeof...(Bs)>)
|
||||
{
|
||||
auto constexpr I = sizeof...(Bs);
|
||||
if(n_ == I)
|
||||
{
|
||||
--n_;
|
||||
new(buf_.data()) iter_t<I-1>{
|
||||
std::get<I-1>(*bs_).end()};
|
||||
}
|
||||
decrement(C<I-1>{});
|
||||
}
|
||||
|
||||
void
|
||||
decrement(C<0>)
|
||||
{
|
||||
auto constexpr I = 0;
|
||||
if(iter<I>() != std::get<I>(*bs_).begin())
|
||||
{
|
||||
--iter<I>();
|
||||
return;
|
||||
}
|
||||
throw std::logic_error("invalid iterator");
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
decrement(C<I>)
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
if(iter<I>() != std::get<I>(*bs_).begin())
|
||||
{
|
||||
--iter<I>();
|
||||
return;
|
||||
}
|
||||
--n_;
|
||||
using Iter = iter_t<I>;
|
||||
iter<I>().~Iter();
|
||||
new(buf_.data()) iter_t<I-1>{
|
||||
std::get<I-1>(*bs_).end()};
|
||||
}
|
||||
decrement(C<I-1>{});
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::~const_iterator()
|
||||
{
|
||||
destroy(C<0>{});
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::const_iterator()
|
||||
: n_(sizeof...(Bs))
|
||||
, bs_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::const_iterator(
|
||||
std::tuple<Bs...> const& bs, bool at_end)
|
||||
: bs_(&bs)
|
||||
{
|
||||
if(at_end)
|
||||
n_ = sizeof...(Bs);
|
||||
else
|
||||
construct(C<0>{});
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::const_iterator(const_iterator&& other)
|
||||
: n_(other.n_)
|
||||
, bs_(other.bs_)
|
||||
{
|
||||
move(C<0>{}, std::move(other));
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::const_iterator(const_iterator const& other)
|
||||
: n_(other.n_)
|
||||
, bs_(other.bs_)
|
||||
{
|
||||
copy(C<0>{}, other);
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
auto
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::operator=(const_iterator&& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
destroy(C<0>{});
|
||||
n_ = other.n_;
|
||||
bs_ = other.bs_;
|
||||
move(C<0>{}, std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
auto
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::operator=(const_iterator const& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
destroy(C<0>{});
|
||||
n_ = other.n_;
|
||||
bs_ = other.bs_;
|
||||
copy(C<0>{}, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
bool
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::operator==(const_iterator const& other) const
|
||||
{
|
||||
if(bs_ != other.bs_)
|
||||
return false;
|
||||
if(n_ != other.n_)
|
||||
return false;
|
||||
return equal(C<0>{}, other);
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
auto
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::operator*() const ->
|
||||
reference
|
||||
{
|
||||
return dereference(C<0>{});
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
auto
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::operator++() ->
|
||||
const_iterator&
|
||||
{
|
||||
increment(C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
auto
|
||||
buffer_cat_helper<ValueType, Bs...>::
|
||||
const_iterator::operator--() ->
|
||||
const_iterator&
|
||||
{
|
||||
decrement(C<sizeof...(Bs)>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
auto
|
||||
buffer_cat_helper<ValueType, Bs...>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator(bs_, false);
|
||||
}
|
||||
|
||||
template<class ValueType, class... Bs>
|
||||
auto
|
||||
buffer_cat_helper<ValueType, Bs...>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator(bs_, true);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`.
|
||||
|
||||
This function returns a `ConstBufferSequence` that 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 `ConstBufferSequence` that represents the concatenation
|
||||
of the input buffer sequences.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... BufferSequence>
|
||||
implementation_defined
|
||||
buffer_cat(BufferSequence const&... buffers)
|
||||
#else
|
||||
template<class B1, class B2, class... Bn>
|
||||
detail::buffer_cat_helper<
|
||||
boost::asio::const_buffer, B1, B2, Bn...>
|
||||
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
|
||||
#endif
|
||||
{
|
||||
return detail::buffer_cat_helper<
|
||||
boost::asio::const_buffer,
|
||||
B1, B2, Bn...>(b1, b2, bn...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BUFFERS_ADAPTER_HPP
|
||||
#define BEAST_BUFFERS_ADAPTER_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Adapts a `MutableBufferSequence` into a `Streambuf`.
|
||||
|
||||
This class wraps a `MutableBufferSequence` to meet the requirements
|
||||
of `Streambuf`. 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 Buffers The type of mutable buffer sequence to wrap.
|
||||
*/
|
||||
template<class Buffers>
|
||||
class buffers_adapter
|
||||
{
|
||||
private:
|
||||
using buffers_type = typename std::decay<Buffers>::type;
|
||||
using iter_type = typename buffers_type::const_iterator;
|
||||
|
||||
static auto constexpr is_mutable =
|
||||
std::is_constructible<boost::asio::mutable_buffer,
|
||||
typename std::iterator_traits<iter_type>::value_type>::value;
|
||||
|
||||
Buffers bs_;
|
||||
iter_type begin_;
|
||||
iter_type out_;
|
||||
iter_type end_;
|
||||
std::size_t max_size_;
|
||||
std::size_t in_pos_ = 0; // offset in *begin_
|
||||
std::size_t in_size_ = 0; // size of input sequence
|
||||
std::size_t out_pos_ = 0; // offset in *out_
|
||||
std::size_t out_end_ = 0; // output end offset
|
||||
|
||||
template<class Deduced>
|
||||
buffers_adapter(Deduced&& other,
|
||||
std::size_t nbegin, std::size_t nout,
|
||||
std::size_t nend)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, begin_(std::next(bs_.begin(), nbegin))
|
||||
, out_(std::next(bs_.begin(), nout))
|
||||
, end_(std::next(bs_.begin(), nend))
|
||||
, max_size_(other.max_size_)
|
||||
, in_pos_(other.in_pos_)
|
||||
, in_size_(other.in_size_)
|
||||
, out_pos_(other.out_pos_)
|
||||
, out_end_(other.out_end_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_buffers_type;
|
||||
|
||||
class mutable_buffers_type;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
buffers_adapter(buffers_adapter&& other);
|
||||
|
||||
/// Copy constructor.
|
||||
buffers_adapter(buffers_adapter const& other);
|
||||
|
||||
/// Move assignment.
|
||||
buffers_adapter& operator=(buffers_adapter&& other);
|
||||
|
||||
/// Copy assignment.
|
||||
buffers_adapter& operator=(buffers_adapter const&);
|
||||
|
||||
/** Construct a buffers adapter.
|
||||
|
||||
@param buffers The mutable buffer sequence to wrap. A copy of
|
||||
the object will be made, but ownership of the memory is not
|
||||
transferred.
|
||||
*/
|
||||
explicit
|
||||
buffers_adapter(Buffers const& buffers);
|
||||
|
||||
/// Returns the largest size output sequence possible.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return max_size_;
|
||||
}
|
||||
|
||||
/// Get the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return in_size_;
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represents the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if the size would exceed the limit
|
||||
imposed by the underlying mutable buffer sequence.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/// Move bytes from the output sequence to the input sequence.
|
||||
void
|
||||
commit(std::size_t n);
|
||||
|
||||
/// Get a list of buffers that represents the input sequence.
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(std::size_t n);
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/impl/buffers_adapter.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BUFFERS_DEBUG_HPP
|
||||
#define BEAST_BUFFERS_DEBUG_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace debug {
|
||||
|
||||
/** Diagnostic utility to convert a `ConstBufferSequence` to a string.
|
||||
|
||||
@note Carriage returns and linefeeds will have additional escape
|
||||
representations printed for visibility.
|
||||
*/
|
||||
template<class Buffers>
|
||||
std::string
|
||||
buffers_to_string(Buffers const& bs)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
std::string s;
|
||||
s.reserve(buffer_size(bs));
|
||||
for(auto const& b : bs)
|
||||
s.append(buffer_cast<char const*>(b),
|
||||
buffer_size(b));
|
||||
for(auto i = s.size(); i-- > 0;)
|
||||
if(s[i] == '\r')
|
||||
s.replace(i, 1, "\\r");
|
||||
else if(s[i] == '\n')
|
||||
s.replace(i, 1, "\\n\n");
|
||||
return s;
|
||||
}
|
||||
|
||||
} // debug
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CONSUMING_BUFFERS_HPP
|
||||
#define BEAST_CONSUMING_BUFFERS_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Adapter to trim the front of a `BufferSequence`.
|
||||
|
||||
This adapter wraps a buffer sequence to create a new sequence
|
||||
which may be incrementally consumed. Bytes consumed are removed
|
||||
from the front of the buffer. The underlying memory is not changed,
|
||||
instead the adapter efficiently iterates through a subset of
|
||||
the buffers wrapped.
|
||||
|
||||
The wrapped buffer is not modified, a copy is made instead.
|
||||
Ownership of the underlying memory is not transferred, the application
|
||||
is still responsible for managing its lifetime.
|
||||
|
||||
@tparam Buffers The buffer sequence to wrap.
|
||||
|
||||
@ptaram ValueType The type of buffer of the final buffer sequence. This
|
||||
can be different from the buffer type of the wrapped sequence. For
|
||||
example, a `MutableBufferSequence` can be transformed into a
|
||||
consumable `ConstBufferSequence`. Violations of buffer const safety
|
||||
are not permitted, and will result in a compile error.
|
||||
*/
|
||||
template<class Buffers,
|
||||
class ValueType = typename Buffers::value_type>
|
||||
class consuming_buffers
|
||||
{
|
||||
using iter_type =
|
||||
typename Buffers::const_iterator;
|
||||
|
||||
static_assert(std::is_constructible<ValueType,
|
||||
typename std::iterator_traits<iter_type>::value_type>::value,
|
||||
"ValueType requirements not met");
|
||||
|
||||
Buffers bs_;
|
||||
iter_type begin_;
|
||||
std::size_t skip_ = 0;
|
||||
|
||||
template<class Deduced>
|
||||
consuming_buffers(Deduced&& other, std::size_t nbegin)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, begin_(std::next(bs_.begin(), nbegin))
|
||||
, skip_(other.skip_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/// The type for each element in the list of buffers.
|
||||
using value_type = ValueType;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_iterator;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
consuming_buffers(consuming_buffers&&);
|
||||
|
||||
/// Copy constructor.
|
||||
consuming_buffers(consuming_buffers const&);
|
||||
|
||||
/// Move assignment.
|
||||
consuming_buffers& operator=(consuming_buffers&&);
|
||||
|
||||
/// Copy assignment.
|
||||
consuming_buffers& operator=(consuming_buffers const&);
|
||||
|
||||
/** Construct to represent a buffer sequence.
|
||||
|
||||
A copy of the buffer sequence is made. Ownership of the
|
||||
underlying memory is not transferred or copied.
|
||||
*/
|
||||
explicit
|
||||
consuming_buffers(Buffers const& buffers);
|
||||
|
||||
/// Get a bidirectional iterator to the first element.
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Get a bidirectional iterator for one past the last element.
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
/** Remove bytes from the beginning of the sequence.
|
||||
|
||||
@param n The number of bytes to remove. If this is
|
||||
larger than the number of bytes remaining, all the
|
||||
bytes remaining are removed.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n);
|
||||
};
|
||||
|
||||
/// Returns a consumed buffer
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers, typename Buffers::value_type>
|
||||
consumed_buffers(Buffers const& bs, std::size_t n);
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/impl/consuming_buffers.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,178 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_BASE64_HPP
|
||||
#define BEAST_DETAIL_BASE64_HPP
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
|
||||
Copyright notice:
|
||||
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 René 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
|
||||
|
||||
*/
|
||||
|
||||
template <class = void>
|
||||
std::string const&
|
||||
base64_alphabet()
|
||||
{
|
||||
static std::string const alphabet =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
return alphabet;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
is_base64(unsigned char c)
|
||||
{
|
||||
return (std::isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
std::string
|
||||
base64_encode (std::uint8_t const* data,
|
||||
std::size_t in_len)
|
||||
{
|
||||
unsigned char c3[3], c4[4];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
std::string ret;
|
||||
ret.reserve (3 + in_len * 8 / 6);
|
||||
|
||||
char const* alphabet (base64_alphabet().data());
|
||||
|
||||
while(in_len--)
|
||||
{
|
||||
c3[i++] = *(data++);
|
||||
if(i == 3)
|
||||
{
|
||||
c4[0] = (c3[0] & 0xfc) >> 2;
|
||||
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
|
||||
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
|
||||
c4[3] = c3[2] & 0x3f;
|
||||
for(i = 0; (i < 4); i++)
|
||||
ret += alphabet[c4[i]];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
c3[j] = '\0';
|
||||
|
||||
c4[0] = (c3[0] & 0xfc) >> 2;
|
||||
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
|
||||
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
|
||||
c4[3] = c3[2] & 0x3f;
|
||||
|
||||
for(j = 0; (j < i + 1); j++)
|
||||
ret += alphabet[c4[j]];
|
||||
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
std::string
|
||||
base64_encode (std::string const& s)
|
||||
{
|
||||
return base64_encode (reinterpret_cast <
|
||||
std::uint8_t const*> (s.data()), s.size());
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
std::string
|
||||
base64_decode(std::string const& data)
|
||||
{
|
||||
int in_len = data.size();
|
||||
unsigned char c3[3], c4[4];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
|
||||
std::string ret;
|
||||
ret.reserve (in_len * 6 / 8); // ???
|
||||
|
||||
while(in_len-- && (data[in_] != '=') &&
|
||||
is_base64(data[in_]))
|
||||
{
|
||||
c4[i++] = data[in_]; in_++;
|
||||
if(i == 4) {
|
||||
for(i = 0; i < 4; i++)
|
||||
c4[i] = static_cast<unsigned char>(
|
||||
base64_alphabet().find(c4[i]));
|
||||
|
||||
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
|
||||
|
||||
for(i = 0; (i < 3); i++)
|
||||
ret += c3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
for(j = i; j < 4; j++)
|
||||
c4[j] = 0;
|
||||
|
||||
for(j = 0; j < 4; j++)
|
||||
c4[j] = static_cast<unsigned char>(
|
||||
base64_alphabet().find(c4[j]));
|
||||
|
||||
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
|
||||
|
||||
for(j = 0; (j < i - 1); j++)
|
||||
ret += c3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
||||
#define BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <cctype>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** Case-insensitive function object for performing less than comparisons. */
|
||||
struct ci_less
|
||||
{
|
||||
static bool const is_transparent = true;
|
||||
|
||||
bool
|
||||
operator()(boost::string_ref const& lhs,
|
||||
boost::string_ref const& rhs) const noexcept
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
return std::lexicographical_compare(
|
||||
begin(lhs), end(lhs), begin(rhs), end(rhs),
|
||||
[](char lhs, char rhs)
|
||||
{
|
||||
return std::tolower(lhs) < std::tolower(rhs);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
bool
|
||||
ci_equal(std::pair<const char*, std::size_t> lhs,
|
||||
std::pair<const char*, std::size_t> rhs)
|
||||
{
|
||||
if(lhs.second != rhs.second)
|
||||
return false;
|
||||
return std::equal (lhs.first, lhs.first + lhs.second,
|
||||
rhs.first,
|
||||
[] (char lhs, char rhs)
|
||||
{
|
||||
return std::tolower(lhs) == std::tolower(rhs);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
inline
|
||||
std::pair<const char*, std::size_t>
|
||||
view(const char (&s)[N])
|
||||
{
|
||||
return {s, N-1};
|
||||
}
|
||||
|
||||
inline
|
||||
std::pair<const char*, std::size_t>
|
||||
view(std::string const& s)
|
||||
{
|
||||
return {s.data(), s.size()};
|
||||
}
|
||||
|
||||
/** Returns `true` if strings are case-insensitive equal. */
|
||||
template <class String1, class String2>
|
||||
inline
|
||||
bool
|
||||
ci_equal(String1 const& lhs, String2 const& rhs)
|
||||
{
|
||||
return ci_equal(view(lhs), view(rhs));
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_CONST_CONTAINER_HPP
|
||||
#define BEAST_DETAIL_CONST_CONTAINER_HPP
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** Adapter to constrain a container interface.
|
||||
The interface allows for limited read only operations. Derived classes
|
||||
provide additional behavior.
|
||||
*/
|
||||
template <class Container>
|
||||
class const_container
|
||||
{
|
||||
private:
|
||||
using cont_type = Container;
|
||||
|
||||
cont_type m_cont;
|
||||
|
||||
protected:
|
||||
cont_type& cont()
|
||||
{
|
||||
return m_cont;
|
||||
}
|
||||
|
||||
cont_type const& cont() const
|
||||
{
|
||||
return m_cont;
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type = typename cont_type::value_type;
|
||||
using size_type = typename cont_type::size_type;
|
||||
using difference_type = typename cont_type::difference_type;
|
||||
using iterator = typename cont_type::const_iterator;
|
||||
using const_iterator = typename cont_type::const_iterator;
|
||||
|
||||
/** Returns `true` if the container is empty. */
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return m_cont.empty();
|
||||
}
|
||||
|
||||
/** Returns the number of items in the container. */
|
||||
size_type
|
||||
size() const
|
||||
{
|
||||
return m_cont.size();
|
||||
}
|
||||
|
||||
/** Returns forward iterators for traversal. */
|
||||
/** @{ */
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return m_cont.cbegin();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return m_cont.cbegin();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return m_cont.cend();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return m_cont.cend();
|
||||
}
|
||||
/** @} */
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
|
||||
#define BEAST_DETAIL_EMPTY_BASE_OPTIMIZATION_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
struct empty_base_optimization_decide
|
||||
: std::integral_constant <bool,
|
||||
std::is_empty <T>::value
|
||||
#ifdef __clang__
|
||||
&& !__is_final(T)
|
||||
#endif
|
||||
>
|
||||
{
|
||||
};
|
||||
|
||||
template <
|
||||
class T,
|
||||
int UniqueID = 0,
|
||||
bool ShouldDeriveFrom =
|
||||
empty_base_optimization_decide<T>::value
|
||||
>
|
||||
class empty_base_optimization : private T
|
||||
{
|
||||
public:
|
||||
empty_base_optimization() = default;
|
||||
|
||||
empty_base_optimization(T const& t)
|
||||
: T (t)
|
||||
{}
|
||||
|
||||
empty_base_optimization(T&& t)
|
||||
: T (std::move (t))
|
||||
{}
|
||||
|
||||
T& member() noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
T const& member() const noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
class T,
|
||||
int UniqueID
|
||||
>
|
||||
class empty_base_optimization <T, UniqueID, false>
|
||||
{
|
||||
public:
|
||||
empty_base_optimization() = default;
|
||||
|
||||
empty_base_optimization(T const& t)
|
||||
: m_t (t)
|
||||
{}
|
||||
|
||||
empty_base_optimization(T&& t)
|
||||
: m_t (std::move (t))
|
||||
{}
|
||||
|
||||
T& member() noexcept
|
||||
{
|
||||
return m_t;
|
||||
}
|
||||
|
||||
T const& member() const noexcept
|
||||
{
|
||||
return m_t;
|
||||
}
|
||||
|
||||
private:
|
||||
T m_t;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
|
||||
#define BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class T, T... Ints>
|
||||
struct integer_sequence
|
||||
{
|
||||
using value_type = T;
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"std::integer_sequence can only be instantiated with an integral type" );
|
||||
|
||||
static std::size_t constexpr static_size = sizeof...(Ints);
|
||||
|
||||
static std::size_t constexpr size()
|
||||
{
|
||||
return sizeof...(Ints);
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t... Ints>
|
||||
using index_sequence = integer_sequence<std::size_t, Ints...>;
|
||||
|
||||
// This workaround is needed for broken sizeof...
|
||||
template<class... Args>
|
||||
struct sizeof_workaround
|
||||
{
|
||||
static std::size_t constexpr size = sizeof... (Args);
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
// This implementation compiles on MSVC and clang but not gcc
|
||||
|
||||
template<class T, unsigned long long N, class Seq>
|
||||
struct make_integer_sequence_unchecked;
|
||||
|
||||
template<class T, unsigned long long N, unsigned long long ...Indices>
|
||||
struct make_integer_sequence_unchecked<
|
||||
T, N, integer_sequence<T, Indices...>>
|
||||
{
|
||||
using type = typename make_integer_sequence_unchecked<
|
||||
T, N-1, integer_sequence<T, N-1, Indices...>>::type;
|
||||
};
|
||||
|
||||
template<class T, unsigned long long ...Indices>
|
||||
struct make_integer_sequence_unchecked<
|
||||
T, 0, integer_sequence<T, Indices...>>
|
||||
{
|
||||
using type = integer_sequence<T, Indices...>;
|
||||
};
|
||||
|
||||
template<class T, T N>
|
||||
struct make_integer_sequence_checked
|
||||
{
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"T must be an integral type");
|
||||
|
||||
static_assert (N >= 0,
|
||||
"N must be non-negative");
|
||||
|
||||
using type = typename make_integer_sequence_unchecked<
|
||||
T, N, integer_sequence<T>>::type;
|
||||
};
|
||||
|
||||
template<class T, T N>
|
||||
using make_integer_sequence =
|
||||
typename make_integer_sequence_checked<T, N>::type;
|
||||
|
||||
template<std::size_t N>
|
||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||
|
||||
template<class... Args>
|
||||
using index_sequence_for =
|
||||
make_index_sequence<sizeof_workaround<Args...>::size>;
|
||||
|
||||
#else
|
||||
|
||||
// This implementation compiles on gcc but not MSVC
|
||||
|
||||
template<std::size_t... Ints>
|
||||
struct index_tuple
|
||||
{
|
||||
using next = index_tuple<Ints..., sizeof... (Ints)>;
|
||||
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
struct build_index_tuple
|
||||
{
|
||||
using type = typename build_index_tuple<N-1>::type::next;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct build_index_tuple<0>
|
||||
{
|
||||
using type = index_tuple<>;
|
||||
};
|
||||
|
||||
template<class T, T N,
|
||||
class Seq = typename build_index_tuple<N>::type
|
||||
>
|
||||
struct integer_sequence_helper;
|
||||
|
||||
template<class T, T N, std::size_t... Ints>
|
||||
struct integer_sequence_helper<T, N, index_tuple<Ints...>>
|
||||
{
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"T must be an integral type");
|
||||
|
||||
static_assert (N >= 0,
|
||||
"N must be non-negative");
|
||||
|
||||
using type = integer_sequence<T, static_cast<T> (Ints)...>;
|
||||
};
|
||||
|
||||
template<class T, T N>
|
||||
using make_integer_sequence =
|
||||
typename integer_sequence_helper<T, N>::type;
|
||||
|
||||
template<std::size_t N>
|
||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||
|
||||
template<class... Args>
|
||||
using index_sequence_for =
|
||||
make_index_sequence<sizeof_workaround<Args...>::size>;
|
||||
|
||||
#endif
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
|
||||
#define BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template <class R, class C, class ...A>
|
||||
auto
|
||||
is_call_possible_test(C&& c, int, A&& ...a)
|
||||
-> decltype(std::is_convertible<
|
||||
decltype(c(a...)), R>::value ||
|
||||
std::is_same<R, void>::value,
|
||||
std::true_type());
|
||||
|
||||
template <class R, class C, class ...A>
|
||||
std::false_type
|
||||
is_call_possible_test(C&& c, long, A&& ...a);
|
||||
|
||||
/** Metafunction returns `true` if F callable as R(A...)
|
||||
Example:
|
||||
is_call_possible<T, void(std::string)>
|
||||
*/
|
||||
/** @{ */
|
||||
template <class C, class F>
|
||||
struct is_call_possible
|
||||
: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class C, class R, class ...A>
|
||||
struct is_call_possible<C, R(A...)>
|
||||
: decltype(is_call_possible_test<R>(
|
||||
std::declval<C>(), 1, std::declval<A>()...))
|
||||
{
|
||||
};
|
||||
/** @} */
|
||||
|
||||
namespace test {
|
||||
|
||||
struct is_call_possible_udt1
|
||||
{
|
||||
void operator()(int) const;
|
||||
};
|
||||
|
||||
struct is_call_possible_udt2
|
||||
{
|
||||
int operator()(int) const;
|
||||
};
|
||||
|
||||
struct is_call_possible_udt3
|
||||
{
|
||||
int operator()(int);
|
||||
};
|
||||
|
||||
static_assert(is_call_possible<
|
||||
is_call_possible_udt1, void(int)>::value, "");
|
||||
|
||||
static_assert(! is_call_possible<
|
||||
is_call_possible_udt1, void(void)>::value, "");
|
||||
|
||||
static_assert(is_call_possible<
|
||||
is_call_possible_udt2, int(int)>::value, "");
|
||||
|
||||
static_assert(! is_call_possible<
|
||||
is_call_possible_udt2, int(void)>::value, "");
|
||||
|
||||
static_assert(! is_call_possible<
|
||||
is_call_possible_udt2, void(void)>::value, "");
|
||||
|
||||
static_assert(is_call_possible<
|
||||
is_call_possible_udt3, int(int)>::value, "");
|
||||
|
||||
static_assert(! is_call_possible<
|
||||
is_call_possible_udt3 const, int(int)>::value, "");
|
||||
|
||||
} // test
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,310 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_SHA1_HPP
|
||||
#define BEAST_DETAIL_SHA1_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
// Based on https://github.com/vog/sha1
|
||||
/*
|
||||
Original authors:
|
||||
Steve Reid (Original C Code)
|
||||
Bruce Guenter (Small changes to fit into bglibs)
|
||||
Volker Grabsch (Translation to simpler C++ Code)
|
||||
Eugene Hopkinson (Safety improvements)
|
||||
Vincent Falco (beast adaptation)
|
||||
*/
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
namespace sha1 {
|
||||
|
||||
static std::size_t constexpr BLOCK_INTS = 16;
|
||||
static std::size_t constexpr BLOCK_BYTES = 64;
|
||||
static std::size_t constexpr DIGEST_BYTES = 20;
|
||||
|
||||
inline
|
||||
std::uint32_t
|
||||
rol(std::uint32_t value, std::size_t bits)
|
||||
{
|
||||
return (value << bits) | (value >> (32 - bits));
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint32_t
|
||||
blk(std::uint32_t block[BLOCK_INTS], std::size_t i)
|
||||
{
|
||||
return rol(
|
||||
block[(i+13)&15] ^ block[(i+8)&15] ^
|
||||
block[(i+2)&15] ^ block[i], 1);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
R0(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void
|
||||
R1(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
block[i] = blk(block, i);
|
||||
z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
R2(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
block[i] = blk(block, i);
|
||||
z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
R3(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
block[i] = blk(block, i);
|
||||
z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
R4(std::uint32_t block[BLOCK_INTS], std::uint32_t v,
|
||||
std::uint32_t &w, std::uint32_t x, std::uint32_t y,
|
||||
std::uint32_t &z, std::size_t i)
|
||||
{
|
||||
block[i] = blk(block, i);
|
||||
z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
make_block(std::uint8_t const* p,
|
||||
std::uint32_t block[BLOCK_INTS])
|
||||
{
|
||||
for(std::size_t i = 0; i < BLOCK_INTS; i++)
|
||||
block[i] =
|
||||
(static_cast<std::uint32_t>(p[4*i+3])) |
|
||||
(static_cast<std::uint32_t>(p[4*i+2]))<< 8 |
|
||||
(static_cast<std::uint32_t>(p[4*i+1]))<<16 |
|
||||
(static_cast<std::uint32_t>(p[4*i+0]))<<24;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
transform(
|
||||
std::uint32_t digest[], std::uint32_t block[BLOCK_INTS])
|
||||
{
|
||||
std::uint32_t a = digest[0];
|
||||
std::uint32_t b = digest[1];
|
||||
std::uint32_t c = digest[2];
|
||||
std::uint32_t d = digest[3];
|
||||
std::uint32_t e = digest[4];
|
||||
|
||||
R0(block, a, b, c, d, e, 0);
|
||||
R0(block, e, a, b, c, d, 1);
|
||||
R0(block, d, e, a, b, c, 2);
|
||||
R0(block, c, d, e, a, b, 3);
|
||||
R0(block, b, c, d, e, a, 4);
|
||||
R0(block, a, b, c, d, e, 5);
|
||||
R0(block, e, a, b, c, d, 6);
|
||||
R0(block, d, e, a, b, c, 7);
|
||||
R0(block, c, d, e, a, b, 8);
|
||||
R0(block, b, c, d, e, a, 9);
|
||||
R0(block, a, b, c, d, e, 10);
|
||||
R0(block, e, a, b, c, d, 11);
|
||||
R0(block, d, e, a, b, c, 12);
|
||||
R0(block, c, d, e, a, b, 13);
|
||||
R0(block, b, c, d, e, a, 14);
|
||||
R0(block, a, b, c, d, e, 15);
|
||||
R1(block, e, a, b, c, d, 0);
|
||||
R1(block, d, e, a, b, c, 1);
|
||||
R1(block, c, d, e, a, b, 2);
|
||||
R1(block, b, c, d, e, a, 3);
|
||||
R2(block, a, b, c, d, e, 4);
|
||||
R2(block, e, a, b, c, d, 5);
|
||||
R2(block, d, e, a, b, c, 6);
|
||||
R2(block, c, d, e, a, b, 7);
|
||||
R2(block, b, c, d, e, a, 8);
|
||||
R2(block, a, b, c, d, e, 9);
|
||||
R2(block, e, a, b, c, d, 10);
|
||||
R2(block, d, e, a, b, c, 11);
|
||||
R2(block, c, d, e, a, b, 12);
|
||||
R2(block, b, c, d, e, a, 13);
|
||||
R2(block, a, b, c, d, e, 14);
|
||||
R2(block, e, a, b, c, d, 15);
|
||||
R2(block, d, e, a, b, c, 0);
|
||||
R2(block, c, d, e, a, b, 1);
|
||||
R2(block, b, c, d, e, a, 2);
|
||||
R2(block, a, b, c, d, e, 3);
|
||||
R2(block, e, a, b, c, d, 4);
|
||||
R2(block, d, e, a, b, c, 5);
|
||||
R2(block, c, d, e, a, b, 6);
|
||||
R2(block, b, c, d, e, a, 7);
|
||||
R3(block, a, b, c, d, e, 8);
|
||||
R3(block, e, a, b, c, d, 9);
|
||||
R3(block, d, e, a, b, c, 10);
|
||||
R3(block, c, d, e, a, b, 11);
|
||||
R3(block, b, c, d, e, a, 12);
|
||||
R3(block, a, b, c, d, e, 13);
|
||||
R3(block, e, a, b, c, d, 14);
|
||||
R3(block, d, e, a, b, c, 15);
|
||||
R3(block, c, d, e, a, b, 0);
|
||||
R3(block, b, c, d, e, a, 1);
|
||||
R3(block, a, b, c, d, e, 2);
|
||||
R3(block, e, a, b, c, d, 3);
|
||||
R3(block, d, e, a, b, c, 4);
|
||||
R3(block, c, d, e, a, b, 5);
|
||||
R3(block, b, c, d, e, a, 6);
|
||||
R3(block, a, b, c, d, e, 7);
|
||||
R3(block, e, a, b, c, d, 8);
|
||||
R3(block, d, e, a, b, c, 9);
|
||||
R3(block, c, d, e, a, b, 10);
|
||||
R3(block, b, c, d, e, a, 11);
|
||||
R4(block, a, b, c, d, e, 12);
|
||||
R4(block, e, a, b, c, d, 13);
|
||||
R4(block, d, e, a, b, c, 14);
|
||||
R4(block, c, d, e, a, b, 15);
|
||||
R4(block, b, c, d, e, a, 0);
|
||||
R4(block, a, b, c, d, e, 1);
|
||||
R4(block, e, a, b, c, d, 2);
|
||||
R4(block, d, e, a, b, c, 3);
|
||||
R4(block, c, d, e, a, b, 4);
|
||||
R4(block, b, c, d, e, a, 5);
|
||||
R4(block, a, b, c, d, e, 6);
|
||||
R4(block, e, a, b, c, d, 7);
|
||||
R4(block, d, e, a, b, c, 8);
|
||||
R4(block, c, d, e, a, b, 9);
|
||||
R4(block, b, c, d, e, a, 10);
|
||||
R4(block, a, b, c, d, e, 11);
|
||||
R4(block, e, a, b, c, d, 12);
|
||||
R4(block, d, e, a, b, c, 13);
|
||||
R4(block, c, d, e, a, b, 14);
|
||||
R4(block, b, c, d, e, a, 15);
|
||||
|
||||
digest[0] += a;
|
||||
digest[1] += b;
|
||||
digest[2] += c;
|
||||
digest[3] += d;
|
||||
digest[4] += e;
|
||||
}
|
||||
|
||||
} // sha1
|
||||
|
||||
struct sha1_context
|
||||
{
|
||||
static unsigned int const block_size = sha1::BLOCK_BYTES;
|
||||
static unsigned int const digest_size = 20;
|
||||
|
||||
std::uint32_t digest[5];
|
||||
std::uint8_t buf[block_size];
|
||||
std::size_t buflen;
|
||||
std::size_t blocks;
|
||||
};
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
init(sha1_context& ctx) noexcept
|
||||
{
|
||||
ctx.buflen = 0;
|
||||
ctx.digest[0] = 0x67452301;
|
||||
ctx.digest[1] = 0xefcdab89;
|
||||
ctx.digest[2] = 0x98badcfe;
|
||||
ctx.digest[3] = 0x10325476;
|
||||
ctx.digest[4] = 0xc3d2e1f0;
|
||||
ctx.blocks = 0;
|
||||
}
|
||||
|
||||
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
|
||||
auto const buflen = ctx.buflen;
|
||||
ctx.buf[ctx.buflen++] = 0x80;
|
||||
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)
|
||||
{
|
||||
++ctx.blocks;
|
||||
sha1::transform(ctx.digest, block);
|
||||
for (size_t i = 0; i < BLOCK_INTS - 2; i++)
|
||||
block[i] = 0;
|
||||
}
|
||||
|
||||
/* Append total_bits, split this uint64_t into two uint32_t */
|
||||
block[BLOCK_INTS - 1] = total_bits & 0xffffffff;
|
||||
block[BLOCK_INTS - 2] = (total_bits >> 32);
|
||||
sha1::transform(ctx.digest, block);
|
||||
for(std::size_t i = 0; i < sha1::DIGEST_BYTES/4; i++)
|
||||
{
|
||||
std::uint8_t* d =
|
||||
reinterpret_cast<std::uint8_t*>(digest) + 4 * i;
|
||||
d[3] = ctx.digest[i] & 0xff;
|
||||
d[2] = (ctx.digest[i] >> 8) & 0xff;
|
||||
d[1] = (ctx.digest[i] >> 16) & 0xff;
|
||||
d[0] = (ctx.digest[i] >> 24) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STREAM_ABSTRACT_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_STREAM_ABSTRACT_OSTREAM_HPP
|
||||
|
||||
#include <beast/detail/stream/basic_abstract_ostream.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** An abstract ostream for `char`. */
|
||||
using abstract_ostream = basic_abstract_ostream <char>;
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STREAM_BASIC_ABSTRACT_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_STREAM_BASIC_ABSTRACT_OSTREAM_HPP
|
||||
|
||||
#include <beast/detail/stream/basic_scoped_ostream.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** Abstraction for an output stream similar to std::basic_ostream. */
|
||||
template <
|
||||
class CharT,
|
||||
class Traits = std::char_traits <CharT>
|
||||
>
|
||||
class basic_abstract_ostream
|
||||
{
|
||||
public:
|
||||
using string_type = std::basic_string <CharT, Traits>;
|
||||
using scoped_stream_type = basic_scoped_ostream <CharT, Traits>;
|
||||
|
||||
basic_abstract_ostream() = default;
|
||||
|
||||
virtual
|
||||
~basic_abstract_ostream() = default;
|
||||
|
||||
basic_abstract_ostream (basic_abstract_ostream const&) = default;
|
||||
basic_abstract_ostream& operator= (
|
||||
basic_abstract_ostream const&) = default;
|
||||
|
||||
/** Returns `true` if the stream is active.
|
||||
Inactive streams do not produce output.
|
||||
*/
|
||||
/** @{ */
|
||||
virtual
|
||||
bool
|
||||
active() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
explicit
|
||||
operator bool() const
|
||||
{
|
||||
return active();
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Called to output each string. */
|
||||
virtual
|
||||
void
|
||||
write (string_type const& s) = 0;
|
||||
|
||||
scoped_stream_type
|
||||
operator<< (std::ostream& manip (std::ostream&))
|
||||
{
|
||||
return scoped_stream_type (manip,
|
||||
[this](string_type const& s)
|
||||
{
|
||||
this->write (s);
|
||||
});
|
||||
}
|
||||
|
||||
template <class T>
|
||||
scoped_stream_type
|
||||
operator<< (T const& t)
|
||||
{
|
||||
return scoped_stream_type (t,
|
||||
[this](string_type const& s)
|
||||
{
|
||||
this->write (s);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STREAM_BASIC_SCOPED_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_STREAM_BASIC_SCOPED_OSTREAM_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
// gcc libstd++ doesn't have move constructors for basic_ostringstream
|
||||
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316
|
||||
//
|
||||
#ifndef BEAST_NO_STDLIB_STREAM_MOVE
|
||||
# ifdef __GLIBCXX__
|
||||
# define BEAST_NO_STDLIB_STREAM_MOVE 1
|
||||
# else
|
||||
# define BEAST_NO_STDLIB_STREAM_MOVE 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template <
|
||||
class CharT,
|
||||
class Traits
|
||||
>
|
||||
class basic_abstract_ostream;
|
||||
|
||||
/** Scoped output stream that forwards to a functor upon destruction. */
|
||||
template <
|
||||
class CharT,
|
||||
class Traits = std::char_traits <CharT>,
|
||||
class Allocator = std::allocator <CharT>
|
||||
>
|
||||
class basic_scoped_ostream
|
||||
{
|
||||
private:
|
||||
using handler_t = std::function <void (
|
||||
std::basic_string <CharT, Traits, Allocator> const&)>;
|
||||
|
||||
using stream_type = std::basic_ostringstream <
|
||||
CharT, Traits, Allocator>;
|
||||
|
||||
handler_t m_handler;
|
||||
|
||||
#if BEAST_NO_STDLIB_STREAM_MOVE
|
||||
std::unique_ptr <stream_type> m_ss;
|
||||
|
||||
stream_type& stream()
|
||||
{
|
||||
return *m_ss;
|
||||
}
|
||||
|
||||
#else
|
||||
stream_type m_ss;
|
||||
|
||||
stream_type& stream()
|
||||
{
|
||||
return m_ss;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public:
|
||||
using string_type = std::basic_string <CharT, Traits>;
|
||||
|
||||
// Disallow copy since that would duplicate the output
|
||||
basic_scoped_ostream (basic_scoped_ostream const&) = delete;
|
||||
basic_scoped_ostream& operator= (basic_scoped_ostream const) = delete;
|
||||
|
||||
template <class Handler>
|
||||
explicit basic_scoped_ostream (Handler&& handler)
|
||||
: m_handler (std::forward <Handler> (handler))
|
||||
#if BEAST_NO_STDLIB_STREAM_MOVE
|
||||
, m_ss (new stream_type())
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
template <class T, class Handler>
|
||||
basic_scoped_ostream (T const& t, Handler&& handler)
|
||||
: m_handler (std::forward <Handler> (handler))
|
||||
#if BEAST_NO_STDLIB_STREAM_MOVE
|
||||
, m_ss (new stream_type())
|
||||
#endif
|
||||
{
|
||||
stream() << t;
|
||||
}
|
||||
|
||||
basic_scoped_ostream (basic_abstract_ostream <
|
||||
CharT, Traits>& ostream)
|
||||
: m_handler (
|
||||
[&](string_type const& s)
|
||||
{
|
||||
ostream.write (s);
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
basic_scoped_ostream (basic_scoped_ostream&& other)
|
||||
: m_handler (std::move (other.m_handler))
|
||||
, m_ss (std::move (other.m_ss))
|
||||
{
|
||||
}
|
||||
|
||||
~basic_scoped_ostream()
|
||||
{
|
||||
auto const& s (stream().str());
|
||||
if (! s.empty())
|
||||
m_handler (s);
|
||||
}
|
||||
|
||||
basic_scoped_ostream&
|
||||
operator<< (std::ostream& manip (std::ostream&))
|
||||
{
|
||||
stream() << manip;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
basic_scoped_ostream&
|
||||
operator<< (T const& t)
|
||||
{
|
||||
stream() << t;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STREAM_BASIC_STD_OSTREAM_H_INCLUDED
|
||||
#define BEAST_DETAIL_STREAM_BASIC_STD_OSTREAM_H_INCLUDED
|
||||
|
||||
#include <beast/detail/stream/basic_abstract_ostream.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** Wraps an existing std::basic_ostream as an abstract_ostream. */
|
||||
template <
|
||||
class CharT,
|
||||
class Traits = std::char_traits <CharT>
|
||||
>
|
||||
class basic_std_ostream
|
||||
: public basic_abstract_ostream <CharT, Traits>
|
||||
{
|
||||
private:
|
||||
using typename basic_abstract_ostream <CharT, Traits>::string_type;
|
||||
|
||||
std::reference_wrapper <std::ostream> m_stream;
|
||||
|
||||
public:
|
||||
explicit basic_std_ostream (
|
||||
std::basic_ostream <CharT, Traits>& stream)
|
||||
: m_stream (stream)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write (string_type const& s) override
|
||||
{
|
||||
m_stream.get() << s << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
using std_ostream = basic_std_ostream <char>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Returns a basic_std_ostream using template argument deduction. */
|
||||
template <
|
||||
class CharT,
|
||||
class Traits = std::char_traits <CharT>
|
||||
>
|
||||
basic_std_ostream <CharT, Traits>
|
||||
make_std_ostream (std::basic_ostream <CharT, Traits>& stream)
|
||||
{
|
||||
return basic_std_ostream <CharT, Traits> (stream);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STREAM_DEBUG_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_STREAM_DEBUG_OSTREAM_HPP
|
||||
|
||||
#include <beast/detail/stream/abstract_ostream.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# ifndef WIN32_LEAN_AND_MEAN // VC_EXTRALEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
# undef WIN32_LEAN_AND_MEAN
|
||||
# else
|
||||
#include <windows.h>
|
||||
# endif
|
||||
# ifdef min
|
||||
# undef min
|
||||
# endif
|
||||
# ifdef max
|
||||
# undef max
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/** A basic_abstract_ostream that redirects output to an attached debugger. */
|
||||
class debug_ostream
|
||||
: public abstract_ostream
|
||||
{
|
||||
private:
|
||||
bool m_debugger;
|
||||
|
||||
public:
|
||||
debug_ostream()
|
||||
: m_debugger (IsDebuggerPresent() != FALSE)
|
||||
{
|
||||
// Note that the check for an attached debugger is made only
|
||||
// during construction time, for efficiency. A stream created before
|
||||
// the debugger is attached will not have output redirected.
|
||||
}
|
||||
|
||||
void
|
||||
write (string_type const& s) override
|
||||
{
|
||||
if (m_debugger)
|
||||
{
|
||||
OutputDebugStringA ((s + "\n").c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << s << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
class debug_ostream
|
||||
: public abstract_ostream
|
||||
{
|
||||
public:
|
||||
void
|
||||
write (string_type const& s) override
|
||||
{
|
||||
std::cout << s << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_TEMP_DIR_H_INCLUDED
|
||||
#define BEAST_DETAIL_TEMP_DIR_H_INCLUDED
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** RAII temporary directory.
|
||||
|
||||
The directory and all its contents are deleted when
|
||||
the instance of `temp_dir` is destroyed.
|
||||
*/
|
||||
class temp_dir
|
||||
{
|
||||
boost::filesystem::path path_;
|
||||
|
||||
public:
|
||||
#if ! GENERATING_DOCS
|
||||
temp_dir(const temp_dir&) = delete;
|
||||
temp_dir& operator=(const temp_dir&) = delete;
|
||||
#endif
|
||||
|
||||
/// Construct a temporary directory.
|
||||
temp_dir()
|
||||
{
|
||||
auto const dir =
|
||||
boost::filesystem::temp_directory_path();
|
||||
do
|
||||
{
|
||||
path_ =
|
||||
dir / boost::filesystem::unique_path();
|
||||
}
|
||||
while(boost::filesystem::exists(path_));
|
||||
boost::filesystem::create_directory (path_);
|
||||
}
|
||||
|
||||
/// Destroy a temporary directory.
|
||||
~temp_dir()
|
||||
{
|
||||
boost::filesystem::remove_all (path_);
|
||||
}
|
||||
|
||||
/// Get the native path for the temporary directory
|
||||
std::string
|
||||
path() const
|
||||
{
|
||||
return path_.string();
|
||||
}
|
||||
|
||||
/** Get the native path for the a file.
|
||||
|
||||
The file does not need to exist.
|
||||
*/
|
||||
std::string
|
||||
file(std::string const& name) const
|
||||
{
|
||||
return (path_ / name).string();
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_H_INCLUDED
|
||||
#define BEAST_DETAIL_UNIT_TEST_H_INCLUDED
|
||||
|
||||
#include <beast/detail/unit_test/amount.h>
|
||||
#include <beast/detail/unit_test/print.h>
|
||||
#include <beast/detail/unit_test/global_suites.h>
|
||||
#include <beast/detail/unit_test/match.h>
|
||||
#include <beast/detail/unit_test/recorder.h>
|
||||
#include <beast/detail/unit_test/reporter.h>
|
||||
#include <beast/detail/unit_test/results.h>
|
||||
#include <beast/detail/unit_test/runner.h>
|
||||
#include <beast/detail/unit_test/suite.h>
|
||||
#include <beast/detail/unit_test/suite_info.h>
|
||||
#include <beast/detail/unit_test/suite_list.h>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_AMOUNT_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_AMOUNT_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
/** Utility for producing nicely composed output of amounts with units. */
|
||||
class amount
|
||||
{
|
||||
private:
|
||||
std::size_t n_;
|
||||
std::string const& what_;
|
||||
|
||||
public:
|
||||
amount (amount const&) = default;
|
||||
amount& operator= (amount const&) = delete;
|
||||
|
||||
template <class = void>
|
||||
amount (std::size_t n, std::string const& what);
|
||||
|
||||
friend
|
||||
std::ostream&
|
||||
operator<< (std::ostream& s, amount const& t);
|
||||
};
|
||||
|
||||
template <class>
|
||||
amount::amount (std::size_t n, std::string const& what)
|
||||
: n_ (n)
|
||||
, what_ (what)
|
||||
{
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<< (std::ostream& s, amount const& t)
|
||||
{
|
||||
s << t.n_ << " " << t.what_ << ((t.n_ != 1) ? "s" : "");
|
||||
return s;
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#include <beast/detail/unit_test/amount.hpp>
|
||||
#include <beast/detail/unit_test/global_suites.hpp>
|
||||
#include <beast/detail/unit_test/suite.hpp>
|
||||
#include <string>
|
||||
|
||||
// Include this .cpp in your project to gain access to the printing suite
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
namespace detail {
|
||||
|
||||
/** A suite that prints the list of globally defined suites. */
|
||||
class print_test : public suite
|
||||
{
|
||||
private:
|
||||
template <class = void>
|
||||
void
|
||||
do_run();
|
||||
|
||||
public:
|
||||
template <class = void>
|
||||
static
|
||||
std::string
|
||||
prefix (suite_info const& s);
|
||||
|
||||
template <class = void>
|
||||
void
|
||||
print (suite_list &c);
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
do_run();
|
||||
}
|
||||
};
|
||||
|
||||
template <class>
|
||||
void
|
||||
print_test::do_run()
|
||||
{
|
||||
log << "------------------------------------------";
|
||||
print (global_suites());
|
||||
log << "------------------------------------------";
|
||||
pass();
|
||||
}
|
||||
|
||||
template <class>
|
||||
std::string
|
||||
print_test::prefix (suite_info const& s)
|
||||
{
|
||||
if (s.manual())
|
||||
return "|M| ";
|
||||
return " ";
|
||||
}
|
||||
|
||||
template <class>
|
||||
void
|
||||
print_test::print (suite_list &c)
|
||||
{
|
||||
std::size_t manual (0);
|
||||
for (auto const& s : c)
|
||||
{
|
||||
log <<
|
||||
prefix (s) <<
|
||||
s.full_name();
|
||||
if (s.manual())
|
||||
++manual;
|
||||
}
|
||||
log <<
|
||||
amount (c.size(), "suite") << " total, " <<
|
||||
amount (manual, "manual suite")
|
||||
;
|
||||
}
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_MANUAL(print,unit_test,beast);
|
||||
|
||||
} // detail
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_GLOBAL_SUITES_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_GLOBAL_SUITES_HPP
|
||||
|
||||
#include <beast/detail/unit_test/suite_list.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class = void>
|
||||
suite_list&
|
||||
global_suites()
|
||||
{
|
||||
static suite_list s;
|
||||
return s;
|
||||
}
|
||||
|
||||
template <class Suite>
|
||||
struct insert_suite
|
||||
{
|
||||
template <class = void>
|
||||
insert_suite (char const* name, char const* module,
|
||||
char const* library, bool manual);
|
||||
};
|
||||
|
||||
template <class Suite>
|
||||
template <class>
|
||||
insert_suite<Suite>::insert_suite (char const* name,
|
||||
char const* module, char const* library, bool manual)
|
||||
{
|
||||
global_suites().insert <Suite> (
|
||||
name, module, library, manual);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
/** Holds suites registered during static initialization. */
|
||||
inline
|
||||
suite_list const&
|
||||
global_suites()
|
||||
{
|
||||
return detail::global_suites();
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_MATCH_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_MATCH_HPP
|
||||
|
||||
#include <beast/detail/unit_test/suite_info.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
// Predicate for implementing matches
|
||||
class selector
|
||||
{
|
||||
public:
|
||||
enum mode_t
|
||||
{
|
||||
// Run all tests except manual ones
|
||||
all,
|
||||
|
||||
// Run tests that match in any field
|
||||
automatch,
|
||||
|
||||
// Match on suite
|
||||
suite,
|
||||
|
||||
// Match on library
|
||||
library,
|
||||
|
||||
// Match on module (used internally)
|
||||
module,
|
||||
|
||||
// Match nothing (used internally)
|
||||
none
|
||||
};
|
||||
|
||||
private:
|
||||
mode_t mode_;
|
||||
std::string pat_;
|
||||
std::string library_;
|
||||
|
||||
public:
|
||||
template <class = void>
|
||||
explicit
|
||||
selector (mode_t mode, std::string const& pattern = "");
|
||||
|
||||
template <class = void>
|
||||
bool
|
||||
operator() (suite_info const& s);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class>
|
||||
selector::selector (mode_t mode, std::string const& pattern)
|
||||
: mode_ (mode)
|
||||
, pat_ (pattern)
|
||||
{
|
||||
if (mode_ == automatch && pattern.empty())
|
||||
mode_ = all;
|
||||
}
|
||||
|
||||
template <class>
|
||||
bool
|
||||
selector::operator() (suite_info const& s)
|
||||
{
|
||||
switch (mode_)
|
||||
{
|
||||
case automatch:
|
||||
// suite or full name
|
||||
if (s.name() == pat_ || s.full_name() == pat_)
|
||||
{
|
||||
mode_ = none;
|
||||
return true;
|
||||
}
|
||||
|
||||
// check module
|
||||
if (pat_ == s.module())
|
||||
{
|
||||
mode_ = module;
|
||||
library_ = s.library();
|
||||
return ! s.manual();
|
||||
}
|
||||
|
||||
// check library
|
||||
if (pat_ == s.library())
|
||||
{
|
||||
mode_ = library;
|
||||
return ! s.manual();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case suite:
|
||||
return pat_ == s.name();
|
||||
|
||||
case module:
|
||||
return pat_ == s.module() && ! s.manual();
|
||||
|
||||
case library:
|
||||
return pat_ == s.library() && ! s.manual();
|
||||
|
||||
case none:
|
||||
return false;
|
||||
|
||||
case all:
|
||||
default:
|
||||
// fall through
|
||||
break;
|
||||
};
|
||||
|
||||
return ! s.manual();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Utility functions for producing predicates to select suites.
|
||||
|
||||
/** Returns a predicate that implements a smart matching rule.
|
||||
The predicate checks the suite, module, and library fields of the
|
||||
suite_info in that order. When it finds a match, it changes modes
|
||||
depending on what was found:
|
||||
|
||||
If a suite is matched first, then only the suite is selected. The
|
||||
suite may be marked manual.
|
||||
|
||||
If a module is matched first, then only suites from that module
|
||||
and library not marked manual are selected from then on.
|
||||
|
||||
If a library is matched first, then only suites from that library
|
||||
not marked manual are selected from then on.
|
||||
|
||||
*/
|
||||
inline
|
||||
selector
|
||||
match_auto (std::string const& name)
|
||||
{
|
||||
return selector (selector::automatch, name);
|
||||
}
|
||||
|
||||
/** Return a predicate that matches all suites not marked manual. */
|
||||
inline
|
||||
selector
|
||||
match_all()
|
||||
{
|
||||
return selector (selector::all);
|
||||
}
|
||||
|
||||
/** Returns a predicate that matches a specific suite. */
|
||||
inline
|
||||
selector
|
||||
match_suite (std::string const& name)
|
||||
{
|
||||
return selector (selector::suite, name);
|
||||
}
|
||||
|
||||
/** Returns a predicate that matches all suites in a library. */
|
||||
inline
|
||||
selector
|
||||
match_library (std::string const& name)
|
||||
{
|
||||
return selector (selector::library, name);
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_PRINT_H_INCLUDED
|
||||
#define BEAST_DETAIL_UNIT_TEST_PRINT_H_INCLUDED
|
||||
|
||||
#include <beast/detail/unit_test/amount.hpp>
|
||||
#include <beast/detail/unit_test/results.hpp>
|
||||
#include <beast/detail/stream/abstract_ostream.hpp>
|
||||
#include <beast/detail/stream/basic_std_ostream.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
/** Write test results to the specified output stream. */
|
||||
/** @{ */
|
||||
template <class = void>
|
||||
void
|
||||
print (results const& r, beast::detail::abstract_ostream& stream)
|
||||
{
|
||||
for (auto const& s : r)
|
||||
{
|
||||
for (auto const& c : s)
|
||||
{
|
||||
stream <<
|
||||
s.name() <<
|
||||
(c.name().empty() ? "" : ("." + c.name()));
|
||||
|
||||
std::size_t i (1);
|
||||
for (auto const& t : c.tests)
|
||||
{
|
||||
if (! t.pass)
|
||||
stream <<
|
||||
"#" << i <<
|
||||
" failed: " << t.reason;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream <<
|
||||
amount (r.size(), "suite") << ", " <<
|
||||
amount (r.cases(), "case") << ", " <<
|
||||
amount (r.total(), "test") << " total, " <<
|
||||
amount (r.failed(), "failure")
|
||||
;
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
void
|
||||
print (results const& r, std::ostream& stream = std::cout)
|
||||
{
|
||||
auto s (make_std_ostream (stream));
|
||||
print (r, s);
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_RECORDER_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_RECORDER_HPP
|
||||
|
||||
#include <beast/detail/unit_test/results.hpp>
|
||||
#include <beast/detail/unit_test/runner.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
/** A test runner that stores the results. */
|
||||
class recorder : public runner
|
||||
{
|
||||
private:
|
||||
results m_results;
|
||||
suite_results m_suite;
|
||||
case_results m_case;
|
||||
|
||||
public:
|
||||
recorder() = default;
|
||||
recorder (recorder const&) = default;
|
||||
recorder& operator= (recorder const&) = default;
|
||||
|
||||
/** Returns a report with the results of all completed suites. */
|
||||
results const&
|
||||
report() const
|
||||
{
|
||||
return m_results;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual
|
||||
void
|
||||
on_suite_begin (suite_info const& info) override
|
||||
{
|
||||
m_suite = suite_results (info.full_name());
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_suite_end() override
|
||||
{
|
||||
m_results.insert (std::move (m_suite));
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_case_begin (std::string const& name) override
|
||||
{
|
||||
m_case = case_results (name);
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_case_end() override
|
||||
{
|
||||
if (m_case.tests.size() > 0)
|
||||
m_suite.insert (std::move (m_case));
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_pass() override
|
||||
{
|
||||
m_case.tests.pass();
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_fail (std::string const& reason) override
|
||||
{
|
||||
m_case.tests.fail (reason);
|
||||
}
|
||||
|
||||
virtual
|
||||
void
|
||||
on_log (std::string const& s) override
|
||||
{
|
||||
m_case.log.insert (s);
|
||||
}
|
||||
};
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,324 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_REPORTER_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_REPORTER_HPP
|
||||
|
||||
#include <beast/detail/unit_test/amount.hpp>
|
||||
#include <beast/detail/unit_test/recorder.hpp>
|
||||
#include <beast/detail/stream/abstract_ostream.hpp>
|
||||
#include <beast/detail/stream/basic_std_ostream.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** A simple test runner that writes everything to a stream in real time.
|
||||
The totals are output when the object is destroyed.
|
||||
*/
|
||||
template <class = void>
|
||||
class reporter : public runner
|
||||
{
|
||||
private:
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
|
||||
struct case_results
|
||||
{
|
||||
std::string name;
|
||||
std::size_t total = 0;
|
||||
std::size_t failed = 0;
|
||||
|
||||
case_results (std::string const& name_ = "");
|
||||
};
|
||||
|
||||
struct suite_results
|
||||
{
|
||||
std::string name;
|
||||
std::size_t cases = 0;
|
||||
std::size_t total = 0;
|
||||
std::size_t failed = 0;
|
||||
typename clock_type::time_point start =
|
||||
clock_type::now();
|
||||
|
||||
explicit
|
||||
suite_results (std::string const& name_ = "");
|
||||
|
||||
void
|
||||
add (case_results const& r);
|
||||
};
|
||||
|
||||
struct results
|
||||
{
|
||||
using run_time = std::pair<std::string,
|
||||
typename clock_type::duration>;
|
||||
|
||||
enum
|
||||
{
|
||||
max_top = 10
|
||||
};
|
||||
|
||||
std::size_t suites = 0;
|
||||
std::size_t cases = 0;
|
||||
std::size_t total = 0;
|
||||
std::size_t failed = 0;
|
||||
std::vector<run_time> top;
|
||||
typename clock_type::time_point start =
|
||||
clock_type::now();
|
||||
|
||||
void
|
||||
add (suite_results const& r);
|
||||
};
|
||||
|
||||
boost::optional <std_ostream> std_ostream_;
|
||||
std::reference_wrapper <beast::detail::abstract_ostream> stream_;
|
||||
results results_;
|
||||
suite_results suite_results_;
|
||||
case_results case_results_;
|
||||
|
||||
public:
|
||||
reporter (reporter const&) = delete;
|
||||
reporter& operator= (reporter const&) = delete;
|
||||
|
||||
~reporter();
|
||||
|
||||
explicit
|
||||
reporter (std::ostream& stream = std::cout);
|
||||
|
||||
explicit
|
||||
reporter (beast::detail::abstract_ostream& stream);
|
||||
|
||||
private:
|
||||
static
|
||||
std::string
|
||||
fmtdur (typename clock_type::duration const& d);
|
||||
|
||||
virtual
|
||||
void
|
||||
on_suite_begin (suite_info const& info) override;
|
||||
|
||||
virtual
|
||||
void
|
||||
on_suite_end() override;
|
||||
|
||||
virtual
|
||||
void
|
||||
on_case_begin (std::string const& name) override;
|
||||
|
||||
virtual
|
||||
void
|
||||
on_case_end() override;
|
||||
|
||||
virtual
|
||||
void
|
||||
on_pass() override;
|
||||
|
||||
virtual
|
||||
void
|
||||
on_fail (std::string const& reason) override;
|
||||
|
||||
virtual
|
||||
void
|
||||
on_log (std::string const& s) override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class _>
|
||||
reporter<_>::case_results::case_results (
|
||||
std::string const& name_)
|
||||
: name (name_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class _>
|
||||
reporter<_>::suite_results::suite_results (
|
||||
std::string const& name_)
|
||||
: name (name_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::suite_results::add (case_results const& r)
|
||||
{
|
||||
++cases;
|
||||
total += r.total;
|
||||
failed += r.failed;
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::results::add (
|
||||
suite_results const& r)
|
||||
{
|
||||
++suites;
|
||||
total += r.total;
|
||||
cases += r.cases;
|
||||
failed += r.failed;
|
||||
|
||||
auto const elapsed =
|
||||
clock_type::now() - r.start;
|
||||
if (elapsed >= std::chrono::seconds(1))
|
||||
{
|
||||
auto const iter = std::lower_bound(top.begin(),
|
||||
top.end(), elapsed,
|
||||
[](run_time const& t1,
|
||||
typename clock_type::duration const& t2)
|
||||
{
|
||||
return t1.second > t2;
|
||||
});
|
||||
if (iter != top.end())
|
||||
{
|
||||
if (top.size() == max_top)
|
||||
top.resize(top.size() - 1);
|
||||
top.emplace(iter, r.name, elapsed);
|
||||
}
|
||||
else if (top.size() < max_top)
|
||||
{
|
||||
top.emplace_back(r.name, elapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class _>
|
||||
reporter<_>::reporter (
|
||||
std::ostream& stream)
|
||||
: std_ostream_ (std::ref (stream))
|
||||
, stream_ (*std_ostream_)
|
||||
{
|
||||
}
|
||||
|
||||
template <class _>
|
||||
reporter<_>::~reporter()
|
||||
{
|
||||
if (results_.top.size() > 0)
|
||||
{
|
||||
stream_.get() << "Longest suite times:";
|
||||
for (auto const& i : results_.top)
|
||||
stream_.get() << std::setw(8) <<
|
||||
fmtdur(i.second) << " " << i.first;
|
||||
}
|
||||
auto const elapsed =
|
||||
clock_type::now() - results_.start;
|
||||
stream_.get() <<
|
||||
fmtdur(elapsed) << ", " <<
|
||||
amount (results_.suites, "suite") << ", " <<
|
||||
amount (results_.cases, "case") << ", " <<
|
||||
amount (results_.total, "test") << " total, " <<
|
||||
amount (results_.failed, "failure");
|
||||
}
|
||||
|
||||
template <class _>
|
||||
reporter<_>::reporter (
|
||||
beast::detail::abstract_ostream& stream)
|
||||
: stream_ (stream)
|
||||
{
|
||||
}
|
||||
|
||||
template <class _>
|
||||
std::string
|
||||
reporter<_>::fmtdur (
|
||||
typename clock_type::duration const& d)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto const ms =
|
||||
duration_cast<milliseconds>(d);
|
||||
if (ms < seconds(1))
|
||||
return std::to_string(ms.count()) + "ms";
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(1) <<
|
||||
(ms.count()/1000.) << "s";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::on_suite_begin (
|
||||
suite_info const& info)
|
||||
{
|
||||
suite_results_ = suite_results (info.full_name());
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::on_suite_end()
|
||||
{
|
||||
results_.add (suite_results_);
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::on_case_begin (
|
||||
std::string const& name)
|
||||
{
|
||||
case_results_ = case_results (name);
|
||||
|
||||
stream_.get() <<
|
||||
suite_results_.name <<
|
||||
(case_results_.name.empty() ?
|
||||
"" : (" " + case_results_.name));
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::on_case_end()
|
||||
{
|
||||
suite_results_.add (case_results_);
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::on_pass()
|
||||
{
|
||||
++case_results_.total;
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::on_fail (
|
||||
std::string const& reason)
|
||||
{
|
||||
++case_results_.failed;
|
||||
++case_results_.total;
|
||||
stream_.get() <<
|
||||
"#" << case_results_.total <<
|
||||
" failed" <<
|
||||
(reason.empty() ? "" : ": ") << reason;
|
||||
}
|
||||
|
||||
template <class _>
|
||||
void
|
||||
reporter<_>::on_log (
|
||||
std::string const& s)
|
||||
{
|
||||
stream_.get() << s;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
using reporter = detail::reporter<>;
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,246 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_RESULTS_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_RESULTS_HPP
|
||||
|
||||
#include <beast/detail/const_container.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
/** Holds a set of test condition outcomes in a testcase. */
|
||||
class case_results
|
||||
{
|
||||
public:
|
||||
/** Holds the result of evaluating one test condition. */
|
||||
struct test
|
||||
{
|
||||
explicit test (bool pass_)
|
||||
: pass (pass_)
|
||||
{
|
||||
}
|
||||
|
||||
test (bool pass_, std::string const& reason_)
|
||||
: pass (pass_)
|
||||
, reason (reason_)
|
||||
{
|
||||
}
|
||||
|
||||
bool pass;
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
private:
|
||||
class tests_t
|
||||
: public const_container <std::vector <test>>
|
||||
{
|
||||
private:
|
||||
std::size_t failed_;
|
||||
|
||||
public:
|
||||
tests_t ()
|
||||
: failed_ (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the total number of test conditions. */
|
||||
std::size_t
|
||||
total() const
|
||||
{
|
||||
return cont().size();
|
||||
}
|
||||
|
||||
/** Returns the number of failed test conditions. */
|
||||
std::size_t
|
||||
failed() const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
/** Register a successful test condition. */
|
||||
void
|
||||
pass()
|
||||
{
|
||||
cont().emplace_back (true);
|
||||
}
|
||||
|
||||
/** Register a failed test condition. */
|
||||
void
|
||||
fail (std::string const& reason = "")
|
||||
{
|
||||
++failed_;
|
||||
cont().emplace_back (false, reason);
|
||||
}
|
||||
};
|
||||
|
||||
class log_t
|
||||
: public const_container <std::vector <std::string>>
|
||||
{
|
||||
public:
|
||||
/** Insert a string into the log. */
|
||||
void
|
||||
insert (std::string const& s)
|
||||
{
|
||||
cont().push_back (s);
|
||||
}
|
||||
};
|
||||
|
||||
std::string name_;
|
||||
|
||||
public:
|
||||
explicit case_results (std::string const& name = "")
|
||||
: name_ (name)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the name of this testcase. */
|
||||
std::string const&
|
||||
name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/** Memberspace for a container of test condition outcomes. */
|
||||
tests_t tests;
|
||||
|
||||
/** Memberspace for a container of testcase log messages. */
|
||||
log_t log;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Holds the set of testcase results in a suite. */
|
||||
class suite_results
|
||||
: public const_container <std::vector <case_results>>
|
||||
{
|
||||
private:
|
||||
std::string name_;
|
||||
std::size_t total_ = 0;
|
||||
std::size_t failed_ = 0;
|
||||
|
||||
public:
|
||||
explicit suite_results (std::string const& name = "")
|
||||
: name_ (name)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the name of this suite. */
|
||||
std::string const&
|
||||
name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/** Returns the total number of test conditions. */
|
||||
std::size_t
|
||||
total() const
|
||||
{
|
||||
return total_;
|
||||
}
|
||||
|
||||
/** Returns the number of failures. */
|
||||
std::size_t
|
||||
failed() const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
/** Insert a set of testcase results. */
|
||||
/** @{ */
|
||||
void
|
||||
insert (case_results&& r)
|
||||
{
|
||||
cont().emplace_back (std::move (r));
|
||||
total_ += r.tests.total();
|
||||
failed_ += r.tests.failed();
|
||||
}
|
||||
|
||||
void
|
||||
insert (case_results const& r)
|
||||
{
|
||||
cont().push_back (r);
|
||||
total_ += r.tests.total();
|
||||
failed_ += r.tests.failed();
|
||||
}
|
||||
/** @} */
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// VFALCO TODO Make this a template class using scoped allocators
|
||||
/** Holds the results of running a set of testsuites. */
|
||||
class results
|
||||
: public const_container <std::vector <suite_results>>
|
||||
{
|
||||
private:
|
||||
std::size_t m_cases;
|
||||
std::size_t total_;
|
||||
std::size_t failed_;
|
||||
|
||||
public:
|
||||
results()
|
||||
: m_cases (0)
|
||||
, total_ (0)
|
||||
, failed_ (0)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the total number of test cases. */
|
||||
std::size_t
|
||||
cases() const
|
||||
{
|
||||
return m_cases;
|
||||
}
|
||||
|
||||
/** Returns the total number of test conditions. */
|
||||
std::size_t
|
||||
total() const
|
||||
{
|
||||
return total_;
|
||||
}
|
||||
|
||||
/** Returns the number of failures. */
|
||||
std::size_t
|
||||
failed() const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
/** Insert a set of suite results. */
|
||||
/** @{ */
|
||||
void
|
||||
insert (suite_results&& r)
|
||||
{
|
||||
m_cases += r.size();
|
||||
total_ += r.total();
|
||||
failed_ += r.failed();
|
||||
cont().emplace_back (std::move (r));
|
||||
}
|
||||
|
||||
void
|
||||
insert (suite_results const& r)
|
||||
{
|
||||
m_cases += r.size();
|
||||
total_ += r.total();
|
||||
failed_ += r.failed();
|
||||
cont().push_back (r);
|
||||
}
|
||||
/** @} */
|
||||
};
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,338 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_RUNNER_H_INCLUDED
|
||||
#define BEAST_DETAIL_UNIT_TEST_RUNNER_H_INCLUDED
|
||||
|
||||
#include <beast/detail/unit_test/suite_info.hpp>
|
||||
#include <beast/detail/stream/abstract_ostream.hpp>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
/** Unit test runner interface.
|
||||
Derived classes can customize the reporting behavior. This interface is
|
||||
injected into the unit_test class to receive the results of the tests.
|
||||
*/
|
||||
class runner
|
||||
{
|
||||
private:
|
||||
// Reroutes log output to the runner
|
||||
class stream_t : public beast::detail::abstract_ostream
|
||||
{
|
||||
private:
|
||||
runner& owner_;
|
||||
|
||||
public:
|
||||
stream_t() = delete;
|
||||
stream_t& operator= (stream_t const&) = delete;
|
||||
|
||||
template <class = void>
|
||||
stream_t (runner& owner);
|
||||
|
||||
void
|
||||
write (string_type const& s) override
|
||||
{
|
||||
owner_.log (s);
|
||||
}
|
||||
};
|
||||
|
||||
stream_t stream_;
|
||||
std::string arg_;
|
||||
bool default_ = false;
|
||||
bool failed_ = false;
|
||||
bool cond_ = false;
|
||||
std::recursive_mutex mutex_;
|
||||
|
||||
public:
|
||||
virtual ~runner() = default;
|
||||
runner (runner const&) = default;
|
||||
runner& operator= (runner const&) = default;
|
||||
|
||||
template <class = void>
|
||||
runner();
|
||||
|
||||
/** Set the argument string.
|
||||
The argument string is available to suites and
|
||||
allows for customization of the test. Each suite
|
||||
defines its own syntax for the argumnet string.
|
||||
The same argument is passed to all suites.
|
||||
*/
|
||||
void
|
||||
arg (std::string const& s)
|
||||
{
|
||||
arg_ = s;
|
||||
}
|
||||
|
||||
/** Returns the argument string. */
|
||||
std::string const&
|
||||
arg() const
|
||||
{
|
||||
return arg_;
|
||||
}
|
||||
|
||||
/** Run the specified suite.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class = void>
|
||||
bool
|
||||
run (suite_info const& s);
|
||||
|
||||
/** Run a sequence of suites.
|
||||
The expression
|
||||
`FwdIter::value_type`
|
||||
must be convertible to `suite_info`.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class FwdIter>
|
||||
bool
|
||||
run (FwdIter first, FwdIter last);
|
||||
|
||||
/** Conditionally run a sequence of suites.
|
||||
pred will be called as:
|
||||
@code
|
||||
bool pred (suite_info const&);
|
||||
@endcode
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class FwdIter, class Pred>
|
||||
bool
|
||||
run_if (FwdIter first, FwdIter last, Pred pred = Pred{});
|
||||
|
||||
/** Run all suites in a container.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class SequenceContainer>
|
||||
bool
|
||||
run_each (SequenceContainer const& c);
|
||||
|
||||
/** Conditionally run suites in a container.
|
||||
pred will be called as:
|
||||
@code
|
||||
bool pred (suite_info const&);
|
||||
@endcode
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class SequenceContainer, class Pred>
|
||||
bool
|
||||
run_each_if (SequenceContainer const& c, Pred pred = Pred{});
|
||||
|
||||
private:
|
||||
//
|
||||
// Overrides
|
||||
//
|
||||
|
||||
/** Called when a new suite starts. */
|
||||
virtual
|
||||
void
|
||||
on_suite_begin (suite_info const&)
|
||||
{
|
||||
}
|
||||
|
||||
/** Called when a suite ends. */
|
||||
virtual
|
||||
void
|
||||
on_suite_end()
|
||||
{
|
||||
}
|
||||
|
||||
/** Called when a new case starts. */
|
||||
virtual
|
||||
void
|
||||
on_case_begin (std::string const&)
|
||||
{
|
||||
}
|
||||
|
||||
/** Called when a new case ends. */
|
||||
virtual
|
||||
void
|
||||
on_case_end()
|
||||
{
|
||||
}
|
||||
|
||||
/** Called for each passing condition. */
|
||||
virtual
|
||||
void
|
||||
on_pass ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Called for each failing condition. */
|
||||
virtual
|
||||
void
|
||||
on_fail (std::string const&)
|
||||
{
|
||||
}
|
||||
|
||||
/** Called when a test logs output. */
|
||||
virtual
|
||||
void
|
||||
on_log (std::string const&)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
friend class suite;
|
||||
|
||||
abstract_ostream&
|
||||
stream()
|
||||
{
|
||||
return stream_;
|
||||
}
|
||||
|
||||
// Start a new testcase.
|
||||
template <class = void>
|
||||
void
|
||||
testcase (std::string const& name);
|
||||
|
||||
template <class = void>
|
||||
void
|
||||
pass();
|
||||
|
||||
template <class = void>
|
||||
void
|
||||
fail (std::string const& reason);
|
||||
|
||||
template <class = void>
|
||||
void
|
||||
log (std::string const& s);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class>
|
||||
runner::stream_t::stream_t (runner& owner)
|
||||
: owner_ (owner)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class>
|
||||
runner::runner()
|
||||
: stream_ (*this)
|
||||
{
|
||||
}
|
||||
|
||||
template <class>
|
||||
bool
|
||||
runner::run (suite_info const& s)
|
||||
{
|
||||
// Enable 'default' testcase
|
||||
default_ = true;
|
||||
failed_ = false;
|
||||
on_suite_begin (s);
|
||||
s.run (*this);
|
||||
// Forgot to call pass or fail.
|
||||
assert (cond_);
|
||||
on_case_end();
|
||||
on_suite_end();
|
||||
return failed_;
|
||||
}
|
||||
|
||||
template <class FwdIter>
|
||||
bool
|
||||
runner::run (FwdIter first, FwdIter last)
|
||||
{
|
||||
bool failed (false);
|
||||
for (;first != last; ++first)
|
||||
failed = run (*first) || failed;
|
||||
return failed;
|
||||
}
|
||||
|
||||
template <class FwdIter, class Pred>
|
||||
bool
|
||||
runner::run_if (FwdIter first, FwdIter last, Pred pred)
|
||||
{
|
||||
bool failed (false);
|
||||
for (;first != last; ++first)
|
||||
if (pred (*first))
|
||||
failed = run (*first) || failed;
|
||||
return failed;
|
||||
}
|
||||
|
||||
template <class SequenceContainer>
|
||||
bool
|
||||
runner::run_each (SequenceContainer const& c)
|
||||
{
|
||||
bool failed (false);
|
||||
for (auto const& s : c)
|
||||
failed = run (s) || failed;
|
||||
return failed;
|
||||
}
|
||||
|
||||
template <class SequenceContainer, class Pred>
|
||||
bool
|
||||
runner::run_each_if (SequenceContainer const& c, Pred pred)
|
||||
{
|
||||
bool failed (false);
|
||||
for (auto const& s : c)
|
||||
if (pred (s))
|
||||
failed = run (s) || failed;
|
||||
return failed;
|
||||
}
|
||||
|
||||
template <class>
|
||||
void
|
||||
runner::testcase (std::string const& name)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
// Name may not be empty
|
||||
assert (default_ || ! name.empty());
|
||||
// Forgot to call pass or fail
|
||||
assert (default_ || cond_);
|
||||
if (! default_)
|
||||
on_case_end();
|
||||
default_ = false;
|
||||
cond_ = false;
|
||||
on_case_begin (name);
|
||||
}
|
||||
|
||||
template <class>
|
||||
void
|
||||
runner::pass()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (default_)
|
||||
testcase ("");
|
||||
on_pass();
|
||||
cond_ = true;
|
||||
}
|
||||
|
||||
template <class>
|
||||
void
|
||||
runner::fail (std::string const& reason)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (default_)
|
||||
testcase ("");
|
||||
on_fail (reason);
|
||||
failed_ = true;
|
||||
cond_ = true;
|
||||
}
|
||||
|
||||
template <class>
|
||||
void
|
||||
runner::log (std::string const& s)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (default_)
|
||||
testcase ("");
|
||||
on_log (s);
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,608 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_SUITE_HPP
|
||||
|
||||
#include <beast/detail/unit_test/runner.hpp>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
class thread;
|
||||
|
||||
/** A testsuite class.
|
||||
Derived classes execute a series of testcases, where each testcase is
|
||||
a series of pass/fail tests. To provide a unit test using this class,
|
||||
derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
|
||||
translation unit.
|
||||
*/
|
||||
class suite
|
||||
{
|
||||
public:
|
||||
enum abort_t
|
||||
{
|
||||
no_abort_on_fail,
|
||||
abort_on_fail
|
||||
};
|
||||
|
||||
private:
|
||||
bool abort_ = false;
|
||||
bool aborted_ = false;
|
||||
runner* runner_ = nullptr;
|
||||
|
||||
// This exception is thrown internally to stop the current suite
|
||||
// in the event of a failure, if the option to stop is set.
|
||||
struct abort_exception : public std::exception
|
||||
{
|
||||
char const*
|
||||
what() const noexcept override
|
||||
{
|
||||
return "suite aborted";
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
// Memberspace
|
||||
class log_t
|
||||
{
|
||||
private:
|
||||
friend class suite;
|
||||
suite* suite_ = nullptr;
|
||||
|
||||
public:
|
||||
log_t () = default;
|
||||
|
||||
template <class T>
|
||||
abstract_ostream::scoped_stream_type
|
||||
operator<< (T const& t);
|
||||
|
||||
/** Returns the raw stream used for output. */
|
||||
abstract_ostream&
|
||||
stream();
|
||||
};
|
||||
private:
|
||||
|
||||
class scoped_testcase;
|
||||
|
||||
// Memberspace
|
||||
class testcase_t
|
||||
{
|
||||
private:
|
||||
friend class suite;
|
||||
suite* suite_ = nullptr;
|
||||
std::stringstream ss_;
|
||||
|
||||
public:
|
||||
testcase_t() = default;
|
||||
|
||||
/** Open a new testcase.
|
||||
A testcase is a series of evaluated test conditions. A test suite
|
||||
may have multiple test cases. A test is associated with the last
|
||||
opened testcase. When the test first runs, a default unnamed
|
||||
case is opened. Tests with only one case may omit the call
|
||||
to testcase.
|
||||
@param abort If `true`, the suite will be stopped on first failure.
|
||||
*/
|
||||
void
|
||||
operator() (std::string const& name,
|
||||
abort_t abort = no_abort_on_fail);
|
||||
|
||||
/** Stream style composition of testcase names. */
|
||||
/** @{ */
|
||||
scoped_testcase
|
||||
operator() (abort_t abort);
|
||||
|
||||
template <class T>
|
||||
scoped_testcase
|
||||
operator<< (T const& t);
|
||||
/** @} */
|
||||
};
|
||||
|
||||
public:
|
||||
/** Type for scoped stream logging.
|
||||
To use this type, declare a local variable of the type
|
||||
on the stack in derived class member function and construct
|
||||
it from log.stream();
|
||||
|
||||
@code
|
||||
|
||||
scoped_stream ss (log.stream();
|
||||
|
||||
ss << "Hello" << std::endl;
|
||||
ss << "world" << std::endl;
|
||||
|
||||
@endcode
|
||||
|
||||
Streams constructed in this fashion will not have the line
|
||||
ending automatically appended.
|
||||
|
||||
Thread safety:
|
||||
|
||||
The scoped_stream may only be used by one thread.
|
||||
Multiline output sent to the stream will be atomically
|
||||
written to the underlying abstract_Ostream
|
||||
*/
|
||||
using scoped_stream = abstract_ostream::scoped_stream_type;
|
||||
|
||||
/** Memberspace for logging. */
|
||||
log_t log;
|
||||
|
||||
/** Memberspace for declaring test cases. */
|
||||
testcase_t testcase;
|
||||
|
||||
/** Returns the "current" running suite.
|
||||
If no suite is running, nullptr is returned.
|
||||
*/
|
||||
static suite* this_suite()
|
||||
{
|
||||
return *p_this_suite();
|
||||
}
|
||||
|
||||
/** Invokes the test using the specified runner.
|
||||
Data members are set up here instead of the constructor as a
|
||||
convenience to writing the derived class to avoid repetition of
|
||||
forwarded constructor arguments to the base.
|
||||
Normally this is called by the framework for you.
|
||||
*/
|
||||
template<class = void>
|
||||
void
|
||||
operator() (runner& r);
|
||||
|
||||
/** Evaluate a test condition.
|
||||
The condition is passed as a template argument instead of `bool` so
|
||||
that implicit conversion is not required. The `reason` argument is
|
||||
logged if the condition is false.
|
||||
@return `true` if the test condition indicates success.
|
||||
*/
|
||||
template<class Condition, class String>
|
||||
bool
|
||||
expect(Condition const& shouldBeTrue,
|
||||
String const& reason);
|
||||
|
||||
template<class Condition>
|
||||
bool
|
||||
expect(Condition const& shouldBeTrue)
|
||||
{
|
||||
return expect(shouldBeTrue, "");
|
||||
}
|
||||
|
||||
/** Expect an exception from f() */
|
||||
/** @{ */
|
||||
template <class F, class String>
|
||||
bool
|
||||
except (F&& f, String const& reason);
|
||||
|
||||
template <class F>
|
||||
bool
|
||||
except (F&& f)
|
||||
{
|
||||
return except(f, "");
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Expect an exception of the given type from f() */
|
||||
/** @{ */
|
||||
template <class E, class F, class String>
|
||||
bool
|
||||
except (F&& f, String const& reason);
|
||||
|
||||
template <class E, class F>
|
||||
bool
|
||||
except (F&& f)
|
||||
{
|
||||
return except<E>(f, "");
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Fail if f() throws */
|
||||
/** @{ */
|
||||
template <class F, class String>
|
||||
bool
|
||||
unexcept (F&& f, String const& reason);
|
||||
|
||||
template <class F>
|
||||
bool
|
||||
unexcept (F&& f)
|
||||
{
|
||||
return unexcept(f, "");
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** Return the argument associated with the runner. */
|
||||
std::string const&
|
||||
arg() const
|
||||
{
|
||||
return runner_->arg();
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
// @return `true` if the test condition indicates success (a false value)
|
||||
template <class Condition, class String>
|
||||
bool
|
||||
unexpected (Condition shouldBeFalse,
|
||||
String const& reason);
|
||||
|
||||
template <class Condition>
|
||||
bool
|
||||
unexpected (Condition shouldBeFalse)
|
||||
{
|
||||
return unexpected (shouldBeFalse, "");
|
||||
}
|
||||
|
||||
/** Record a successful test condition. */
|
||||
template <class = void>
|
||||
void
|
||||
pass();
|
||||
|
||||
/** Record a failure. */
|
||||
template <class = void>
|
||||
void
|
||||
fail (std::string const& reason = "");
|
||||
|
||||
private:
|
||||
friend class thread;
|
||||
|
||||
static
|
||||
suite**
|
||||
p_this_suite()
|
||||
{
|
||||
static suite* pts = nullptr;
|
||||
return &pts;
|
||||
}
|
||||
|
||||
/** Runs the suite. */
|
||||
virtual
|
||||
void
|
||||
run() = 0;
|
||||
|
||||
void
|
||||
propagate_abort();
|
||||
|
||||
template <class = void>
|
||||
void
|
||||
run (runner& r);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
abstract_ostream::scoped_stream_type
|
||||
suite::log_t::operator<< (T const& t)
|
||||
{
|
||||
return suite_->runner_->stream() << t;
|
||||
}
|
||||
|
||||
/** Returns the raw stream used for output. */
|
||||
inline
|
||||
abstract_ostream&
|
||||
suite::log_t::stream()
|
||||
{
|
||||
return suite_->runner_->stream();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Helper for streaming testcase names
|
||||
class suite::scoped_testcase
|
||||
{
|
||||
private:
|
||||
suite* suite_;
|
||||
std::stringstream* ss_;
|
||||
|
||||
public:
|
||||
~scoped_testcase();
|
||||
|
||||
scoped_testcase (suite* s, std::stringstream* ss);
|
||||
|
||||
template <class T>
|
||||
scoped_testcase (suite* s, std::stringstream* ss, T const& t);
|
||||
|
||||
scoped_testcase& operator= (scoped_testcase const&) = delete;
|
||||
|
||||
template <class T>
|
||||
scoped_testcase&
|
||||
operator<< (T const& t);
|
||||
};
|
||||
|
||||
inline
|
||||
suite::scoped_testcase::~scoped_testcase()
|
||||
{
|
||||
auto const& name (ss_->str());
|
||||
if (! name.empty())
|
||||
suite_->runner_->testcase (name);
|
||||
}
|
||||
|
||||
inline
|
||||
suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss)
|
||||
: suite_ (s)
|
||||
, ss_ (ss)
|
||||
{
|
||||
ss_->clear();
|
||||
ss_->str({});
|
||||
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
suite::scoped_testcase::scoped_testcase (suite* s, std::stringstream* ss, T const& t)
|
||||
: suite_ (s)
|
||||
, ss_ (ss)
|
||||
{
|
||||
ss_->clear();
|
||||
ss_->str({});
|
||||
*ss_ << t;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
suite::scoped_testcase&
|
||||
suite::scoped_testcase::operator<< (T const& t)
|
||||
{
|
||||
*ss_ << t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
void
|
||||
suite::testcase_t::operator() (std::string const& name,
|
||||
abort_t abort)
|
||||
{
|
||||
suite_->abort_ = abort == abort_on_fail;
|
||||
suite_->runner_->testcase (name);
|
||||
}
|
||||
|
||||
inline
|
||||
suite::scoped_testcase
|
||||
suite::testcase_t::operator() (abort_t abort)
|
||||
{
|
||||
suite_->abort_ = abort == abort_on_fail;
|
||||
return { suite_, &ss_ };
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline
|
||||
suite::scoped_testcase
|
||||
suite::testcase_t::operator<< (T const& t)
|
||||
{
|
||||
return { suite_, &ss_, t };
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class>
|
||||
void
|
||||
suite::operator()(runner& r)
|
||||
{
|
||||
*p_this_suite() = this;
|
||||
try
|
||||
{
|
||||
run(r);
|
||||
*p_this_suite() = nullptr;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
*p_this_suite() = nullptr;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Condition, class String>
|
||||
inline
|
||||
bool
|
||||
suite::expect(Condition const& shouldBeTrue,
|
||||
String const& reason)
|
||||
{
|
||||
if(shouldBeTrue)
|
||||
{
|
||||
pass();
|
||||
return true;
|
||||
}
|
||||
fail(reason);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class F, class String>
|
||||
bool
|
||||
suite::except (F&& f, String const& reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
f();
|
||||
fail(reason);
|
||||
return false;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class E, class F, class String>
|
||||
bool
|
||||
suite::except (F&& f, String const& reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
f();
|
||||
fail(reason);
|
||||
return false;
|
||||
}
|
||||
catch(E const&)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class F, class String>
|
||||
bool
|
||||
suite::unexcept (F&& f, String const& reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
f();
|
||||
pass();
|
||||
return true;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
fail(reason);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class Condition, class String>
|
||||
inline
|
||||
bool
|
||||
suite::unexpected (Condition shouldBeFalse,
|
||||
String const& reason)
|
||||
{
|
||||
bool const b =
|
||||
static_cast<bool>(shouldBeFalse);
|
||||
if (! b)
|
||||
pass();
|
||||
else
|
||||
fail (reason);
|
||||
return ! b;
|
||||
}
|
||||
|
||||
template <class>
|
||||
void
|
||||
suite::pass()
|
||||
{
|
||||
propagate_abort();
|
||||
runner_->pass();
|
||||
}
|
||||
|
||||
template <class>
|
||||
void
|
||||
suite::fail (std::string const& reason)
|
||||
{
|
||||
propagate_abort();
|
||||
runner_->fail (reason);
|
||||
if (abort_)
|
||||
{
|
||||
aborted_ = true;
|
||||
throw abort_exception();
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
suite::propagate_abort()
|
||||
{
|
||||
if (abort_ && aborted_)
|
||||
throw abort_exception();
|
||||
}
|
||||
|
||||
template <class>
|
||||
void
|
||||
suite::run (runner& r)
|
||||
{
|
||||
runner_ = &r;
|
||||
log.suite_ = this;
|
||||
testcase.suite_ = this;
|
||||
|
||||
try
|
||||
{
|
||||
run();
|
||||
}
|
||||
catch (abort_exception const&)
|
||||
{
|
||||
// ends the suite
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
runner_->fail ("unhandled exception: " +
|
||||
std::string (e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
runner_->fail ("unhandled exception");
|
||||
}
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// detail:
|
||||
// This inserts the suite with the given manual flag
|
||||
#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \
|
||||
static beast::detail::unit_test::detail::insert_suite <Class##_test> \
|
||||
Library ## Module ## Class ## _test_instance ( \
|
||||
#Class, #Module, #Library, manual);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Preprocessor directives for controlling unit test definitions.
|
||||
|
||||
// If this is already defined, don't redefine it. This allows
|
||||
// programs to provide custom behavior for testsuite definitions
|
||||
//
|
||||
#ifndef BEAST_DEFINE_TESTSUITE
|
||||
|
||||
/** Enables insertion of test suites into the global container.
|
||||
The default is to insert all test suite definitions into the global
|
||||
container. If BEAST_DEFINE_TESTSUITE is user defined, this macro
|
||||
has no effect.
|
||||
*/
|
||||
#ifndef BEAST_NO_UNIT_TEST_INLINE
|
||||
#define BEAST_NO_UNIT_TEST_INLINE 0
|
||||
#endif
|
||||
|
||||
/** Define a unit test suite.
|
||||
|
||||
Class The type representing the class being tested.
|
||||
Module Identifies the module.
|
||||
Library Identifies the library.
|
||||
|
||||
The declaration for the class implementing the test should be the same
|
||||
as Class ## _test. For example, if Class is aged_ordered_container, the
|
||||
test class must be declared as:
|
||||
|
||||
@code
|
||||
|
||||
struct aged_ordered_container_test : beast::unit_test::suite
|
||||
{
|
||||
//...
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
The macro invocation must appear in the same namespace as the test class.
|
||||
*/
|
||||
|
||||
#if BEAST_NO_UNIT_TEST_INLINE
|
||||
#define BEAST_DEFINE_TESTSUITE(Class,Module,Library)
|
||||
|
||||
#else
|
||||
#include <beast/detail/unit_test/global_suites.hpp>
|
||||
#define BEAST_DEFINE_TESTSUITE(Class,Module,Library) \
|
||||
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,false)
|
||||
#define BEAST_DEFINE_TESTSUITE_MANUAL(Class,Module,Library) \
|
||||
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,true)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_INFO_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_SUITE_INFO_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
class runner;
|
||||
|
||||
/** Associates a unit test type with metadata. */
|
||||
class suite_info
|
||||
{
|
||||
private:
|
||||
using run_type = std::function <void (runner&)>;
|
||||
|
||||
std::string name_;
|
||||
std::string module_;
|
||||
std::string library_;
|
||||
bool m_manual;
|
||||
run_type m_run;
|
||||
|
||||
public:
|
||||
template <class = void>
|
||||
suite_info (std::string const& name, std::string const& module,
|
||||
std::string const& library, bool manual, run_type run);
|
||||
|
||||
std::string const&
|
||||
name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
std::string const&
|
||||
module() const
|
||||
{
|
||||
return module_;
|
||||
}
|
||||
|
||||
std::string const&
|
||||
library() const
|
||||
{
|
||||
return library_;
|
||||
}
|
||||
|
||||
/** Returns `true` if this suite only runs manually. */
|
||||
bool
|
||||
manual() const
|
||||
{
|
||||
return m_manual;
|
||||
}
|
||||
|
||||
/** Return the canonical suite name as a string. */
|
||||
template <class = void>
|
||||
std::string
|
||||
full_name() const;
|
||||
|
||||
/** Run a new instance of the associated test suite. */
|
||||
void
|
||||
run (runner& r) const
|
||||
{
|
||||
m_run (r);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class>
|
||||
suite_info::suite_info (std::string const& name, std::string const& module,
|
||||
std::string const& library, bool manual, run_type run)
|
||||
: name_ (name)
|
||||
, module_ (module)
|
||||
, library_ (library)
|
||||
, m_manual (manual)
|
||||
, m_run (std::move (run))
|
||||
{
|
||||
}
|
||||
|
||||
template <class>
|
||||
std::string
|
||||
suite_info::full_name() const
|
||||
{
|
||||
return library_ + "." + module_ + "." + name_;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
operator< (suite_info const& lhs, suite_info const& rhs)
|
||||
{
|
||||
return lhs.full_name() < rhs.full_name();
|
||||
}
|
||||
|
||||
/** Convenience for producing suite_info for a given test type. */
|
||||
template <class Suite>
|
||||
suite_info
|
||||
make_suite_info (std::string const& name, std::string const& module,
|
||||
std::string const& library, bool manual)
|
||||
{
|
||||
return suite_info(name, module, library, manual,
|
||||
[](runner& r) { return Suite{}(r); });
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_LIST_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_SUITE_LIST_HPP
|
||||
|
||||
#include <beast/detail/unit_test/suite_info.hpp>
|
||||
#include <beast/detail/const_container.hpp>
|
||||
#include <cassert>
|
||||
#include <typeindex>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
/** A container of test suites. */
|
||||
class suite_list
|
||||
: public const_container <std::set <suite_info>>
|
||||
{
|
||||
private:
|
||||
#ifndef NDEBUG
|
||||
std::unordered_set <std::string> names_;
|
||||
std::unordered_set <std::type_index> classes_;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/** Insert a suite into the set.
|
||||
The suite must not already exist.
|
||||
*/
|
||||
template <class Suite>
|
||||
void
|
||||
insert (char const* name, char const* module, char const* library,
|
||||
bool manual);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class Suite>
|
||||
void
|
||||
suite_list::insert (char const* name, char const* module, char const* library,
|
||||
bool manual)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
std::string s;
|
||||
s = std::string(library) + "." + module + "." + name;
|
||||
auto const result (names_.insert(s));
|
||||
assert (result.second); // Duplicate name
|
||||
}
|
||||
|
||||
{
|
||||
auto const result (classes_.insert (
|
||||
std::type_index (typeid(Suite))));
|
||||
assert (result.second); // Duplicate type
|
||||
}
|
||||
#endif
|
||||
|
||||
cont().emplace (std::move (make_suite_info <Suite> (
|
||||
name, module, library, manual)));
|
||||
}
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_UNIT_TEST_THREAD_HPP
|
||||
#define BEAST_DETAIL_UNIT_TEST_THREAD_HPP
|
||||
|
||||
#include <beast/detail/unit_test/suite.hpp>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
namespace unit_test {
|
||||
|
||||
/** Replacement for std::thread that handles exceptions in unit tests. */
|
||||
class thread
|
||||
{
|
||||
private:
|
||||
suite* s_ = nullptr;
|
||||
std::thread t_;
|
||||
|
||||
public:
|
||||
using id = std::thread::id;
|
||||
using native_handle_type = std::thread::native_handle_type;
|
||||
|
||||
thread() = default;
|
||||
thread (thread const&) = delete;
|
||||
thread& operator= (thread const&) = delete;
|
||||
|
||||
thread (thread&& other)
|
||||
: s_ (other.s_)
|
||||
, t_ (std::move(other.t_))
|
||||
{
|
||||
}
|
||||
|
||||
thread& operator= (thread&& other)
|
||||
{
|
||||
s_ = other.s_;
|
||||
t_ = std::move(other.t_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class F, class... Args>
|
||||
explicit
|
||||
thread (suite& s, F&& f, Args&&... args)
|
||||
: s_ (&s)
|
||||
{
|
||||
std::function<void(void)> b =
|
||||
std::bind(std::forward<F>(f),
|
||||
std::forward<Args>(args)...);
|
||||
t_ = std::thread (&thread::run, this,
|
||||
std::move(b));
|
||||
}
|
||||
|
||||
bool
|
||||
joinable() const
|
||||
{
|
||||
return t_.joinable();
|
||||
}
|
||||
|
||||
std::thread::id
|
||||
get_id() const
|
||||
{
|
||||
return t_.get_id();
|
||||
}
|
||||
|
||||
static
|
||||
unsigned
|
||||
hardware_concurrency() noexcept
|
||||
{
|
||||
return std::thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
void
|
||||
join()
|
||||
{
|
||||
t_.join();
|
||||
s_->propagate_abort();
|
||||
}
|
||||
|
||||
void
|
||||
detach()
|
||||
{
|
||||
t_.detach();
|
||||
}
|
||||
|
||||
void
|
||||
swap (thread& other)
|
||||
{
|
||||
std::swap(s_, other.s_);
|
||||
std::swap(t_, other.t_);
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
run (std::function <void(void)> f)
|
||||
{
|
||||
try
|
||||
{
|
||||
f();
|
||||
}
|
||||
catch (suite::abort_exception const&)
|
||||
{
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
s_->fail ("unhandled exception: " +
|
||||
std::string (e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
s_->fail ("unhandled exception");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // unit_test
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_WRITE_STREAMBUF_HPP
|
||||
#define BEAST_DETAIL_WRITE_STREAMBUF_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// detects string literals.
|
||||
template<class T>
|
||||
struct is_string_literal : std::integral_constant<bool,
|
||||
! std::is_same<T, typename std::remove_extent<T>::type>::value &&
|
||||
std::is_same<char, typename std::remove_extent<T>::type>::value>
|
||||
{
|
||||
};
|
||||
|
||||
// `true` if a call to boost::asio::buffer(T const&) is possible
|
||||
// note: we exclude string literals because boost::asio::buffer()
|
||||
// will include the null terminator, which we don't want.
|
||||
template<class T>
|
||||
class is_BufferConvertible
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
boost::asio::buffer(std::declval<U const&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
static bool const value = type::value &&
|
||||
! is_string_literal<T>::value;
|
||||
};
|
||||
|
||||
template<class Streambuf>
|
||||
inline
|
||||
void
|
||||
write_streambuf(Streambuf&)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_streambuf(Streambuf& streambuf,
|
||||
boost::asio::const_buffer const& buffer)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
streambuf.commit(buffer_copy(
|
||||
streambuf.prepare(buffer_size(buffer)),
|
||||
buffer));
|
||||
}
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_streambuf(Streambuf& streambuf,
|
||||
boost::asio::mutable_buffer const& buffer)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
streambuf.commit(buffer_copy(
|
||||
streambuf.prepare(buffer_size(buffer)),
|
||||
buffer));
|
||||
}
|
||||
|
||||
template<class Streambuf, class T>
|
||||
typename std::enable_if<
|
||||
is_BufferConvertible<T>::value &&
|
||||
! std::is_convertible<T, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<T, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_streambuf(Streambuf& streambuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const buffers = boost::asio::buffer(t);
|
||||
streambuf.commit(buffer_copy(
|
||||
streambuf.prepare(buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
|
||||
template<class Streambuf, class Buffers>
|
||||
typename std::enable_if<
|
||||
is_ConstBufferSequence<Buffers>::value &&
|
||||
! is_BufferConvertible<Buffers>::value &&
|
||||
! std::is_convertible<Buffers, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_streambuf(Streambuf& streambuf, Buffers const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
streambuf.commit(buffer_copy(
|
||||
streambuf.prepare(buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
|
||||
template<class Streambuf, std::size_t N>
|
||||
void
|
||||
write_streambuf(Streambuf& streambuf, const char (&s)[N])
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
streambuf.commit(buffer_copy(
|
||||
streambuf.prepare(N - 1),
|
||||
boost::asio::buffer(s, N - 1)));
|
||||
}
|
||||
|
||||
template<class Streambuf, class T>
|
||||
typename std::enable_if<
|
||||
! is_string_literal<T>::value &&
|
||||
! is_ConstBufferSequence<T>::value &&
|
||||
! is_BufferConvertible<T>::value &&
|
||||
! std::is_convertible<T, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<T, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_streambuf(Streambuf& streambuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const s = boost::lexical_cast<std::string>(t);
|
||||
streambuf.commit(buffer_copy(
|
||||
streambuf.prepare(s.size()), buffer(s)));
|
||||
}
|
||||
|
||||
template<class Streambuf, class T0, class T1, class... TN>
|
||||
void
|
||||
write_streambuf(Streambuf& streambuf, T0&& t0, T1&& t1, TN... tn)
|
||||
{
|
||||
write_streambuf(streambuf, std::forward<T0>(t0));
|
||||
write_streambuf(streambuf, std::forward<T1>(t1));
|
||||
write_streambuf(streambuf, std::forward<TN>(tn)...);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_ALLOC_HPP
|
||||
#define BEAST_HANDLER_ALLOC_HPP
|
||||
|
||||
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
// Guidance from
|
||||
// http://howardhinnant.github.io/allocator_boilerplate.html
|
||||
|
||||
/** An allocator that uses handler customizations.
|
||||
|
||||
This allocator uses the handler customizations `asio_handler_allocate`
|
||||
and `asio_handler_deallocate` to manage memory. It meets the requirements
|
||||
of `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 Allocated memory is only valid until the handler is called. The
|
||||
caller is still responsible for freeing memory.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template <class T, class Handler>
|
||||
class handler_alloc;
|
||||
#else
|
||||
template <class T, class Handler>
|
||||
class handler_alloc
|
||||
{
|
||||
private:
|
||||
// We want a partial template specialization as a friend
|
||||
// but that isn't allowed so we friend all versions. This
|
||||
// should produce a compile error if Handler is not
|
||||
// constructible from H.
|
||||
//
|
||||
template <class U, class H>
|
||||
friend class handler_alloc;
|
||||
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using is_always_equal = std::true_type;
|
||||
|
||||
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.
|
||||
|
||||
The handler is moved or copied into the allocator.
|
||||
*/
|
||||
explicit
|
||||
handler_alloc(Handler&& h)
|
||||
: h_(std::move(h))
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct the allocator.
|
||||
|
||||
A copy of the handler is made.
|
||||
*/
|
||||
explicit
|
||||
handler_alloc(Handler const& h)
|
||||
: h_(h)
|
||||
{
|
||||
}
|
||||
|
||||
template<class U>
|
||||
handler_alloc(
|
||||
handler_alloc<U, Handler>&& other)
|
||||
: h_(std::move(other.h_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class U>
|
||||
handler_alloc(
|
||||
handler_alloc<U, Handler> const& other)
|
||||
: h_(other.h_)
|
||||
{
|
||||
}
|
||||
|
||||
value_type*
|
||||
allocate(std::ptrdiff_t n)
|
||||
{
|
||||
auto const size = n * sizeof(T);
|
||||
return static_cast<value_type*>(
|
||||
boost_asio_handler_alloc_helpers::allocate(
|
||||
size, h_));
|
||||
}
|
||||
|
||||
void
|
||||
deallocate(value_type* p, std::ptrdiff_t n)
|
||||
{
|
||||
auto const size = n * sizeof(T);
|
||||
boost_asio_handler_alloc_helpers::deallocate(
|
||||
p, size, h_);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Work-around for MSVC not using allocator_traits
|
||||
// in the implementation of shared_ptr
|
||||
//
|
||||
void
|
||||
destroy(T* t)
|
||||
{
|
||||
t->~T();
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class U>
|
||||
friend
|
||||
bool
|
||||
operator==(handler_alloc const& lhs,
|
||||
handler_alloc<U, Handler> const& rhs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class U>
|
||||
friend
|
||||
bool
|
||||
operator!=(handler_alloc const& lhs,
|
||||
handler_alloc<U, Handler> const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_HPP_INCLUDED
|
||||
#define BEAST_HTTP_HPP_INCLUDED
|
||||
|
||||
#include <beast/http/basic_headers.hpp>
|
||||
#include <beast/http/basic_parser.hpp>
|
||||
#include <beast/http/chunk_encode.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/headers.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/reason.hpp>
|
||||
#include <beast/http/resume_context.hpp>
|
||||
#include <beast/http/rfc2616.hpp>
|
||||
#include <beast/http/streambuf_body.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,434 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_HEADERS_HPP
|
||||
#define BEAST_HTTP_BASIC_HEADERS_HPP
|
||||
|
||||
#include <beast/type_check.hpp>
|
||||
#include <beast/detail/ci_char_traits.hpp>
|
||||
#include <beast/detail/empty_base_optimization.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Allocator>
|
||||
class basic_headers;
|
||||
|
||||
namespace detail {
|
||||
|
||||
class basic_headers_base
|
||||
{
|
||||
public:
|
||||
struct value_type
|
||||
{
|
||||
std::string first;
|
||||
std::string second;
|
||||
|
||||
value_type(boost::string_ref const& name_,
|
||||
boost::string_ref const& value_)
|
||||
: first(name_)
|
||||
, second(value_)
|
||||
{
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
name() const
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
value() const
|
||||
{
|
||||
return second;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_headers;
|
||||
|
||||
struct element
|
||||
: boost::intrusive::set_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
, boost::intrusive::list_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
{
|
||||
value_type data;
|
||||
|
||||
element(boost::string_ref const& name,
|
||||
boost::string_ref const& value)
|
||||
: data(name, value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct less : private beast::detail::ci_less
|
||||
{
|
||||
template<class String>
|
||||
bool
|
||||
operator()(String const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs, rhs.data.first);
|
||||
}
|
||||
|
||||
template<class String>
|
||||
bool
|
||||
operator()(element const& lhs, String const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs.data.first, rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(
|
||||
lhs.data.first, rhs.data.first);
|
||||
}
|
||||
};
|
||||
|
||||
using list_t = typename boost::intrusive::make_list<
|
||||
element, boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using set_t = typename boost::intrusive::make_set<
|
||||
element, boost::intrusive::constant_time_size<true>,
|
||||
boost::intrusive::compare<less>>::type;
|
||||
|
||||
// data
|
||||
set_t set_;
|
||||
list_t list_;
|
||||
|
||||
basic_headers_base(set_t&& set, list_t&& list)
|
||||
: set_(std::move(set))
|
||||
, list_(std::move(list))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
class const_iterator;
|
||||
|
||||
using iterator = const_iterator;
|
||||
|
||||
basic_headers_base() = default;
|
||||
|
||||
/// Returns an iterator to the beginning of the field sequence.
|
||||
iterator
|
||||
begin() const;
|
||||
|
||||
/// Returns an iterator to the end of the field sequence.
|
||||
iterator
|
||||
end() const;
|
||||
|
||||
/// Returns an iterator to the beginning of the field sequence.
|
||||
iterator
|
||||
cbegin() const;
|
||||
|
||||
/// Returns an iterator to the end of the field sequence.
|
||||
iterator
|
||||
cend() const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class basic_headers_base::const_iterator
|
||||
{
|
||||
using iter_type = list_t::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_headers;
|
||||
|
||||
friend class basic_headers_base;
|
||||
|
||||
const_iterator(iter_type it)
|
||||
: it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename basic_headers_base::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return it_->data;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Container to store HTTP headers.
|
||||
|
||||
Meets the requirements of `FieldSequence`.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_headers
|
||||
#if ! GENERATING_DOCS
|
||||
: private beast::detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_headers_base::element>>
|
||||
, public detail::basic_headers_base
|
||||
#endif
|
||||
{
|
||||
using alloc_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_headers_base::element>;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<alloc_type>;
|
||||
|
||||
using size_type =
|
||||
typename std::allocator_traits<Allocator>::size_type;
|
||||
|
||||
void
|
||||
delete_all();
|
||||
|
||||
void
|
||||
move_assign(basic_headers&, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_headers&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_headers const&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_headers const&, std::true_type);
|
||||
|
||||
template<class FieldSequence>
|
||||
void
|
||||
copy_from(FieldSequence const& fs)
|
||||
{
|
||||
for(auto const& e : fs)
|
||||
insert(e.first, e.second);
|
||||
}
|
||||
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
|
||||
/// Default constructor.
|
||||
basic_headers() = default;
|
||||
|
||||
/// Destructor
|
||||
~basic_headers();
|
||||
|
||||
/** Construct the headers.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_headers(Allocator const& alloc);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_headers(basic_headers&& other);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_headers& operator=(basic_headers&& other);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_headers(basic_headers const&);
|
||||
|
||||
/// Copy assignment.
|
||||
basic_headers& operator=(basic_headers const&);
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherAlloc>
|
||||
basic_headers(basic_headers<OtherAlloc> const&);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherAlloc>
|
||||
basic_headers& operator=(basic_headers<OtherAlloc> const&);
|
||||
|
||||
/// Construct from a field sequence.
|
||||
template<class FwdIt>
|
||||
basic_headers(FwdIt first, FwdIt last);
|
||||
|
||||
/// Returns `true` if the field sequence contains no elements.
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return set_.empty();
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the field sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return set_.size();
|
||||
}
|
||||
|
||||
/** Returns `true` if the specified field exists. */
|
||||
bool
|
||||
exists(boost::string_ref const& name) const
|
||||
{
|
||||
return set_.find(name, less{}) != set_.end();
|
||||
}
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching header. */
|
||||
iterator
|
||||
find(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or "" */
|
||||
boost::string_ref
|
||||
operator[](boost::string_ref const& name) const;
|
||||
|
||||
/** Clear the contents of the basic_headers. */
|
||||
void
|
||||
clear() noexcept;
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(boost::string_ref const& name);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field value already exists the new value will be
|
||||
extended as per RFC2616 Section 4.2.
|
||||
*/
|
||||
// VFALCO TODO Consider allowing rvalue references for std::move?
|
||||
void
|
||||
insert(boost::string_ref const& name,
|
||||
boost::string_ref const& value);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field value already exists the new value will be
|
||||
extended as per RFC2616 Section 4.2.
|
||||
*/
|
||||
template<class T,
|
||||
class = typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type>
|
||||
void
|
||||
insert(boost::string_ref name, T const& value)
|
||||
{
|
||||
insert(name,
|
||||
boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
The current field value, if any, is removed. Then the
|
||||
specified value is inserted as if by insert(field, value).
|
||||
*/
|
||||
void
|
||||
replace(boost::string_ref const& name,
|
||||
boost::string_ref const& value);
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
The current field value, if any, is removed. Then the
|
||||
specified value is inserted as if by insert(field, value).
|
||||
*/
|
||||
template<class T,
|
||||
class = typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type>
|
||||
void
|
||||
replace(boost::string_ref const& name, T const& value)
|
||||
{
|
||||
replace(name,
|
||||
boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_headers.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,761 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_PARSER_HPP
|
||||
#define BEAST_HTTP_BASIC_PARSER_HPP
|
||||
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/detail/basic_parser.hpp>
|
||||
#include <beast/type_check.hpp>
|
||||
#include <beast/detail/ci_char_traits.hpp>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace parse_flag {
|
||||
enum values
|
||||
{
|
||||
chunked = 1 << 0,
|
||||
connection_keep_alive = 1 << 1,
|
||||
connection_close = 1 << 2,
|
||||
connection_upgrade = 1 << 3,
|
||||
trailing = 1 << 4,
|
||||
upgrade = 1 << 5,
|
||||
skipbody = 1 << 6,
|
||||
contentlength = 1 << 7
|
||||
};
|
||||
} // parse_flag
|
||||
|
||||
/** Parser for producing HTTP requests and responses.
|
||||
|
||||
During parsing, callbacks will be made to the derived class
|
||||
if those members are present (detected through SFINAE). The
|
||||
signatures which can be present in the derived class are:<br>
|
||||
|
||||
@li `void on_method(boost::string_ref const&, error_code& ec)`
|
||||
|
||||
Called for each piece of the Request-Method
|
||||
|
||||
@li `void on_uri(boost::string_ref const&, error_code& ec)`
|
||||
|
||||
Called for each piece of the Request-URI
|
||||
|
||||
@li `void on_reason(boost::string_ref const&, error_code& ec)`
|
||||
|
||||
Called for each piece of the reason-phrase
|
||||
|
||||
@li `void on_request(error_code& ec)`
|
||||
|
||||
Called after the entire Request-Line has been parsed successfully.
|
||||
|
||||
@li `void on_response(error_code& ec)`
|
||||
|
||||
Called after the entire Response-Line has been parsed successfully.
|
||||
|
||||
@li `void on_field(boost::string_ref const&, error_code& ec)`
|
||||
|
||||
Called for each piece of the current header field.
|
||||
|
||||
@li `void on_value(boost::string_ref const&, error_code& ec)`
|
||||
|
||||
Called for each piece of the current header value.
|
||||
|
||||
@li `int on_headers(error_code& ec)`
|
||||
|
||||
Called when all the headers have been parsed successfully.
|
||||
|
||||
@li `void on_body(boost::string_ref const&, error_code& ec)`
|
||||
|
||||
Called for each piece of the body. If the headers indicated
|
||||
chunked encoding, the chunk encoding is removed from the
|
||||
buffer before being passed to the callback.
|
||||
|
||||
@li `void on_complete(error_code& ec)`
|
||||
|
||||
Called when the entire message has been parsed successfully.
|
||||
At this point, basic_parser::complete() returns `true`, and
|
||||
the parser is ready to parse another message if keep_alive()
|
||||
would return `true`.
|
||||
|
||||
The return value of `on_headers` is special, it controls whether
|
||||
or not the parser should expect a body. These are the return values:
|
||||
|
||||
@li *0* The parser should expect a body
|
||||
|
||||
@li *1* The parser should skip the body. For example, this is
|
||||
used when sending a response to a HEAD request.
|
||||
|
||||
@li *2* The parser should skip ths body, this is an
|
||||
upgrade to a different protocol.
|
||||
|
||||
The parser uses traits to determine if the callback is possible.
|
||||
If the Derived type omits one or more callbacks, they are simply
|
||||
skipped with no compilation error. The default behavior of on_body
|
||||
when the derived class does not provide the member, is to specify that
|
||||
the body should not be skipped.
|
||||
|
||||
If a callback sets an error, parsing stops at the current octet
|
||||
and the error is returned to the caller.
|
||||
*/
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser
|
||||
{
|
||||
private:
|
||||
using self = basic_parser;
|
||||
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||
|
||||
static std::uint64_t constexpr no_content_length =
|
||||
std::numeric_limits<std::uint64_t>::max();
|
||||
|
||||
enum state : std::uint8_t
|
||||
{
|
||||
s_closed = 1,
|
||||
|
||||
s_req_start,
|
||||
s_req_method_start,
|
||||
s_req_method,
|
||||
s_req_space_before_url,
|
||||
s_req_url_start,
|
||||
s_req_url,
|
||||
s_req_http_start,
|
||||
s_req_http_H,
|
||||
s_req_http_HT,
|
||||
s_req_http_HTT,
|
||||
s_req_http_HTTP,
|
||||
s_req_major_start,
|
||||
s_req_major,
|
||||
s_req_minor_start,
|
||||
s_req_minor,
|
||||
s_req_line_end,
|
||||
|
||||
s_res_start,
|
||||
s_res_H,
|
||||
s_res_HT,
|
||||
s_res_HTT,
|
||||
s_res_HTTP,
|
||||
s_res_major_start,
|
||||
s_res_major,
|
||||
s_res_minor_start,
|
||||
s_res_minor,
|
||||
s_res_status_code_start,
|
||||
s_res_status_code,
|
||||
s_res_status_start,
|
||||
s_res_status,
|
||||
s_res_line_almost_done,
|
||||
s_res_line_done,
|
||||
|
||||
s_header_field_start,
|
||||
s_header_field,
|
||||
s_header_value_start,
|
||||
s_header_value_discard_lWs0,
|
||||
s_header_value_discard_ws0,
|
||||
s_header_value_almost_done0,
|
||||
s_header_value_text_start,
|
||||
s_header_value_discard_lWs,
|
||||
s_header_value_discard_ws,
|
||||
s_header_value_text,
|
||||
s_header_value_almost_done,
|
||||
|
||||
s_headers_almost_done,
|
||||
s_headers_done,
|
||||
|
||||
s_chunk_size_start,
|
||||
s_chunk_size,
|
||||
s_chunk_parameters,
|
||||
s_chunk_size_almost_done,
|
||||
|
||||
// states below do not count towards
|
||||
// the limit on the size of the message
|
||||
|
||||
s_body_identity0,
|
||||
s_body_identity,
|
||||
s_body_identity_eof0,
|
||||
s_body_identity_eof,
|
||||
|
||||
s_chunk_data_start,
|
||||
s_chunk_data,
|
||||
s_chunk_data_almost_done,
|
||||
s_chunk_data_done,
|
||||
|
||||
s_complete,
|
||||
s_restart
|
||||
};
|
||||
|
||||
enum field_state : std::uint8_t
|
||||
{
|
||||
h_general = 0,
|
||||
h_C,
|
||||
h_CO,
|
||||
h_CON,
|
||||
|
||||
h_matching_connection,
|
||||
h_matching_proxy_connection,
|
||||
h_matching_content_length,
|
||||
h_matching_transfer_encoding,
|
||||
h_matching_upgrade,
|
||||
|
||||
h_connection,
|
||||
h_content_length,
|
||||
h_transfer_encoding,
|
||||
h_upgrade,
|
||||
|
||||
h_matching_transfer_encoding_chunked,
|
||||
h_matching_connection_token_start,
|
||||
h_matching_connection_keep_alive,
|
||||
h_matching_connection_close,
|
||||
h_matching_connection_upgrade,
|
||||
h_matching_connection_token,
|
||||
|
||||
h_transfer_encoding_chunked,
|
||||
h_connection_keep_alive,
|
||||
h_connection_close,
|
||||
h_connection_upgrade,
|
||||
};
|
||||
|
||||
std::uint64_t content_length_;
|
||||
std::uint64_t nread_;
|
||||
pmf_t cb_;
|
||||
state s_ : 8;
|
||||
unsigned flags_ : 8;
|
||||
unsigned fs_ : 8;
|
||||
unsigned pos_ : 8; // position in field state
|
||||
unsigned http_major_ : 16;
|
||||
unsigned http_minor_ : 16;
|
||||
unsigned status_code_ : 16;
|
||||
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||
|
||||
public:
|
||||
/// Copy constructor.
|
||||
basic_parser(basic_parser const&) = default;
|
||||
|
||||
/// Copy assignment.
|
||||
basic_parser& operator=(basic_parser const&) = default;
|
||||
|
||||
/// Constructor
|
||||
basic_parser()
|
||||
{
|
||||
init(std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
/// Returns internal flags associated with the parser.
|
||||
unsigned
|
||||
flags() const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message end is indicated by eof.
|
||||
|
||||
This function returns true if the semantics of the message require
|
||||
that the end of the message is signaled by an end of file. For
|
||||
example, if the message is a HTTP/1.0 message and the Content-Length
|
||||
is unspecified, the end of the message is indicated by an end of file.
|
||||
|
||||
@return `true` if write_eof must be used to indicate the message end.
|
||||
*/
|
||||
bool
|
||||
needs_eof() const
|
||||
{
|
||||
return needs_eof(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
/** Returns the major HTTP version number.
|
||||
|
||||
Examples:
|
||||
* Returns 1 for HTTP/1.1
|
||||
* Returns 1 for HTTP/1.0
|
||||
|
||||
@return The HTTP major version number.
|
||||
*/
|
||||
unsigned
|
||||
http_major() const
|
||||
{
|
||||
return http_major_;
|
||||
}
|
||||
|
||||
/** Returns the minor HTTP version number.
|
||||
|
||||
Examples:
|
||||
* Returns 1 for HTTP/1.1
|
||||
* Returns 0 for HTTP/1.0
|
||||
|
||||
@return The HTTP minor version number.
|
||||
*/
|
||||
unsigned
|
||||
http_minor() const
|
||||
{
|
||||
return http_minor_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
A value of `true` indicates that the parser has successfully
|
||||
completed parsing a HTTP upgrade message.
|
||||
|
||||
@return `true` if the message is an upgrade message.
|
||||
*/
|
||||
bool
|
||||
upgrade() const
|
||||
{
|
||||
return upgrade_;
|
||||
}
|
||||
|
||||
/** Returns the numeric HTTP Status-Code of a response.
|
||||
|
||||
@return The Status-Code.
|
||||
*/
|
||||
unsigned
|
||||
status_code() const
|
||||
{
|
||||
return status_code_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the connection should be kept open.
|
||||
|
||||
@note This function is only valid to call when the parser
|
||||
is complete.
|
||||
*/
|
||||
bool
|
||||
keep_alive() const;
|
||||
|
||||
/** Returns `true` if the parse has completed succesfully.
|
||||
|
||||
When the parse has completed successfully, and the semantics
|
||||
of the parsed message indicate that the connection is still
|
||||
active, a subsequent call to `write` will begin parsing a
|
||||
new message.
|
||||
|
||||
@return `true` If the parsing has completed successfully.
|
||||
*/
|
||||
bool
|
||||
complete() const
|
||||
{
|
||||
return s_ == s_restart;
|
||||
}
|
||||
|
||||
/** Write data to the parser.
|
||||
|
||||
@param buffers An object meeting the requirements of
|
||||
ConstBufferSequence that represents the input sequence.
|
||||
|
||||
@param ec Set to the error, if any error occurred.
|
||||
|
||||
@return The number of bytes consumed in the input sequence.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
/** Write data to the parser.
|
||||
|
||||
@param data A pointer to a buffer representing the input sequence.
|
||||
@param size The number of bytes in the buffer pointed to by data.
|
||||
@param ec Set to the error, if any error occurred.
|
||||
|
||||
@return The number of bytes consumed in the input sequence.
|
||||
*/
|
||||
std::size_t
|
||||
write(void const* data, std::size_t size, error_code& ec);
|
||||
|
||||
/** Called to indicate the end of file.
|
||||
|
||||
HTTP needs to know where the end of the stream is. For example,
|
||||
sometimes servers send responses without Content-Length and
|
||||
expect the client to consume input (for the body) until EOF.
|
||||
Callbacks and errors will still be processed as usual.
|
||||
|
||||
@note This is typically called when a socket read returns eof.
|
||||
|
||||
@throws boost::system::system_error Thrown on failure.
|
||||
*/
|
||||
void
|
||||
write_eof(error_code& ec);
|
||||
|
||||
private:
|
||||
Derived&
|
||||
impl()
|
||||
{
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
void
|
||||
init(std::true_type)
|
||||
{
|
||||
s_ = s_req_start;
|
||||
}
|
||||
|
||||
void
|
||||
init(std::false_type)
|
||||
{
|
||||
s_ = s_res_start;
|
||||
}
|
||||
|
||||
bool
|
||||
needs_eof(std::true_type) const;
|
||||
|
||||
bool
|
||||
needs_eof(std::false_type) const;
|
||||
|
||||
template<class C>
|
||||
class has_on_method_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_method(
|
||||
std::declval<boost::string_ref const&>(),
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_method =
|
||||
std::integral_constant<bool, has_on_method_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_uri_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_uri(
|
||||
std::declval<boost::string_ref const&>(),
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_uri =
|
||||
std::integral_constant<bool, has_on_uri_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_reason_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_reason(
|
||||
std::declval<boost::string_ref const&>(),
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_reason =
|
||||
std::integral_constant<bool, has_on_reason_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_request_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_request(
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_request =
|
||||
std::integral_constant<bool, has_on_request_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_response_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_response(
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_response =
|
||||
std::integral_constant<bool, has_on_response_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_field_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_uri(
|
||||
std::declval<boost::string_ref const&>(),
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_field =
|
||||
std::integral_constant<bool, has_on_field_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_value_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_uri(
|
||||
std::declval<boost::string_ref const&>(),
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_value =
|
||||
std::integral_constant<bool, has_on_value_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_headers_t
|
||||
{
|
||||
template<class T, class R = std::is_same<int,
|
||||
decltype(std::declval<T>().on_headers(
|
||||
std::declval<error_code&>()))>>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_headers =
|
||||
std::integral_constant<bool, has_on_headers_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_body_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_body(
|
||||
std::declval<boost::string_ref const&>(),
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_body =
|
||||
std::integral_constant<bool, has_on_body_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_complete_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_complete(
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using has_on_complete =
|
||||
std::integral_constant<bool, has_on_complete_t<C>::value>;
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
impl().on_method(s, ec);
|
||||
}
|
||||
|
||||
void call_on_method(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
call_on_method(ec, s, std::integral_constant<bool,
|
||||
isRequest && has_on_method<Derived>::value>{});
|
||||
}
|
||||
|
||||
void call_on_uri(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
impl().on_uri(s, ec);
|
||||
}
|
||||
|
||||
void call_on_uri(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_uri(error_code& ec, boost::string_ref const& s)
|
||||
{
|
||||
call_on_uri(ec, s, std::integral_constant<bool,
|
||||
isRequest && has_on_uri<Derived>::value>{});
|
||||
}
|
||||
|
||||
void call_on_reason(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
impl().on_reason(s, ec);
|
||||
}
|
||||
|
||||
void call_on_reason(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
||||
{
|
||||
call_on_reason(ec, s, std::integral_constant<bool,
|
||||
! isRequest && has_on_reason<Derived>::value>{});
|
||||
}
|
||||
|
||||
void call_on_request(error_code& ec, std::true_type)
|
||||
{
|
||||
impl().on_request(ec);
|
||||
}
|
||||
|
||||
void call_on_request(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_request(error_code& ec)
|
||||
{
|
||||
call_on_request(ec, std::integral_constant<bool,
|
||||
isRequest && has_on_request<Derived>::value>{});
|
||||
}
|
||||
|
||||
void call_on_response(error_code& ec, std::true_type)
|
||||
{
|
||||
impl().on_response(ec);
|
||||
}
|
||||
|
||||
void call_on_response(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_response(error_code& ec)
|
||||
{
|
||||
call_on_response(ec, std::integral_constant<bool,
|
||||
! isRequest && has_on_response<Derived>::value>{});
|
||||
}
|
||||
|
||||
void call_on_field(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
impl().on_field(s, ec);
|
||||
}
|
||||
|
||||
void call_on_field(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_field(error_code& ec, boost::string_ref const& s)
|
||||
{
|
||||
call_on_field(ec, s, has_on_field<Derived>{});
|
||||
}
|
||||
|
||||
void call_on_value(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
impl().on_value(s, ec);
|
||||
}
|
||||
|
||||
void call_on_value(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_value(error_code& ec, boost::string_ref const& s)
|
||||
{
|
||||
call_on_value(ec, s, has_on_value<Derived>{});
|
||||
}
|
||||
|
||||
int call_on_headers(error_code& ec, std::true_type)
|
||||
{
|
||||
return impl().on_headers(ec);
|
||||
}
|
||||
|
||||
int call_on_headers(error_code& ec, std::false_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int call_on_headers(error_code& ec)
|
||||
{
|
||||
return call_on_headers(ec, has_on_headers<Derived>{});
|
||||
}
|
||||
|
||||
void call_on_body(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
impl().on_body(s, ec);
|
||||
}
|
||||
|
||||
void call_on_body(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_body(error_code& ec, boost::string_ref const& s)
|
||||
{
|
||||
call_on_body(ec, s, has_on_body<Derived>{});
|
||||
}
|
||||
|
||||
void call_on_complete(error_code& ec, std::true_type)
|
||||
{
|
||||
impl().on_complete(ec);
|
||||
}
|
||||
|
||||
void call_on_complete(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_complete(error_code& ec)
|
||||
{
|
||||
call_on_complete(ec, has_on_complete<Derived>{});
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_parser.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
#define BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class Buffers>
|
||||
class chunk_encoded_buffers
|
||||
{
|
||||
private:
|
||||
using const_buffer = boost::asio::const_buffer;
|
||||
|
||||
Buffers buffers_;
|
||||
const_buffer head_;
|
||||
const_buffer tail_;
|
||||
|
||||
// Storage for the longest hex string we might need, plus delimiters.
|
||||
std::array<char, 2 * sizeof(std::size_t) + 2> data_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
chunk_encoded_buffers() = delete;
|
||||
chunk_encoded_buffers (chunk_encoded_buffers const&) = default;
|
||||
chunk_encoded_buffers& operator= (chunk_encoded_buffers const&) = default;
|
||||
|
||||
chunk_encoded_buffers (Buffers const& buffers, bool final_chunk);
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return const_iterator(*this, false);
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return const_iterator(*this, true);
|
||||
}
|
||||
|
||||
private:
|
||||
// Unchecked conversion of unsigned to hex string
|
||||
template<class OutIter, class Unsigned>
|
||||
static
|
||||
typename std::enable_if<
|
||||
std::is_unsigned<Unsigned>::value, OutIter>::type
|
||||
to_hex(OutIter const first, OutIter const last, Unsigned n);
|
||||
};
|
||||
|
||||
template <class Buffers>
|
||||
class chunk_encoded_buffers<Buffers>::const_iterator
|
||||
: public std::iterator<std::bidirectional_iterator_tag, const_buffer>
|
||||
{
|
||||
private:
|
||||
using iterator = typename Buffers::const_iterator;
|
||||
enum class Where { head, input, end };
|
||||
chunk_encoded_buffers const* buffers_;
|
||||
Where where_;
|
||||
iterator iter_;
|
||||
|
||||
public:
|
||||
const_iterator();
|
||||
const_iterator (const_iterator const&) = default;
|
||||
const_iterator& operator= (const_iterator const&) = default;
|
||||
bool operator== (const_iterator const& other) const;
|
||||
bool operator!= (const_iterator const& other) const;
|
||||
const_iterator& operator++();
|
||||
const_iterator& operator--();
|
||||
const_iterator operator++(int) const;
|
||||
const_iterator operator--(int) const;
|
||||
const_buffer operator*() const;
|
||||
|
||||
private:
|
||||
friend class chunk_encoded_buffers;
|
||||
const_iterator(chunk_encoded_buffers const& buffers, bool past_the_end);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class Buffers>
|
||||
chunk_encoded_buffers<Buffers>::chunk_encoded_buffers (
|
||||
Buffers const& buffers, bool final_chunk)
|
||||
: buffers_(buffers)
|
||||
{
|
||||
auto const size = boost::asio::buffer_size(buffers);
|
||||
data_[data_.size() - 2] = '\r';
|
||||
data_[data_.size() - 1] = '\n';
|
||||
auto pos = to_hex(data_.begin(), data_.end() - 2, size);
|
||||
head_ = const_buffer(&*pos,
|
||||
std::distance(pos, data_.end()));
|
||||
if (size > 0 && final_chunk)
|
||||
tail_ = const_buffer("\r\n0\r\n\r\n", 7);
|
||||
else
|
||||
tail_ = const_buffer("\r\n", 2);
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
template <class OutIter, class Unsigned>
|
||||
typename std::enable_if<
|
||||
std::is_unsigned<Unsigned>::value, OutIter>::type
|
||||
chunk_encoded_buffers<Buffers>::to_hex(
|
||||
OutIter const first, OutIter const last, Unsigned n)
|
||||
{
|
||||
assert(first != last);
|
||||
OutIter iter = last;
|
||||
if(n == 0)
|
||||
{
|
||||
*--iter = '0';
|
||||
return iter;
|
||||
}
|
||||
while(n)
|
||||
{
|
||||
assert(iter != first);
|
||||
*--iter = "0123456789abcdef"[n&0xf];
|
||||
n>>=4;
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator()
|
||||
: buffers_(nullptr)
|
||||
, where_(Where::end)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
bool
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::operator==(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return buffers_ == other.buffers_ &&
|
||||
where_ == other.where_ && iter_ == other.iter_;
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
bool
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::operator!=(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return buffers_ != other.buffers_ ||
|
||||
where_ != other.where_ || iter_ != other.iter_;
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
auto
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::operator++() ->
|
||||
const_iterator&
|
||||
{
|
||||
assert(buffers_);
|
||||
assert(where_ != Where::end);
|
||||
if (where_ == Where::head)
|
||||
where_ = Where::input;
|
||||
else if (iter_ != buffers_->buffers_.end())
|
||||
++iter_;
|
||||
else
|
||||
where_ = Where::end;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
auto
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::operator--() ->
|
||||
const_iterator&
|
||||
{
|
||||
assert(buffers_);
|
||||
assert(where_ != Where::head);
|
||||
if (where_ == Where::end)
|
||||
where_ = Where::input;
|
||||
else if (iter_ != buffers_->buffers_.begin())
|
||||
--iter_;
|
||||
else
|
||||
where_ = Where::head;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
auto
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::operator++(int) const ->
|
||||
const_iterator
|
||||
{
|
||||
auto iter = *this;
|
||||
++iter;
|
||||
return iter;
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
auto
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::operator--(int) const ->
|
||||
const_iterator
|
||||
{
|
||||
auto iter = *this;
|
||||
--iter;
|
||||
return iter;
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
auto
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::operator*() const ->
|
||||
const_buffer
|
||||
{
|
||||
assert(buffers_);
|
||||
assert(where_ != Where::end);
|
||||
if (where_ == Where::head)
|
||||
return buffers_->head_;
|
||||
if (iter_ != buffers_->buffers_.end())
|
||||
return *iter_;
|
||||
return buffers_->tail_;
|
||||
}
|
||||
|
||||
template <class Buffers>
|
||||
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator(
|
||||
chunk_encoded_buffers const& buffers, bool past_the_end)
|
||||
: buffers_(&buffers)
|
||||
, where_(past_the_end ? Where::end : Where::head)
|
||||
, iter_(past_the_end ? buffers_->buffers_.end() :
|
||||
buffers_->buffers_.begin())
|
||||
{
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
/** Returns a chunk-encoded BufferSequence.
|
||||
|
||||
See:
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
|
||||
|
||||
@param buffers The input buffer sequence.
|
||||
|
||||
@param final_chunk `true` If this should include a final-chunk.
|
||||
|
||||
@return A chunk-encoded ConstBufferSequence representing the input.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
detail::chunk_encoded_buffers<ConstBufferSequence>
|
||||
#endif
|
||||
chunk_encode(ConstBufferSequence const& buffers,
|
||||
bool final_chunk = false)
|
||||
{
|
||||
return detail::chunk_encoded_buffers<
|
||||
ConstBufferSequence>{buffers, final_chunk};
|
||||
}
|
||||
|
||||
/// Returns a chunked encoding final chunk.
|
||||
inline
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
boost::asio::const_buffers_1
|
||||
#endif
|
||||
chunk_encode_final()
|
||||
{
|
||||
return boost::asio::const_buffers_1(
|
||||
"0\r\n\r\n", 5);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,203 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
// '0'...'9'
|
||||
inline
|
||||
bool
|
||||
is_digit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
is_token(char c)
|
||||
{
|
||||
/* token = 1*<any CHAR except CTLs or separators>
|
||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
sep = "(" | ")" | "<" | ">" | "@"
|
||||
| "," | ";" | ":" | "\" | <">
|
||||
| "/" | "[" | "]" | "?" | "="
|
||||
| "{" | "}" | SP | HT
|
||||
*/
|
||||
static std::array<char, 256> 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
|
||||
}};
|
||||
return tab[static_cast<std::uint8_t>(c)] != 0;
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
is_text(char c)
|
||||
{
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
static std::array<char, 256> 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
|
||||
}};
|
||||
return tab[static_cast<std::uint8_t>(c)] != 0;
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
// returns 0 if not a valid token char
|
||||
//
|
||||
inline
|
||||
char
|
||||
to_field_char(char c)
|
||||
{
|
||||
/* token = 1*<any CHAR except CTLs or separators>
|
||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
sep = "(" | ")" | "<" | ">" | "@"
|
||||
| "," | ";" | ":" | "\" | <">
|
||||
| "/" | "[" | "]" | "?" | "="
|
||||
| "{" | "}" | SP | HT
|
||||
*/
|
||||
static std::array<char, 256> constexpr tab = {{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
|
||||
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
|
||||
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0
|
||||
}};
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
// returns 0 if not a valid text char
|
||||
//
|
||||
inline
|
||||
char
|
||||
to_value_char(char c)
|
||||
{
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
static std::array<std::uint8_t, 256> 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
|
||||
}};
|
||||
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint8_t
|
||||
unhex(char c)
|
||||
{
|
||||
static std::array<std::int8_t, 256> 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
|
||||
}};
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
};
|
||||
|
||||
template<class = void>
|
||||
struct parser_str_t
|
||||
{
|
||||
static char constexpr close[6] = "close";
|
||||
static char constexpr chunked[8] = "chunked";
|
||||
static char constexpr keep_alive[11] = "keep-alive";
|
||||
|
||||
static char constexpr upgrade[8] = "upgrade";
|
||||
static char constexpr connection[11] = "connection";
|
||||
static char constexpr content_length[15] = "content-length";
|
||||
static char constexpr proxy_connection[17] = "proxy-connection";
|
||||
static char constexpr transfer_encoding[18] = "transfer-encoding";
|
||||
};
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::close[6];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::chunked[8];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::keep_alive[11];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::upgrade[8];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::connection[11];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::content_length[15];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::proxy_connection[17];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::transfer_encoding[18];
|
||||
|
||||
using parser_str = parser_str_t<>;
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP
|
||||
#define BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP
|
||||
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <beast/write_streambuf.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class has_content_length_value
|
||||
{
|
||||
template<class U, class R = typename std::is_convertible<
|
||||
decltype(std::declval<U>().content_length()),
|
||||
std::size_t>>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
// `true` if `T` meets the requirements.
|
||||
static bool const value = type::value;
|
||||
};
|
||||
|
||||
// Determines if the writer can provide the content length
|
||||
template<class T>
|
||||
using has_content_length =
|
||||
std::integral_constant<bool,
|
||||
has_content_length_value<T>::value>;
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
struct write_preparation
|
||||
{
|
||||
using headers_type =
|
||||
basic_headers<std::allocator<char>>;
|
||||
|
||||
message<isRequest, Body, Headers> const& msg;
|
||||
typename Body::writer w;
|
||||
streambuf sb;
|
||||
bool chunked;
|
||||
bool close;
|
||||
|
||||
explicit
|
||||
write_preparation(
|
||||
message<isRequest, Body, Headers> const& msg_)
|
||||
: msg(msg_)
|
||||
, w(msg)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
w.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
// VFALCO TODO This implementation requires making a
|
||||
// copy of the headers, we can do better.
|
||||
// VFALCO Should we be using handler_alloc?
|
||||
headers_type h(msg.headers.begin(), msg.headers.end());
|
||||
set_content_length(h, has_content_length<
|
||||
typename Body::writer>{});
|
||||
|
||||
// VFALCO TODO Keep-Alive
|
||||
|
||||
if(close)
|
||||
{
|
||||
if(msg.version >= 11)
|
||||
h.insert("Connection", "close");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(msg.version < 11)
|
||||
h.insert("Connection", "keep-alive");
|
||||
}
|
||||
|
||||
msg.write_firstline(sb);
|
||||
write_fields(sb, h);
|
||||
beast::write(sb, "\r\n");
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
set_content_length(headers_type& h,
|
||||
std::true_type)
|
||||
{
|
||||
close = false;
|
||||
chunked = false;
|
||||
h.insert("Content-Length", w.content_length());
|
||||
}
|
||||
|
||||
void
|
||||
set_content_length(headers_type& h,
|
||||
std::false_type)
|
||||
{
|
||||
if(msg.version >= 11)
|
||||
{
|
||||
close = false;
|
||||
chunked = true;
|
||||
h.insert("Transfer-Encoding", "chunked");
|
||||
}
|
||||
else
|
||||
{
|
||||
close = true;
|
||||
chunked = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_EMPTY_BODY_HPP
|
||||
#define BEAST_HTTP_EMPTY_BODY_HPP
|
||||
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** An empty content-body.
|
||||
*/
|
||||
struct empty_body
|
||||
{
|
||||
#if GENERATING_DOCS
|
||||
/// The type of the `message::body` member
|
||||
using value_type = void;
|
||||
#else
|
||||
struct value_type {};
|
||||
#endif
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
|
||||
struct reader
|
||||
{
|
||||
template<bool isRequest, class Allocator>
|
||||
explicit
|
||||
reader(message<isRequest, empty_body, Allocator>&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const*, std::size_t, error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct writer
|
||||
{
|
||||
template<bool isRequest, class Allocator>
|
||||
explicit
|
||||
writer(message<isRequest, empty_body, Allocator> const& m)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t
|
||||
content_length() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class Write>
|
||||
boost::tribool
|
||||
operator()(resume_context&&, error_code&, Write&& write)
|
||||
{
|
||||
write(boost::asio::null_buffers{});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_ERROR_HPP
|
||||
#define BEAST_HTTP_ERROR_HPP
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
using error_code = boost::system::error_code;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_HEADERS_HPP
|
||||
#define BEAST_HTTP_HEADERS_HPP
|
||||
|
||||
#include <beast/http/basic_headers.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Allocator>
|
||||
using headers = basic_headers<Allocator>;
|
||||
|
||||
using http_headers =
|
||||
basic_headers<std::allocator<char>>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,299 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
||||
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
||||
|
||||
#include <beast/type_check.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
auto
|
||||
basic_headers_base::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
basic_headers_base::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
basic_headers_base::cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
basic_headers_base::cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_headers<Allocator>::
|
||||
delete_all()
|
||||
{
|
||||
for(auto it = list_.begin(); it != list_.end();)
|
||||
{
|
||||
auto& e = *it++;
|
||||
e.~element();
|
||||
alloc_traits::deallocate(
|
||||
this->member(), &e, 1);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_headers<Allocator>::
|
||||
move_assign(basic_headers& other, std::false_type)
|
||||
{
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
copy_from(other);
|
||||
other.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
set_ = std::move(other.set_);
|
||||
list_ = std::move(other.list_);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_headers<Allocator>::
|
||||
move_assign(basic_headers& other, std::true_type)
|
||||
{
|
||||
this->member() = std::move(other.member());
|
||||
set_ = std::move(other.set_);
|
||||
list_ = std::move(other.list_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_headers<Allocator>::
|
||||
copy_assign(basic_headers const& other, std::false_type)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_headers<Allocator>::
|
||||
copy_assign(basic_headers const& other, std::true_type)
|
||||
{
|
||||
this->member() = other.member();
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
basic_headers<Allocator>::
|
||||
~basic_headers()
|
||||
{
|
||||
delete_all();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_headers<Allocator>::
|
||||
basic_headers(Allocator const& alloc)
|
||||
: beast::detail::empty_base_optimization<
|
||||
alloc_type>(alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_headers<Allocator>::
|
||||
basic_headers(basic_headers&& other)
|
||||
: beast::detail::empty_base_optimization<alloc_type>(
|
||||
std::move(other.member()))
|
||||
, detail::basic_headers_base(
|
||||
std::move(other.set_), std::move(other.list_))
|
||||
{
|
||||
other.list_.clear();
|
||||
other.set_.clear();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_headers<Allocator>::
|
||||
operator=(basic_headers&& other) ->
|
||||
basic_headers&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
clear();
|
||||
move_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_move_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_headers<Allocator>::
|
||||
basic_headers(basic_headers const& other)
|
||||
: basic_headers(alloc_traits::
|
||||
select_on_container_copy_construction(other.member()))
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_headers<Allocator>::
|
||||
operator=(basic_headers const& other) ->
|
||||
basic_headers&
|
||||
{
|
||||
clear();
|
||||
copy_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_copy_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_headers<Allocator>::
|
||||
basic_headers(basic_headers<OtherAlloc> const& other)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_headers<Allocator>::
|
||||
operator=(basic_headers<OtherAlloc> const& other) ->
|
||||
basic_headers&
|
||||
{
|
||||
clear();
|
||||
copy_from(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class FwdIt>
|
||||
basic_headers<Allocator>::
|
||||
basic_headers(FwdIt first, FwdIt last)
|
||||
{
|
||||
for(;first != last; ++first)
|
||||
insert(first->name(), first->value());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_headers<Allocator>::
|
||||
find(boost::string_ref const& name) const ->
|
||||
iterator
|
||||
{
|
||||
auto const it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return list_.end();
|
||||
return list_.iterator_to(*it);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
boost::string_ref
|
||||
basic_headers<Allocator>::
|
||||
operator[](boost::string_ref const& name) const
|
||||
{
|
||||
// VFALCO This none object looks sketchy
|
||||
static boost::string_ref const none;
|
||||
auto const it = find(name);
|
||||
if(it == end())
|
||||
return none;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_headers<Allocator>::
|
||||
clear() noexcept
|
||||
{
|
||||
delete_all();
|
||||
list_.clear();
|
||||
set_.clear();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_headers<Allocator>::
|
||||
erase(boost::string_ref const& name)
|
||||
{
|
||||
auto const it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return 0;
|
||||
auto& e = *it;
|
||||
set_.erase(set_.iterator_to(e));
|
||||
list_.erase(list_.iterator_to(e));
|
||||
alloc_traits::deallocate(this->member(), &e, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_headers<Allocator>::
|
||||
insert(boost::string_ref const& name,
|
||||
boost::string_ref const& value)
|
||||
{
|
||||
typename set_t::insert_commit_data d;
|
||||
auto const result =
|
||||
set_.insert_check(name, less{}, d);
|
||||
if (result.second)
|
||||
{
|
||||
auto const p = alloc_traits::allocate(
|
||||
this->member(), 1);
|
||||
alloc_traits::construct(
|
||||
this->member(), p, name, value);
|
||||
list_.push_back(*p);
|
||||
set_.insert_commit(*p, d);
|
||||
return;
|
||||
}
|
||||
// If field already exists, insert comma
|
||||
// separated value as per RFC2616 section 4.2
|
||||
auto& cur = result.first->data.second;
|
||||
cur.reserve(cur.size() + 1 + value.size());
|
||||
cur.append(1, ',');
|
||||
cur.append(value.data(), value.size());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_headers<Allocator>::
|
||||
replace(boost::string_ref const& name,
|
||||
boost::string_ref const& value)
|
||||
{
|
||||
erase(name);
|
||||
insert(name, value);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,318 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
#define BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
|
||||
#include <beast/http/chunk_encode.hpp>
|
||||
#include <beast/http/resume_context.hpp>
|
||||
#include <beast/http/rfc2616.hpp>
|
||||
#include <beast/write_streambuf.hpp>
|
||||
#include <beast/type_check.hpp>
|
||||
#include <beast/http/detail/write_preparation.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
message<isRequest, Body, Headers>::
|
||||
message()
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
message<isRequest, Body, Headers>::
|
||||
message(request_params params)
|
||||
{
|
||||
static_assert(isRequest, "message is not a request");
|
||||
this->method = params.method;
|
||||
this->url = std::move(params.url);
|
||||
version = params.version;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
message<isRequest, Body, Headers>::
|
||||
message(response_params params)
|
||||
{
|
||||
static_assert(! isRequest, "message is not a response");
|
||||
this->status = params.status;
|
||||
this->reason = std::move(params.reason);
|
||||
version = params.version;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
template<class Streambuf>
|
||||
void
|
||||
message<isRequest, Body, Headers>::
|
||||
write_firstline(Streambuf& streambuf,
|
||||
std::true_type) const
|
||||
{
|
||||
write(streambuf, to_string(this->method));
|
||||
write(streambuf, " ");
|
||||
write(streambuf, this->url);
|
||||
switch(version)
|
||||
{
|
||||
case 10:
|
||||
write(streambuf, " HTTP/1.0\r\n");
|
||||
break;
|
||||
case 11:
|
||||
write(streambuf, " HTTP/1.1\r\n");
|
||||
break;
|
||||
default:
|
||||
write(streambuf, " HTTP/");
|
||||
write(streambuf, version / 10);
|
||||
write(streambuf, ".");
|
||||
write(streambuf, version % 10);
|
||||
write(streambuf, "\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
template<class Streambuf>
|
||||
void
|
||||
message<isRequest, Body, Headers>::
|
||||
write_firstline(Streambuf& streambuf,
|
||||
std::false_type) const
|
||||
{
|
||||
switch(version)
|
||||
{
|
||||
case 10:
|
||||
write(streambuf, "HTTP/1.0 ");
|
||||
break;
|
||||
case 11:
|
||||
write(streambuf, "HTTP/1.1 ");
|
||||
break;
|
||||
default:
|
||||
write(streambuf, " HTTP/");
|
||||
write(streambuf, version / 10);
|
||||
write(streambuf, ".");
|
||||
write(streambuf, version % 10);
|
||||
write(streambuf, " ");
|
||||
break;
|
||||
}
|
||||
write(streambuf, this->status);
|
||||
write(streambuf, " ");
|
||||
write(streambuf, this->reason);
|
||||
write(streambuf, "\r\n");
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::string
|
||||
buffers_to_string(ConstBufferSequence const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
std::string s;
|
||||
s.reserve(buffer_size(buffers));
|
||||
for(auto const& b : buffers)
|
||||
s.append(buffer_cast<char const*>(b),
|
||||
buffer_size(b));
|
||||
return s;
|
||||
}
|
||||
|
||||
class writef_ostream
|
||||
{
|
||||
std::ostream& os_;
|
||||
bool chunked_;
|
||||
|
||||
public:
|
||||
writef_ostream(std::ostream& os, bool chunked)
|
||||
: os_(os)
|
||||
, chunked_(chunked)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(ConstBufferSequence const& buffers)
|
||||
{
|
||||
if(chunked_)
|
||||
os_ << buffers_to_string(
|
||||
chunk_encode(buffers));
|
||||
else
|
||||
os_ << buffers_to_string(
|
||||
buffers);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
// Diagnostic output only
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
message<isRequest, Body, Headers> const& msg)
|
||||
{
|
||||
error_code ec;
|
||||
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
||||
wp.init(ec);
|
||||
if(ec)
|
||||
return os;
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
bool ready = false;
|
||||
resume_context resume{
|
||||
[&]
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
ready = true;
|
||||
cv.notify_one();
|
||||
}};
|
||||
auto copy = resume;
|
||||
os << detail::buffers_to_string(wp.sb.data());
|
||||
wp.sb.consume(wp.sb.size());
|
||||
detail::writef_ostream writef(os, wp.chunked);
|
||||
for(;;)
|
||||
{
|
||||
{
|
||||
auto result = wp.w(std::move(copy), ec, writef);
|
||||
if(ec)
|
||||
return os;
|
||||
if(result)
|
||||
break;
|
||||
if(boost::indeterminate(result))
|
||||
{
|
||||
copy = resume;
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, [&]{ return ready; });
|
||||
ready = false;
|
||||
}
|
||||
}
|
||||
wp.sb.consume(wp.sb.size());
|
||||
for(;;)
|
||||
{
|
||||
auto result = wp.w(std::move(copy), ec, writef);
|
||||
if(ec)
|
||||
return os;
|
||||
if(result)
|
||||
break;
|
||||
if(boost::indeterminate(result))
|
||||
{
|
||||
copy = resume;
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, [&]{ return ready; });
|
||||
ready = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(wp.chunked)
|
||||
{
|
||||
// VFALCO Unfortunately the current interface to the
|
||||
// Writer concept prevents us from using coalescing the
|
||||
// final body chunk with the final chunk delimiter.
|
||||
//
|
||||
// write final chunk
|
||||
os << detail::buffers_to_string(chunk_encode_final());
|
||||
if(ec)
|
||||
return os;
|
||||
}
|
||||
os << std::endl;
|
||||
return os;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
set_connection(bool keep_alive,
|
||||
message<isRequest, Body, Headers>& req)
|
||||
{
|
||||
if(req.version >= 11)
|
||||
{
|
||||
if(! keep_alive)
|
||||
req.headers.replace("Connection", "close");
|
||||
else
|
||||
req.headers.erase("Connection");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(keep_alive)
|
||||
req.headers.replace("Connection", "keep-alive");
|
||||
else
|
||||
req.headers.erase("Connection");
|
||||
}
|
||||
}
|
||||
|
||||
template<class Body, class Headers,
|
||||
class OtherBody, class OtherAllocator>
|
||||
void
|
||||
set_connection(bool keep_alive,
|
||||
message<false, Body, Headers>& resp,
|
||||
message<true, OtherBody, OtherAllocator> const& req)
|
||||
{
|
||||
if(req.version >= 11)
|
||||
{
|
||||
if(rfc2616::token_in_list(req["Connection"], "close"))
|
||||
keep_alive = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(! rfc2616::token_in_list(req["Connection"], "keep-alive"))
|
||||
keep_alive = false;
|
||||
}
|
||||
set_connection(keep_alive, resp);
|
||||
}
|
||||
|
||||
template<class Streambuf, class FieldSequence>
|
||||
void
|
||||
write_fields(Streambuf& streambuf, FieldSequence const& fields)
|
||||
{
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
//static_assert(is_FieldSequence<FieldSequence>::value,
|
||||
// "FieldSequence requirements not met");
|
||||
for(auto const& field : fields)
|
||||
{
|
||||
write(streambuf, field.name());
|
||||
write(streambuf, ": ");
|
||||
write(streambuf, field.value());
|
||||
write(streambuf, "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
bool
|
||||
is_keep_alive(message<isRequest, Body, Headers> const& msg)
|
||||
{
|
||||
if(msg.version >= 11)
|
||||
{
|
||||
if(rfc2616::token_in_list(
|
||||
msg.headers["Connection"], "close"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
if(rfc2616::token_in_list(
|
||||
msg.headers["Connection"], "keep-alive"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
bool
|
||||
is_upgrade(message<isRequest, Body, Headers> const& msg)
|
||||
{
|
||||
if(msg.version < 11)
|
||||
return false;
|
||||
if(rfc2616::token_in_list(
|
||||
msg.headers["Connection"], "upgrade"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/message.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,286 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
|
||||
#include <beast/bind_handler.hpp>
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class Handler>
|
||||
class read_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
using parser_type =
|
||||
parser<isRequest, Body, Headers>;
|
||||
|
||||
using message_type =
|
||||
message<isRequest, Body, Headers>;
|
||||
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
Streambuf& sb;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
Handler h;
|
||||
bool started = false;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
Streambuf& sb_, message_type& m_)
|
||||
: s(s_)
|
||||
, sb(sb_)
|
||||
, m(m_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_op(DeducedHandler&& h, Stream&s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class Handler>
|
||||
void
|
||||
read_op<Stream, Streambuf, isRequest, Body, Headers, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto const used =
|
||||
d.p.write(d.sb.data(), ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
}
|
||||
if(used > 0)
|
||||
d.started = true;
|
||||
d.sb.consume(used);
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
// read
|
||||
d.state = 2;
|
||||
d.s.async_read_some(d.sb.prepare(
|
||||
read_size_helper(d.sb, 65536)),
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got data
|
||||
case 2:
|
||||
{
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! d.started)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
if(! ec)
|
||||
{
|
||||
assert(d.p.complete());
|
||||
d.m = d.p.release();
|
||||
}
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
d.sb.commit(bytes_transferred);
|
||||
d.sb.consume(d.p.write(d.sb.data(), ec));
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
break;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class SyncReadStream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
message<isRequest, Body, Headers>& m,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
parser<isRequest, Body, Headers> p;
|
||||
bool started = false;
|
||||
for(;;)
|
||||
{
|
||||
auto used =
|
||||
p.write(streambuf.data(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
streambuf.consume(used);
|
||||
if(used > 0)
|
||||
started = true;
|
||||
if(p.complete())
|
||||
{
|
||||
m = p.release();
|
||||
break;
|
||||
}
|
||||
streambuf.commit(stream.read_some(
|
||||
streambuf.prepare(read_size_helper(
|
||||
streambuf, 65536)), ec));
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
return;
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! started)
|
||||
return;
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
p.write_eof(ec);
|
||||
if(ec)
|
||||
return;
|
||||
assert(p.complete());
|
||||
m = p.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class AsyncReadStream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_read(AsyncReadStream& stream, Streambuf& streambuf,
|
||||
message<isRequest, Body, Headers>& m,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion(handler);
|
||||
detail::read_op<AsyncReadStream, Streambuf,
|
||||
isRequest, Body, Headers, decltype(
|
||||
completion.handler)>{completion.handler,
|
||||
stream, streambuf, m};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,456 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_WRITE_IPP
|
||||
#define BEAST_HTTP_IMPL_WRITE_IPP
|
||||
|
||||
#include <beast/http/chunk_encode.hpp>
|
||||
#include <beast/http/resume_context.hpp>
|
||||
#include <beast/http/detail/write_preparation.hpp>
|
||||
#include <beast/buffer_cat.hpp>
|
||||
#include <beast/bind_handler.hpp>
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <beast/type_check.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Headers>
|
||||
class write_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
// VFALCO How do we use handler_alloc in write_preparation?
|
||||
write_preparation<
|
||||
isRequest, Body, Headers> wp;
|
||||
Handler h;
|
||||
resume_context resume;
|
||||
resume_context copy;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
message<isRequest, Body, Headers> const& m_)
|
||||
: s(s_)
|
||||
, wp(m_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class writef0
|
||||
{
|
||||
write_op& self_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
writef0(write_op& self)
|
||||
: self_(self)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(ConstBufferSequence const& buffers)
|
||||
{
|
||||
auto& d = *self_.d_;
|
||||
// write headers and body
|
||||
if(d.wp.chunked)
|
||||
boost::asio::async_write(d.s,
|
||||
buffer_cat(d.wp.sb.data(),
|
||||
chunk_encode(buffers)),
|
||||
std::move(self_));
|
||||
else
|
||||
boost::asio::async_write(d.s,
|
||||
buffer_cat(d.wp.sb.data(),
|
||||
buffers), std::move(self_));
|
||||
}
|
||||
};
|
||||
|
||||
class writef
|
||||
{
|
||||
write_op& self_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
writef(write_op& self)
|
||||
: self_(self)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(ConstBufferSequence const& buffers)
|
||||
{
|
||||
auto& d = *self_.d_;
|
||||
// write body
|
||||
if(d.wp.chunked)
|
||||
boost::asio::async_write(d.s,
|
||||
chunk_encode(buffers),
|
||||
std::move(self_));
|
||||
else
|
||||
boost::asio::async_write(d.s,
|
||||
buffers, std::move(self_));
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
auto& d = *d_;
|
||||
auto sp = d_;
|
||||
d.resume = {
|
||||
[sp]() mutable
|
||||
{
|
||||
write_op self(std::move(sp));
|
||||
self.d_->cont = false;
|
||||
auto& ios = self.d_->s.get_io_service();
|
||||
ios.dispatch(bind_handler(std::move(self),
|
||||
error_code{}, 0, false));
|
||||
}};
|
||||
d.copy = d.resume;
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
explicit
|
||||
write_op(std::shared_ptr<data> d)
|
||||
: d_(std::move(d))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
write_op<Stream, Handler, isRequest, Body, Headers>::
|
||||
operator()(error_code ec, std::size_t, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
d.wp.init(ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(bind_handler(
|
||||
std::move(*this), ec, 0, false));
|
||||
return;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
auto const result = d.wp.w(
|
||||
std::move(d.copy), ec, writef0{*this});
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(bind_handler(
|
||||
std::move(*this), ec, false));
|
||||
return;
|
||||
}
|
||||
if(boost::indeterminate(result))
|
||||
{
|
||||
// suspend
|
||||
d.copy = d.resume;
|
||||
return;
|
||||
}
|
||||
if(result)
|
||||
d.state = d.wp.chunked ? 4 : 5;
|
||||
else
|
||||
d.state = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
// sent headers and body
|
||||
case 2:
|
||||
d.wp.sb.consume(d.wp.sb.size());
|
||||
d.state = 3;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
auto const result = d.wp.w(
|
||||
std::move(d.copy), ec, writef{*this});
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(boost::indeterminate(result))
|
||||
{
|
||||
// suspend
|
||||
d.copy = d.resume;
|
||||
return;
|
||||
}
|
||||
if(result)
|
||||
d.state = d.wp.chunked ? 4 : 5;
|
||||
else
|
||||
d.state = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
case 4:
|
||||
// VFALCO Unfortunately the current interface to the
|
||||
// Writer concept prevents us from coalescing the
|
||||
// final body chunk with the final chunk delimiter.
|
||||
//
|
||||
// write final chunk
|
||||
d.state = 5;
|
||||
boost::asio::async_write(d.s,
|
||||
chunk_encode_final(), std::move(*this));
|
||||
return;
|
||||
|
||||
case 5:
|
||||
if(d.wp.close)
|
||||
{
|
||||
// VFALCO TODO Decide on an error code
|
||||
ec = boost::asio::error::eof;
|
||||
}
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
d.resume = {};
|
||||
d.copy = {};
|
||||
}
|
||||
|
||||
template<class SyncWriteStream, class Streambuf>
|
||||
class writef0_write
|
||||
{
|
||||
Streambuf const& sb_;
|
||||
SyncWriteStream& stream_;
|
||||
bool chunked_;
|
||||
error_code& ec_;
|
||||
|
||||
public:
|
||||
writef0_write(SyncWriteStream& stream,
|
||||
Streambuf const& sb, bool chunked, error_code& ec)
|
||||
: sb_(sb)
|
||||
, stream_(stream)
|
||||
, chunked_(chunked)
|
||||
, ec_(ec)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(ConstBufferSequence const& buffers)
|
||||
{
|
||||
// write headers and body
|
||||
if(chunked_)
|
||||
boost::asio::write(stream_, buffer_cat(
|
||||
sb_.data(), chunk_encode(buffers)), ec_);
|
||||
else
|
||||
boost::asio::write(stream_, buffer_cat(
|
||||
sb_.data(), buffers), ec_);
|
||||
}
|
||||
};
|
||||
|
||||
template<class SyncWriteStream>
|
||||
class writef_write
|
||||
{
|
||||
SyncWriteStream& stream_;
|
||||
bool chunked_;
|
||||
error_code& ec_;
|
||||
|
||||
public:
|
||||
writef_write(SyncWriteStream& stream,
|
||||
bool chunked, error_code& ec)
|
||||
: stream_(stream)
|
||||
, chunked_(chunked)
|
||||
, ec_(ec)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void operator()(ConstBufferSequence const& buffers)
|
||||
{
|
||||
// write body
|
||||
if(chunked_)
|
||||
boost::asio::write(stream_,
|
||||
chunk_encode(buffers), ec_);
|
||||
else
|
||||
boost::asio::write(stream_, buffers, ec_);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Headers> const& msg,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
||||
wp.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
bool ready = false;
|
||||
resume_context resume{
|
||||
[&]
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
ready = true;
|
||||
cv.notify_one();
|
||||
}};
|
||||
auto copy = resume;
|
||||
for(;;)
|
||||
{
|
||||
{
|
||||
auto result = wp.w(std::move(copy), ec,
|
||||
detail::writef0_write<SyncWriteStream, decltype(wp.sb)>{
|
||||
stream, wp.sb, wp.chunked, ec});
|
||||
if(ec)
|
||||
return;
|
||||
if(result)
|
||||
break;
|
||||
if(boost::indeterminate(result))
|
||||
{
|
||||
boost::asio::write(stream, wp.sb.data(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
wp.sb.consume(wp.sb.size());
|
||||
copy = resume;
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, [&]{ return ready; });
|
||||
ready = false;
|
||||
}
|
||||
}
|
||||
wp.sb.consume(wp.sb.size());
|
||||
for(;;)
|
||||
{
|
||||
auto result = wp.w(std::move(copy), ec,
|
||||
detail::writef_write<SyncWriteStream>{
|
||||
stream, wp.chunked, ec});
|
||||
if(ec)
|
||||
return;
|
||||
if(result)
|
||||
break;
|
||||
if(boost::indeterminate(result))
|
||||
{
|
||||
copy = resume;
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
cv.wait(lock, [&]{ return ready; });
|
||||
ready = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(wp.chunked)
|
||||
{
|
||||
// VFALCO Unfortunately the current interface to the
|
||||
// Writer concept prevents us from using coalescing the
|
||||
// final body chunk with the final chunk delimiter.
|
||||
//
|
||||
// write final chunk
|
||||
boost::asio::write(stream, chunk_encode_final(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
if(wp.close)
|
||||
{
|
||||
// VFALCO TODO Decide on an error code
|
||||
ec = boost::asio::error::eof;
|
||||
}
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
async_write(AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Headers> const& msg,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
is_AsyncWriteStream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
beast::async_completion<WriteHandler,
|
||||
void(error_code)> completion(handler);
|
||||
detail::write_op<AsyncWriteStream, decltype(completion.handler),
|
||||
isRequest, Body, Headers>{completion.handler, stream, msg};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,174 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_MESSAGE_HPP
|
||||
#define BEAST_HTTP_MESSAGE_HPP
|
||||
|
||||
#include <beast/http/basic_headers.hpp>
|
||||
#include <beast/http/method.hpp>
|
||||
#include <beast/buffers_debug.hpp>
|
||||
#include <beast/type_check.hpp>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct request_fields
|
||||
{
|
||||
http::method_t method;
|
||||
std::string url;
|
||||
};
|
||||
|
||||
struct response_fields
|
||||
{
|
||||
int status;
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
|
||||
struct request_params
|
||||
{
|
||||
http::method_t method;
|
||||
std::string url;
|
||||
int version;
|
||||
};
|
||||
|
||||
struct response_params
|
||||
{
|
||||
int status;
|
||||
std::string reason;
|
||||
int version;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/** A HTTP message.
|
||||
|
||||
A message can be a request or response, depending on the `isRequest`
|
||||
template argument value. Requests and responses have different types,
|
||||
so functions may be overloaded on them if desired.
|
||||
|
||||
The `Body` template argument type determines the model used
|
||||
to read or write the content body of the message.
|
||||
|
||||
@tparam isRequest `true` if this is a request.
|
||||
|
||||
@tparam Body A type meeting the requirements of Body.
|
||||
|
||||
@tparam Headers A type meeting the requirements of Headers.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
struct message
|
||||
: std::conditional<isRequest,
|
||||
detail::request_fields, detail::response_fields>::type
|
||||
{
|
||||
/** The trait type characterizing the body.
|
||||
|
||||
The body member will be of type body_type::value_type.
|
||||
*/
|
||||
using body_type = Body;
|
||||
using headers_type = Headers;
|
||||
|
||||
using is_request =
|
||||
std::integral_constant<bool, isRequest>;
|
||||
|
||||
int version; // 10 or 11
|
||||
headers_type headers;
|
||||
typename Body::value_type body;
|
||||
|
||||
message();
|
||||
message(message&&) = default;
|
||||
message(message const&) = default;
|
||||
message& operator=(message&&) = default;
|
||||
message& operator=(message const&) = default;
|
||||
|
||||
/** Construct a HTTP request.
|
||||
*/
|
||||
explicit
|
||||
message(request_params params);
|
||||
|
||||
/** Construct a HTTP response.
|
||||
*/
|
||||
explicit
|
||||
message(response_params params);
|
||||
|
||||
/// Serialize the request or response line to a Streambuf.
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_firstline(Streambuf& streambuf) const
|
||||
{
|
||||
write_firstline(streambuf,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
/// Diagnostics only
|
||||
template<bool, class, class>
|
||||
friend
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
message const& m);
|
||||
|
||||
private:
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_firstline(Streambuf& streambuf,
|
||||
std::true_type) const;
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_firstline(Streambuf& streambuf,
|
||||
std::false_type) const;
|
||||
};
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
|
||||
/// A typical HTTP request
|
||||
template<class Body,
|
||||
class Headers = basic_headers<std::allocator<char>>>
|
||||
using request = message<true, Body, Headers>;
|
||||
|
||||
/// A typical HTTP response
|
||||
template<class Body,
|
||||
class Headers = basic_headers<std::allocator<char>>>
|
||||
using response = message<false, Body, Headers>;
|
||||
|
||||
#endif
|
||||
|
||||
// For diagnostic output only
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
message<isRequest, Body, Headers> const& m);
|
||||
|
||||
/// Write a FieldSequence to a Streambuf.
|
||||
template<class Streambuf, class FieldSequence>
|
||||
void
|
||||
write_fields(Streambuf& streambuf, FieldSequence const& fields);
|
||||
|
||||
/// Returns `true` if a message indicates a keep alive
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
bool
|
||||
is_keep_alive(message<isRequest, Body, Headers> const& msg);
|
||||
|
||||
/// Returns `true` if a message indicates a HTTP Upgrade request or response
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
bool
|
||||
is_upgrade(message<isRequest, Body, Headers> const& msg);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/message.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,179 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_METHOD_HPP
|
||||
#define BEAST_HTTP_METHOD_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class method_t
|
||||
{
|
||||
http_delete,
|
||||
http_get,
|
||||
http_head,
|
||||
http_post,
|
||||
http_put,
|
||||
|
||||
// pathological
|
||||
http_connect,
|
||||
http_options,
|
||||
http_trace,
|
||||
|
||||
// webdav
|
||||
http_copy,
|
||||
http_lock,
|
||||
http_mkcol,
|
||||
http_move,
|
||||
http_propfind,
|
||||
http_proppatch,
|
||||
http_search,
|
||||
http_unlock,
|
||||
http_bind,
|
||||
http_rebind,
|
||||
http_unbind,
|
||||
http_acl,
|
||||
|
||||
// subversion
|
||||
http_report,
|
||||
http_mkactivity,
|
||||
http_checkout,
|
||||
http_merge,
|
||||
|
||||
// upnp
|
||||
http_msearch,
|
||||
http_notify,
|
||||
http_subscribe,
|
||||
http_unsubscribe,
|
||||
|
||||
// RFC-5789
|
||||
http_patch,
|
||||
http_purge,
|
||||
|
||||
// CalDav
|
||||
http_mkcalendar,
|
||||
|
||||
// RFC-2068, section 19.6.1.2
|
||||
http_link,
|
||||
http_unlink
|
||||
};
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
to_string(method_t m)
|
||||
{
|
||||
switch(m)
|
||||
{
|
||||
case method_t::http_delete: return "DELETE";
|
||||
case method_t::http_get: return "GET";
|
||||
case method_t::http_head: return "HEAD";
|
||||
case method_t::http_post: return "POST";
|
||||
case method_t::http_put: return "PUT";
|
||||
|
||||
case method_t::http_connect: return "CONNECT";
|
||||
case method_t::http_options: return "OPTIONS";
|
||||
case method_t::http_trace: return "TRACE";
|
||||
|
||||
case method_t::http_copy: return "COPY";
|
||||
case method_t::http_lock: return "LOCK";
|
||||
case method_t::http_mkcol: return "MKCOL";
|
||||
case method_t::http_move: return "MOVE";
|
||||
case method_t::http_propfind: return "PROPFIND";
|
||||
case method_t::http_proppatch: return "PROPPATCH";
|
||||
case method_t::http_search: return "SEARCH";
|
||||
case method_t::http_unlock: return "UNLOCK";
|
||||
|
||||
case method_t::http_report: return "REPORT";
|
||||
case method_t::http_mkactivity: return "MKACTIVITY";
|
||||
case method_t::http_checkout: return "CHECKOUT";
|
||||
case method_t::http_merge: return "MERGE";
|
||||
|
||||
case method_t::http_msearch: return "MSEARCH";
|
||||
case method_t::http_notify: return "NOTIFY";
|
||||
case method_t::http_subscribe: return "SUBSCRIBE";
|
||||
case method_t::http_unsubscribe: return "UNSUBSCRIBE";
|
||||
|
||||
case method_t::http_patch: return "PATCH";
|
||||
case method_t::http_purge: return "PURGE";
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
};
|
||||
|
||||
return "GET";
|
||||
}
|
||||
|
||||
template <class Stream>
|
||||
Stream&
|
||||
operator<< (Stream& s, method_t m)
|
||||
{
|
||||
return s << to_string(m);
|
||||
}
|
||||
|
||||
/** Returns the string corresponding to the numeric HTTP status code. */
|
||||
template<class = void>
|
||||
std::string
|
||||
status_text (int status)
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case 100: return "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
case 206: return "Partial Content";
|
||||
case 300: return "Multiple Choices";
|
||||
case 301: return "Moved Permanently";
|
||||
case 302: return "Found";
|
||||
case 303: return "See Other";
|
||||
case 304: return "Not Modified";
|
||||
case 305: return "Use Proxy";
|
||||
//case 306: return "<reserved>";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 402: return "Payment Required";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 406: return "Not Acceptable";
|
||||
case 407: return "Proxy Authentication Required";
|
||||
case 408: return "Request Timeout";
|
||||
case 409: return "Conflict";
|
||||
case 410: return "Gone";
|
||||
case 411: return "Length Required";
|
||||
case 412: return "Precondition Failed";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 414: return "Request-URI Too Long";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Requested Range Not Satisfiable";
|
||||
case 417: return "Expectation Failed";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 502: return "Bad Gateway";
|
||||
case 503: return "Service Unavailable";
|
||||
case 504: return "Gateway Timeout";
|
||||
case 505: return "HTTP Version Not Supported";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "Unknown HTTP status";
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_PARSE_ERROR_HPP
|
||||
#define BEAST_HTTP_PARSE_ERROR_HPP
|
||||
|
||||
#include <beast/http/error.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class parse_error
|
||||
{
|
||||
connection_closed,
|
||||
|
||||
bad_method,
|
||||
bad_uri,
|
||||
bad_version,
|
||||
bad_crlf,
|
||||
bad_request,
|
||||
|
||||
bad_status_code,
|
||||
bad_status,
|
||||
|
||||
bad_field,
|
||||
bad_value,
|
||||
bad_content_length,
|
||||
illegal_content_length,
|
||||
bad_on_headers_rv,
|
||||
|
||||
invalid_chunk_size,
|
||||
|
||||
short_read
|
||||
};
|
||||
|
||||
class parse_error_category : public boost::system::error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "http";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<parse_error>(ev))
|
||||
{
|
||||
case parse_error::connection_closed:
|
||||
return "data after Connection close";
|
||||
|
||||
case parse_error::bad_method:
|
||||
return "bad method";
|
||||
|
||||
case parse_error::bad_uri:
|
||||
return "bad Request-URI";
|
||||
|
||||
case parse_error::bad_version:
|
||||
return "bad HTTP-Version";
|
||||
|
||||
case parse_error::bad_crlf:
|
||||
return "missing CRLF";
|
||||
|
||||
case parse_error::bad_request:
|
||||
return "bad Request-Line";
|
||||
|
||||
case parse_error::bad_status_code:
|
||||
return "bad Status-Code";
|
||||
|
||||
case parse_error::bad_status:
|
||||
return "bad Status-Line";
|
||||
|
||||
case parse_error::bad_field:
|
||||
return "bad field token";
|
||||
|
||||
case parse_error::bad_value:
|
||||
return "bad field-value";
|
||||
|
||||
case parse_error::bad_content_length:
|
||||
return "bad Content-Length";
|
||||
|
||||
case parse_error::illegal_content_length:
|
||||
return "illegal Content-Length with chunked Transfer-Encoding";
|
||||
|
||||
case parse_error::bad_on_headers_rv:
|
||||
return "on_headers returned an unknown value";
|
||||
|
||||
case parse_error::invalid_chunk_size:
|
||||
return "invalid chunk size";
|
||||
|
||||
case parse_error::short_read:
|
||||
return "unexpected end of data";
|
||||
|
||||
default:
|
||||
return "beast::http::parser error";
|
||||
}
|
||||
}
|
||||
|
||||
boost::system::error_condition
|
||||
default_error_condition(int ev) const noexcept override
|
||||
{
|
||||
return boost::system::error_condition(ev, *this);
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
boost::system::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
|
||||
boost::system::error_category const&
|
||||
get_parse_error_category()
|
||||
{
|
||||
static parse_error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
inline
|
||||
boost::system::error_code
|
||||
make_error_code(parse_error ev)
|
||||
{
|
||||
return error_code(static_cast<int>(ev),
|
||||
get_parse_error_category());
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::http::parse_error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,234 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_PARSER_HPP
|
||||
#define BEAST_HTTP_PARSER_HPP
|
||||
|
||||
#include <beast/http/basic_parser.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct parser_request
|
||||
{
|
||||
std::string method_;
|
||||
std::string uri_;
|
||||
};
|
||||
|
||||
struct parser_response
|
||||
{
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
class parser
|
||||
: public basic_parser<isRequest,
|
||||
parser<isRequest, Body, Headers>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::parser_request, detail::parser_response>::type
|
||||
{
|
||||
using message_type =
|
||||
message<isRequest, Body, Headers>;
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
message_type m_;
|
||||
typename message_type::body_type::reader r_;
|
||||
|
||||
public:
|
||||
parser(parser&&) = default;
|
||||
|
||||
parser()
|
||||
: r_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
message_type
|
||||
release()
|
||||
{
|
||||
return std::move(m_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser<isRequest, parser>;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(! value_.empty())
|
||||
{
|
||||
rfc2616::trim_right_in_place(value_);
|
||||
// VFALCO could std::move
|
||||
m_.headers.insert(field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_uri(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->uri_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_reason(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
field_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_value(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
value_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void set(std::true_type)
|
||||
{
|
||||
// VFALCO This is terrible for setting method
|
||||
auto m =
|
||||
[&](char const* s, method_t m)
|
||||
{
|
||||
if(this->method_ == s)
|
||||
{
|
||||
m_.method = m;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
do
|
||||
{
|
||||
if(m("DELETE", method_t::http_delete))
|
||||
break;
|
||||
if(m("GET", method_t::http_get))
|
||||
break;
|
||||
if(m("HEAD", method_t::http_head))
|
||||
break;
|
||||
if(m("POST", method_t::http_post))
|
||||
break;
|
||||
if(m("PUT", method_t::http_put))
|
||||
break;
|
||||
if(m("CONNECT", method_t::http_connect))
|
||||
break;
|
||||
if(m("OPTIONS", method_t::http_options))
|
||||
break;
|
||||
if(m("TRACE", method_t::http_trace))
|
||||
break;
|
||||
if(m("COPY", method_t::http_copy))
|
||||
break;
|
||||
if(m("LOCK", method_t::http_lock))
|
||||
break;
|
||||
if(m("MKCOL", method_t::http_mkcol))
|
||||
break;
|
||||
if(m("MOVE", method_t::http_move))
|
||||
break;
|
||||
if(m("PROPFIND", method_t::http_propfind))
|
||||
break;
|
||||
if(m("PROPPATCH", method_t::http_proppatch))
|
||||
break;
|
||||
if(m("SEARCH", method_t::http_search))
|
||||
break;
|
||||
if(m("UNLOCK", method_t::http_unlock))
|
||||
break;
|
||||
if(m("BIND", method_t::http_bind))
|
||||
break;
|
||||
if(m("REBID", method_t::http_rebind))
|
||||
break;
|
||||
if(m("UNBIND", method_t::http_unbind))
|
||||
break;
|
||||
if(m("ACL", method_t::http_acl))
|
||||
break;
|
||||
if(m("REPORT", method_t::http_report))
|
||||
break;
|
||||
if(m("MKACTIVITY", method_t::http_mkactivity))
|
||||
break;
|
||||
if(m("CHECKOUT", method_t::http_checkout))
|
||||
break;
|
||||
if(m("MERGE", method_t::http_merge))
|
||||
break;
|
||||
if(m("MSEARCH", method_t::http_msearch))
|
||||
break;
|
||||
if(m("NOTIFY", method_t::http_notify))
|
||||
break;
|
||||
if(m("SUBSCRIBE", method_t::http_subscribe))
|
||||
break;
|
||||
if(m("UNSUBSCRIBE",method_t::http_unsubscribe))
|
||||
break;
|
||||
if(m("PATCH", method_t::http_patch))
|
||||
break;
|
||||
if(m("PURGE", method_t::http_purge))
|
||||
break;
|
||||
if(m("MKCALENDAR", method_t::http_mkcalendar))
|
||||
break;
|
||||
if(m("LINK", method_t::http_link))
|
||||
break;
|
||||
if(m("UNLINK", method_t::http_unlink))
|
||||
break;
|
||||
}
|
||||
while(false);
|
||||
|
||||
m_.url = std::move(this->uri_);
|
||||
|
||||
}
|
||||
|
||||
void set(std::false_type)
|
||||
{
|
||||
m_.status = this->status_code();
|
||||
m_.reason = this->reason_;
|
||||
}
|
||||
|
||||
int on_headers(error_code&)
|
||||
{
|
||||
flush();
|
||||
m_.version = 10 * this->http_major() + this->http_minor();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
{
|
||||
set(std::integral_constant<
|
||||
bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code& ec)
|
||||
{
|
||||
set(std::integral_constant<
|
||||
bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const& s, error_code& ec)
|
||||
{
|
||||
r_.write(s.data(), s.size(), ec);
|
||||
}
|
||||
|
||||
void on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_READ_HPP
|
||||
#define BEAST_HTTP_READ_HPP
|
||||
|
||||
#include <beast/async_completion.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/parser.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Read a HTTP message from a stream.
|
||||
|
||||
@param stream The stream to read the message from.
|
||||
|
||||
@param streambuf A Streambuf used to hold unread bytes. The
|
||||
implementation may read past the end of the message. The extra
|
||||
bytes are stored here, to be presented in a subsequent call to
|
||||
read.
|
||||
|
||||
@param msg An object used to store the read message. Any
|
||||
contents will be overwritten.
|
||||
|
||||
@throws boost::system::system_error on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
message<isRequest, Body, Headers>& msg)
|
||||
{
|
||||
error_code ec;
|
||||
read(stream, streambuf, msg, ec);
|
||||
if(ec)
|
||||
throw boost::system::system_error{ec};
|
||||
}
|
||||
|
||||
/** Read a HTTP message from a stream.
|
||||
|
||||
@param stream The stream to read the message from.
|
||||
|
||||
@param streambuf A Streambuf used to hold unread bytes. The
|
||||
implementation may read past the end of the message. The extra
|
||||
bytes are stored here, to be presented in a subsequent call to
|
||||
read.
|
||||
|
||||
@param msg An object used to store the read message. Any
|
||||
contents will be overwritten.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
message<isRequest, Body, Headers>& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Start reading a HTTP message from a stream asynchronously.
|
||||
|
||||
@param stream The stream to read the message from.
|
||||
|
||||
@param streambuf A Streambuf used to hold unread bytes. The
|
||||
implementation may read past the end of the message. The extra
|
||||
bytes are stored here, to be presented in a subsequent call to
|
||||
async_read.
|
||||
|
||||
@param msg An object used to store the read message. Any
|
||||
contents will be overwritten.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using boost::asio::io_service::post().
|
||||
*/
|
||||
template<class AsyncReadStream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_read(AsyncReadStream& stream, Streambuf& streambuf,
|
||||
message<isRequest, Body, Headers>& msg,
|
||||
ReadHandler&& handler);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/read.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_REASON_HPP
|
||||
#define BEAST_HTTP_REASON_HPP
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Returns the text for a known status code integer. */
|
||||
template<class = void>
|
||||
char const*
|
||||
reason_string(int status)
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case 100: return "Continue";
|
||||
case 101: return "Switching Protocols";
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 202: return "Accepted";
|
||||
case 203: return "Non-Authoritative Information";
|
||||
case 204: return "No Content";
|
||||
case 205: return "Reset Content";
|
||||
case 206: return "Partial Content";
|
||||
case 300: return "Multiple Choices";
|
||||
case 301: return "Moved Permanently";
|
||||
case 302: return "Found";
|
||||
case 303: return "See Other";
|
||||
case 304: return "Not Modified";
|
||||
case 305: return "Use Proxy";
|
||||
case 307: return "Temporary Redirect";
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized";
|
||||
case 402: return "Payment Required";
|
||||
case 403: return "Forbidden";
|
||||
case 404: return "Not Found";
|
||||
case 405: return "Method Not Allowed";
|
||||
case 406: return "Not Acceptable";
|
||||
case 407: return "Proxy Authentication Required";
|
||||
case 408: return "Request Timeout";
|
||||
case 409: return "Conflict";
|
||||
case 410: return "Gone";
|
||||
case 411: return "Length Required";
|
||||
case 412: return "Precondition Failed";
|
||||
case 413: return "Request Entity Too Large";
|
||||
case 414: return "Request-URI Too Long";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Requested Range Not Satisfiable";
|
||||
case 417: return "Expectation Failed";
|
||||
case 500: return "Internal Server Error";
|
||||
case 501: return "Not Implemented";
|
||||
case 502: return "Bad Gateway";
|
||||
case 503: return "Service Unavailable";
|
||||
case 504: return "Gateway Timeout";
|
||||
case 505: return "HTTP Version Not Supported";
|
||||
|
||||
case 306: return "<reserved>";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<unknown-status>";
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_RESUME_CONTEXT_HPP
|
||||
#define BEAST_HTTP_RESUME_CONTEXT_HPP
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A functor that resumes a write operation.
|
||||
|
||||
An rvalue reference to an object of this type is provided by the
|
||||
write implementation to the `writer` associated with the body of
|
||||
a message being sent.
|
||||
|
||||
If it is desired that the `writer` suspend the write operation (for
|
||||
example, to wait until data is ready), it can take ownership of
|
||||
the resume context using a move. Then, it returns `boost::indeterminate`
|
||||
to indicate that the write operation should suspend. Later, the calling
|
||||
code invokes the resume function and the write operation continues
|
||||
from where it left off.
|
||||
*/
|
||||
using resume_context = std::function<void(void)>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,464 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_RFC2616_HPP
|
||||
#define BEAST_HTTP_RFC2616_HPP
|
||||
|
||||
#include <boost/range/algorithm/equal.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
#include <tuple> // for std::tie, remove ASAP
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace beast {
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
|
||||
/** Routines for performing RFC2616 compliance.
|
||||
RFC2616:
|
||||
Hypertext Transfer Protocol -- HTTP/1.1
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616
|
||||
*/
|
||||
namespace rfc2616 {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct ci_equal_pred
|
||||
{
|
||||
bool operator()(char c1, char c2)
|
||||
{
|
||||
// VFALCO TODO Use a table lookup here
|
||||
return std::tolower(c1) == std::tolower(c2);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** Returns `true` if `c` is linear white space.
|
||||
|
||||
This excludes the CRLF sequence allowed for line continuations.
|
||||
*/
|
||||
inline
|
||||
bool
|
||||
is_lws(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
/** Returns `true` if `c` is any whitespace character. */
|
||||
inline
|
||||
bool
|
||||
is_white(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ': case '\f': case '\n':
|
||||
case '\r': case '\t': case '\v':
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns `true` if `c` is a control character. */
|
||||
inline
|
||||
bool
|
||||
is_control(char c)
|
||||
{
|
||||
return c <= 31 || c >= 127;
|
||||
}
|
||||
|
||||
/** Returns `true` if `c` is a separator. */
|
||||
inline
|
||||
bool
|
||||
is_separator(char c)
|
||||
{
|
||||
// VFALCO Could use a static table
|
||||
switch (c)
|
||||
{
|
||||
case '(': case ')': case '<': case '>': case '@':
|
||||
case ',': case ';': case ':': case '\\': case '"':
|
||||
case '{': case '}': case ' ': case '\t':
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns `true` if `c` is a character. */
|
||||
inline
|
||||
bool
|
||||
is_char(char c)
|
||||
{
|
||||
return c >= 0 && c <= 127;
|
||||
}
|
||||
|
||||
template <class FwdIter>
|
||||
FwdIter
|
||||
trim_left (FwdIter first, FwdIter last)
|
||||
{
|
||||
return std::find_if_not (first, last,
|
||||
is_white);
|
||||
}
|
||||
|
||||
template <class FwdIter>
|
||||
FwdIter
|
||||
trim_right (FwdIter first, FwdIter last)
|
||||
{
|
||||
if (first == last)
|
||||
return last;
|
||||
do
|
||||
{
|
||||
--last;
|
||||
if (! is_white (*last))
|
||||
return ++last;
|
||||
}
|
||||
while (last != first);
|
||||
return first;
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Allocator>
|
||||
void
|
||||
trim_right_in_place (std::basic_string <
|
||||
CharT, Traits, Allocator>& s)
|
||||
{
|
||||
s.resize (std::distance (s.begin(),
|
||||
trim_right (s.begin(), s.end())));
|
||||
}
|
||||
|
||||
template <class FwdIter>
|
||||
std::pair <FwdIter, FwdIter>
|
||||
trim (FwdIter first, FwdIter last)
|
||||
{
|
||||
first = trim_left (first, last);
|
||||
last = trim_right (first, last);
|
||||
return std::make_pair (first, last);
|
||||
}
|
||||
|
||||
template <class String>
|
||||
String
|
||||
trim (String const& s)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
auto first = begin(s);
|
||||
auto last = end(s);
|
||||
std::tie (first, last) = trim (first, last);
|
||||
return { first, last };
|
||||
}
|
||||
|
||||
template <class String>
|
||||
String
|
||||
trim_right (String const& s)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
auto first (begin(s));
|
||||
auto last (end(s));
|
||||
last = trim_right (first, last);
|
||||
return { first, last };
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
trim (std::string const& s)
|
||||
{
|
||||
return trim <std::string> (s);
|
||||
}
|
||||
|
||||
/** Parse a character sequence of values separated by commas.
|
||||
Double quotes and escape sequences will be converted. Excess white
|
||||
space, commas, double quotes, and empty elements are not copied.
|
||||
Format:
|
||||
#(token|quoted-string)
|
||||
Reference:
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2
|
||||
*/
|
||||
template <class FwdIt,
|
||||
class Result = std::vector<
|
||||
std::basic_string<typename
|
||||
std::iterator_traits<FwdIt>::value_type>>,
|
||||
class Char>
|
||||
Result
|
||||
split(FwdIt first, FwdIt last, Char delim)
|
||||
{
|
||||
Result result;
|
||||
using string = typename Result::value_type;
|
||||
FwdIt iter = first;
|
||||
string e;
|
||||
while (iter != last)
|
||||
{
|
||||
if (*iter == '"')
|
||||
{
|
||||
// quoted-string
|
||||
++iter;
|
||||
while (iter != last)
|
||||
{
|
||||
if (*iter == '"')
|
||||
{
|
||||
++iter;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*iter == '\\')
|
||||
{
|
||||
// quoted-pair
|
||||
++iter;
|
||||
if (iter != last)
|
||||
e.append (1, *iter++);
|
||||
}
|
||||
else
|
||||
{
|
||||
// qdtext
|
||||
e.append (1, *iter++);
|
||||
}
|
||||
}
|
||||
if (! e.empty())
|
||||
{
|
||||
result.emplace_back(std::move(e));
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
else if (*iter == delim)
|
||||
{
|
||||
e = trim_right (e);
|
||||
if (! e.empty())
|
||||
{
|
||||
result.emplace_back(std::move(e));
|
||||
e.clear();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
else if (is_lws (*iter))
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.append (1, *iter++);
|
||||
}
|
||||
}
|
||||
|
||||
if (! e.empty())
|
||||
{
|
||||
e = trim_right (e);
|
||||
if (! e.empty())
|
||||
result.emplace_back(std::move(e));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class FwdIt,
|
||||
class Result = std::vector<
|
||||
std::basic_string<typename std::iterator_traits<
|
||||
FwdIt>::value_type>>>
|
||||
Result
|
||||
split_commas(FwdIt first, FwdIt last)
|
||||
{
|
||||
return split(first, last, ',');
|
||||
}
|
||||
|
||||
template <class Result = std::vector<std::string>>
|
||||
Result
|
||||
split_commas(boost::string_ref const& s)
|
||||
{
|
||||
return split_commas(s.begin(), s.end());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Iterates through a comma separated list.
|
||||
|
||||
Meets the requirements of ForwardIterator.
|
||||
|
||||
List defined in rfc2616 2.1.
|
||||
|
||||
@note Values returned may contain backslash escapes.
|
||||
*/
|
||||
class list_iterator
|
||||
{
|
||||
using iter_type = boost::string_ref::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
iter_type end_;
|
||||
boost::string_ref value_;
|
||||
|
||||
public:
|
||||
using value_type = boost::string_ref;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::forward_iterator_tag;
|
||||
|
||||
list_iterator(iter_type begin, iter_type end)
|
||||
: it_(begin)
|
||||
, end_(end)
|
||||
{
|
||||
if(it_ != end_)
|
||||
increment();
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(list_iterator const& other) const
|
||||
{
|
||||
return other.it_ == it_ && other.end_ == end_
|
||||
&& other.value_.size() == value_.size();
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(list_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
list_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
list_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
template<class = void>
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
template<class>
|
||||
void
|
||||
list_iterator::increment()
|
||||
{
|
||||
value_.clear();
|
||||
while(it_ != end_)
|
||||
{
|
||||
if(*it_ == '"')
|
||||
{
|
||||
// quoted-string
|
||||
++it_;
|
||||
if(it_ == end_)
|
||||
return;
|
||||
if(*it_ != '"')
|
||||
{
|
||||
auto start = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == end_)
|
||||
{
|
||||
value_ = boost::string_ref(
|
||||
&*start, std::distance(start, it_));
|
||||
return;
|
||||
}
|
||||
if(*it_ == '"')
|
||||
{
|
||||
value_ = boost::string_ref(
|
||||
&*start, std::distance(start, it_));
|
||||
++it_;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
++it_;
|
||||
}
|
||||
else if(*it_ == ',')
|
||||
{
|
||||
it_++;
|
||||
continue;
|
||||
}
|
||||
else if(is_lws(*it_))
|
||||
{
|
||||
++it_;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto start = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == end_ ||
|
||||
*it_ == ',' ||
|
||||
is_lws(*it_))
|
||||
{
|
||||
value_ = boost::string_ref(
|
||||
&*start, std::distance(start, it_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if two strings are equal.
|
||||
|
||||
A case-insensitive comparison is used.
|
||||
*/
|
||||
inline
|
||||
bool
|
||||
ci_equal(boost::string_ref s1, boost::string_ref s2)
|
||||
{
|
||||
return boost::range::equal(s1, s2,
|
||||
detail::ci_equal_pred{});
|
||||
}
|
||||
|
||||
/** Returns a range representing the list. */
|
||||
inline
|
||||
boost::iterator_range<list_iterator>
|
||||
make_list(boost::string_ref const& field)
|
||||
{
|
||||
return boost::iterator_range<list_iterator>{
|
||||
list_iterator{field.begin(), field.end()},
|
||||
list_iterator{field.end(), field.end()}};
|
||||
}
|
||||
|
||||
/** Returns true if the specified token exists in the list.
|
||||
|
||||
A case-insensitive comparison is used.
|
||||
*/
|
||||
template<class = void>
|
||||
bool
|
||||
token_in_list(boost::string_ref const& value,
|
||||
boost::string_ref const& token)
|
||||
{
|
||||
for(auto const& item : make_list(value))
|
||||
if(ci_equal(item, token))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // rfc2616
|
||||
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_RFC7230_HPP
|
||||
#define BEAST_HTTP_RFC7230_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace rfc7230 {
|
||||
|
||||
|
||||
|
||||
} // rfc7230
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_STREAMBUF_BODY_HPP
|
||||
#define BEAST_HTTP_STREAMBUF_BODY_HPP
|
||||
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/buffer_cat.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A Body represented by a Streambuf
|
||||
*/
|
||||
template<class Streambuf>
|
||||
struct basic_streambuf_body
|
||||
{
|
||||
/// The type of the `message::body` member
|
||||
using value_type = Streambuf;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& sb_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Allocator>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
basic_streambuf_body, Allocator>& m) noexcept
|
||||
: sb_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
sb_.commit(buffer_copy(
|
||||
sb_.prepare(size), buffer(data, size)));
|
||||
}
|
||||
};
|
||||
|
||||
class writer
|
||||
{
|
||||
Streambuf const& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Allocator>
|
||||
explicit
|
||||
writer(message<isRequest, basic_streambuf_body,
|
||||
Allocator> const& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t
|
||||
content_length() const
|
||||
{
|
||||
return body_.size();
|
||||
}
|
||||
|
||||
template<class Write>
|
||||
boost::tribool
|
||||
operator()(resume_context&&, error_code&, Write&& write)
|
||||
{
|
||||
write(body_.data());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using streambuf_body = basic_streambuf_body<streambuf>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_STRING_BODY_HPP
|
||||
#define BEAST_HTTP_STRING_BODY_HPP
|
||||
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/resume_context.hpp>
|
||||
#include <beast/buffer_cat.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A Body represented by a std::string.
|
||||
*/
|
||||
struct string_body
|
||||
{
|
||||
/// The type of the `message::body` member
|
||||
using value_type = std::string;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& s_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Allocator>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
string_body, Allocator>& m) noexcept
|
||||
: s_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
{
|
||||
auto const n = s_.size();
|
||||
s_.resize(n + size);
|
||||
std::memcpy(&s_[n], data, size);
|
||||
}
|
||||
};
|
||||
|
||||
class writer
|
||||
{
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Allocator>
|
||||
explicit
|
||||
writer(message<isRequest, string_body, Allocator> const& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t
|
||||
content_length() const
|
||||
{
|
||||
return body_.size();
|
||||
}
|
||||
|
||||
template<class Write>
|
||||
boost::tribool
|
||||
operator()(resume_context&&, error_code&, Write&& write)
|
||||
{
|
||||
write(boost::asio::buffer(body_));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_WRITE_HPP
|
||||
#define BEAST_HTTP_WRITE_HPP
|
||||
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/async_completion.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Write a HTTP message to a stream.
|
||||
|
||||
@param stream The stream to send the message on.
|
||||
|
||||
@param msg The message to send.
|
||||
|
||||
@throws boost::system::error code on failure.
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Headers> const& msg)
|
||||
{
|
||||
error_code ec;
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
throw boost::system::system_error{ec};
|
||||
}
|
||||
|
||||
/** Write a HTTP message to a stream.
|
||||
|
||||
@param stream The stream to send the message on.
|
||||
|
||||
@param msg The message to send.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Headers> const& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Start writing a HTTP message to a stream asynchronously.
|
||||
|
||||
@param stream The stream to send the message on.
|
||||
|
||||
@param msg The message to send.
|
||||
|
||||
@param token 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 message must remain valid at least until the
|
||||
completion handler is called, no copies are made.
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class WriteHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_write(AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Headers> const& msg,
|
||||
WriteHandler&& handler);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/write.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,892 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP
|
||||
#define BEAST_IMPL_BASIC_STREAMBUF_IPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/* These diagrams illustrate the layout and state variables.
|
||||
|
||||
1 Input and output contained entirely in one element:
|
||||
|
||||
0 out_
|
||||
|<-------------+------------------------------------------->|
|
||||
in_pos_ out_pos_ out_end_
|
||||
|
||||
|
||||
2 Output contained in first and second elements:
|
||||
|
||||
out_
|
||||
|<------+----------+------->| |<----------+-------------->|
|
||||
in_pos_ out_pos_ out_end_
|
||||
|
||||
|
||||
3 Output contained in the second element:
|
||||
|
||||
out_
|
||||
|<------------+------------>| |<----+-------------------->|
|
||||
in_pos_ out_pos_ out_end_
|
||||
|
||||
|
||||
4 Output contained in second and third elements:
|
||||
|
||||
out_
|
||||
|<-----+-------->| |<-------+------>| |<--------------->|
|
||||
in_pos_ out_pos_ out_end_
|
||||
|
||||
|
||||
5 Input sequence is empty:
|
||||
|
||||
out_
|
||||
|<------+------------------>| |<-----------+------------->|
|
||||
out_pos_ out_end_
|
||||
in_pos_
|
||||
|
||||
|
||||
6 Output sequence is empty:
|
||||
|
||||
out_
|
||||
|<------+------------------>| |<------+------------------>|
|
||||
in_pos_ out_pos_
|
||||
out_end_
|
||||
|
||||
|
||||
7 The end of output can point to the end of an element.
|
||||
But out_pos_ should never point to the end:
|
||||
|
||||
out_
|
||||
|<------+------------------>| |<------+------------------>|
|
||||
in_pos_ out_pos_ out_end_
|
||||
|
||||
|
||||
8 When the input sequence entirely fills the last element and
|
||||
the output sequence is empty, out_ will point to the end of
|
||||
the list of buffers, and out_pos_ and out_end_ will be 0:
|
||||
|
||||
|
||||
|<------+------------------>| out_ == list_.end()
|
||||
in_pos_ out_pos_ == 0
|
||||
out_end_ == 0
|
||||
*/
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::element
|
||||
: public boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
{
|
||||
using size_type = typename std::allocator_traits<Allocator>::size_type;
|
||||
|
||||
size_type const size_;
|
||||
|
||||
public:
|
||||
element(element const&) = delete;
|
||||
element& operator=(element const&) = delete;
|
||||
|
||||
explicit
|
||||
element(size_type n)
|
||||
: size_(n)
|
||||
{
|
||||
}
|
||||
|
||||
size_type
|
||||
size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
char*
|
||||
data() const
|
||||
{
|
||||
return const_cast<char*>(
|
||||
reinterpret_cast<char const*>(this+1));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::const_buffers_type
|
||||
{
|
||||
basic_streambuf const* sb_ = nullptr;
|
||||
|
||||
friend class basic_streambuf;
|
||||
|
||||
explicit
|
||||
const_buffers_type(basic_streambuf const& sb);
|
||||
|
||||
public:
|
||||
/// Why?
|
||||
using value_type = boost::asio::const_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
const_buffers_type() = default;
|
||||
const_buffers_type(const_buffers_type const&) = default;
|
||||
const_buffers_type& operator=(const_buffers_type const&) = default;
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::mutable_buffers_type
|
||||
{
|
||||
basic_streambuf const* sb_;
|
||||
|
||||
friend class basic_streambuf;
|
||||
|
||||
explicit
|
||||
mutable_buffers_type(basic_streambuf const& sb);
|
||||
|
||||
public:
|
||||
using value_type = mutable_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
mutable_buffers_type() = default;
|
||||
mutable_buffers_type(mutable_buffers_type const&) = default;
|
||||
mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::const_buffers_type::const_iterator
|
||||
{
|
||||
basic_streambuf const* sb_ = nullptr;
|
||||
typename list_type::const_iterator it_;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename const_buffers_type::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
const_iterator(basic_streambuf const& sb,
|
||||
typename list_type::const_iterator const& it)
|
||||
: sb_(&sb)
|
||||
, it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return sb_ == other.sb_ && it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
auto const& e = *it_;
|
||||
return value_type{e.data(),
|
||||
(sb_->out_ == sb_->list_.end() ||
|
||||
&e != &*sb_->out_) ? e.size() : sb_->out_pos_} +
|
||||
(&e == &*sb_->list_.begin() ? sb_->in_pos_ : 0);
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::const_buffers_type::const_buffers_type(
|
||||
basic_streambuf const& sb)
|
||||
: sb_(&sb)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::const_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->list_.begin()};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::const_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->out_ ==
|
||||
sb_->list_.end() ? sb_->list_.end() :
|
||||
std::next(sb_->out_)};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::mutable_buffers_type::const_iterator
|
||||
{
|
||||
basic_streambuf const* sb_ = nullptr;
|
||||
typename list_type::const_iterator it_;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename mutable_buffers_type::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
const_iterator(basic_streambuf const& sb,
|
||||
typename list_type::const_iterator const& it)
|
||||
: sb_(&sb)
|
||||
, it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return sb_ == other.sb_ && it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
auto const& e = *it_;
|
||||
return value_type{e.data(),
|
||||
&e == &*std::prev(sb_->list_.end()) ?
|
||||
sb_->out_end_ : e.size()} +
|
||||
(&e == &*sb_->out_ ? sb_->out_pos_ : 0);
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::mutable_buffers_type::mutable_buffers_type(
|
||||
basic_streambuf const& sb)
|
||||
: sb_(&sb)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::mutable_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->out_};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::mutable_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->list_.end()};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::~basic_streambuf()
|
||||
{
|
||||
delete_list();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf&& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
std::move(other.member()))
|
||||
, alloc_size_(other.alloc_size_)
|
||||
, in_size_(other.in_size_)
|
||||
, in_pos_(other.in_pos_)
|
||||
, out_pos_(other.out_pos_)
|
||||
, out_end_(other.out_end_)
|
||||
{
|
||||
auto const at_end =
|
||||
other.out_ == other.list_.end();
|
||||
list_ = std::move(other.list_);
|
||||
out_ = at_end ? list_.end() : other.out_;
|
||||
other.in_size_ = 0;
|
||||
other.out_ = other.list_.end();
|
||||
other.in_pos_ = 0;
|
||||
other.out_pos_ = 0;
|
||||
other.out_end_ = 0;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf&& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
if(this->member() != other.member())
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
else
|
||||
move_assign(other, std::true_type{});
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf&& other) -> basic_streambuf&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
// VFALCO If any memory allocated we could use it first?
|
||||
clear();
|
||||
alloc_size_ = other.alloc_size_;
|
||||
move_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_move_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf const& other)
|
||||
: basic_streambuf(other.alloc_size_,
|
||||
alloc_traits::select_on_container_copy_construction(other.member()))
|
||||
{
|
||||
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf const& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf const& other) ->
|
||||
basic_streambuf&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
using boost::asio::buffer_copy;
|
||||
clear();
|
||||
copy_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_copy_assignment::value>{});
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
basic_streambuf<OtherAlloc> const& other)
|
||||
: basic_streambuf(other.alloc_size_)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
basic_streambuf<OtherAlloc> const& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf<OtherAlloc> const& other) ->
|
||||
basic_streambuf&
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
clear();
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
std::size_t alloc_size, Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, out_(list_.end())
|
||||
, alloc_size_(alloc_size)
|
||||
{
|
||||
if(alloc_size <= 0)
|
||||
throw std::invalid_argument(
|
||||
"basic_streambuf: invalid alloc_size");
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::prepare(size_type n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
list_type reuse;
|
||||
if(out_ != list_.end())
|
||||
{
|
||||
if(out_ != list_.iterator_to(list_.back()))
|
||||
{
|
||||
out_end_ = out_->size();
|
||||
reuse.splice(reuse.end(), list_,
|
||||
std::next(out_), list_.end());
|
||||
debug_check();
|
||||
}
|
||||
auto const avail = out_->size() - out_pos_;
|
||||
if(n > avail)
|
||||
{
|
||||
out_end_ = out_->size();
|
||||
n -= avail;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_end_ = out_pos_ + n;
|
||||
n = 0;
|
||||
}
|
||||
debug_check();
|
||||
}
|
||||
while(n > 0 && ! reuse.empty())
|
||||
{
|
||||
auto& e = reuse.front();
|
||||
reuse.erase(reuse.iterator_to(e));
|
||||
list_.push_back(e);
|
||||
if(n > e.size())
|
||||
{
|
||||
out_end_ = e.size();
|
||||
n -= e.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
out_end_ = n;
|
||||
n = 0;
|
||||
}
|
||||
debug_check();
|
||||
}
|
||||
while(n > 0)
|
||||
{
|
||||
auto const size = std::max(alloc_size_, n);
|
||||
auto& e = *reinterpret_cast<element*>(
|
||||
alloc_traits::allocate(this->member(),
|
||||
sizeof(element) + size));
|
||||
alloc_traits::construct(this->member(), &e, size);
|
||||
list_.push_back(e);
|
||||
if(out_ == list_.end())
|
||||
out_ = list_.iterator_to(e);
|
||||
if(n > e.size())
|
||||
{
|
||||
out_end_ = e.size();
|
||||
n -= e.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
out_end_ = n;
|
||||
n = 0;
|
||||
}
|
||||
debug_check();
|
||||
}
|
||||
for(auto it = reuse.begin(); it != reuse.end();)
|
||||
{
|
||||
auto& e = *it++;
|
||||
reuse.erase(list_.iterator_to(e));
|
||||
auto const len = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<std::uint8_t*>(&e), len);
|
||||
}
|
||||
return mutable_buffers_type(*this);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::commit(size_type n)
|
||||
{
|
||||
if(list_.empty())
|
||||
return;
|
||||
if(out_ == list_.end())
|
||||
return;
|
||||
auto const back =
|
||||
list_.iterator_to(list_.back());
|
||||
while(out_ != back)
|
||||
{
|
||||
auto const avail =
|
||||
out_->size() - out_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
out_pos_ += n;
|
||||
in_size_ += n;
|
||||
debug_check();
|
||||
return;
|
||||
}
|
||||
++out_;
|
||||
n -= avail;
|
||||
out_pos_ = 0;
|
||||
in_size_ += avail;
|
||||
debug_check();
|
||||
}
|
||||
|
||||
n = std::min(n, out_end_ - out_pos_);
|
||||
out_pos_ += n;
|
||||
in_size_ += n;
|
||||
if(out_pos_ == out_->size())
|
||||
{
|
||||
++out_;
|
||||
out_pos_ = 0;
|
||||
out_end_ = 0;
|
||||
}
|
||||
debug_check();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type(*this);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::consume(size_type n)
|
||||
{
|
||||
if(list_.empty())
|
||||
return;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(list_.begin() != out_)
|
||||
{
|
||||
auto const avail = list_.front().size() - in_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
in_size_ -= n;
|
||||
in_pos_ += n;
|
||||
debug_check();
|
||||
break;
|
||||
}
|
||||
n -= avail;
|
||||
in_size_ -= avail;
|
||||
in_pos_ = 0;
|
||||
auto& e = list_.front();
|
||||
list_.erase(list_.iterator_to(e));
|
||||
auto const len = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<std::uint8_t*>(&e), len);
|
||||
debug_check();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const avail = out_pos_ - in_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
in_size_ -= n;
|
||||
in_pos_ += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_size_ = 0;
|
||||
if(out_ != list_.iterator_to(list_.back()) ||
|
||||
out_pos_ != out_end_)
|
||||
{
|
||||
in_pos_ = out_pos_;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Input and output sequences are empty, reuse buffer.
|
||||
// Alternatively we could deallocate it.
|
||||
in_pos_ = 0;
|
||||
out_pos_ = 0;
|
||||
out_end_ = 0;
|
||||
}
|
||||
}
|
||||
debug_check();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::clear()
|
||||
{
|
||||
delete_list();
|
||||
list_.clear();
|
||||
out_ = list_.begin();
|
||||
in_size_ = 0;
|
||||
in_pos_ = 0;
|
||||
out_pos_ = 0;
|
||||
out_end_ = 0;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
move_assign(basic_streambuf& other, std::false_type)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
other.clear();
|
||||
}
|
||||
else
|
||||
move_assign(other, std::true_type{});
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
move_assign(basic_streambuf& other, std::true_type)
|
||||
{
|
||||
this->member() = std::move(other.member());
|
||||
auto const at_end =
|
||||
other.out_ == other.list_.end();
|
||||
list_ = std::move(other.list_);
|
||||
out_ = at_end ? list_.end() : other.out_;
|
||||
|
||||
in_size_ = other.in_size_;
|
||||
in_pos_ = other.in_pos_;
|
||||
out_pos_ = other.out_pos_;
|
||||
out_end_ = other.out_end_;
|
||||
|
||||
other.in_size_ = 0;
|
||||
other.out_ = other.list_.end();
|
||||
other.in_pos_ = 0;
|
||||
other.out_pos_ = 0;
|
||||
other.out_end_ = 0;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
copy_assign(basic_streambuf const& other, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
copy_assign(basic_streambuf const& other, std::true_type)
|
||||
{
|
||||
this->member() = other.member();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::delete_list()
|
||||
{
|
||||
for(auto iter = list_.begin(); iter != list_.end();)
|
||||
{
|
||||
auto& e = *iter++;
|
||||
auto const n = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<std::uint8_t*>(&e), n);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of bytes which can be
|
||||
// prepared without causing a memory allocation.
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_streambuf<Allocator>::prepare_size() const
|
||||
{
|
||||
auto pos = out_;
|
||||
if(pos == list_.end())
|
||||
return 0;
|
||||
auto n = pos->size() - out_pos_;
|
||||
while(++pos != list_.end())
|
||||
n += pos->size();
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::debug_check() const
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
using boost::asio::buffer_size;
|
||||
assert(buffer_size(data()) == in_size_);
|
||||
if(list_.empty())
|
||||
{
|
||||
assert(in_pos_ == 0);
|
||||
assert(in_size_ == 0);
|
||||
assert(out_pos_ == 0);
|
||||
assert(out_end_ == 0);
|
||||
assert(out_ == list_.end());
|
||||
return;
|
||||
}
|
||||
|
||||
auto const& front = list_.front();
|
||||
|
||||
assert(in_pos_ < front.size());
|
||||
|
||||
if(out_ == list_.end())
|
||||
{
|
||||
assert(out_pos_ == 0);
|
||||
assert(out_end_ == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const& out = *out_;
|
||||
auto const& back = list_.back();
|
||||
|
||||
assert(out_end_ <= back.size());
|
||||
assert(out_pos_ < out.size());
|
||||
assert(&out != &front || out_pos_ >= in_pos_);
|
||||
assert(&out != &front || out_pos_ - in_pos_ == in_size_);
|
||||
assert(&out != &back || out_pos_ <= out_end_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class Alloc, class T>
|
||||
basic_streambuf<Alloc>&
|
||||
operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
std::stringstream ss;
|
||||
ss << t;
|
||||
auto const& s = ss.str();
|
||||
streambuf.commit(buffer_copy(
|
||||
streambuf.prepare(s.size()), buffer(s)));
|
||||
return streambuf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
read_size_helper(basic_streambuf<
|
||||
Allocator> const& streambuf, std::size_t max_size)
|
||||
{
|
||||
return std::min<std::size_t>(max_size,
|
||||
std::max<std::size_t>(512, streambuf.prepare_size()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::string
|
||||
to_string(basic_streambuf<Allocator> const& streambuf)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
std::string s;
|
||||
s.resize(streambuf.size());
|
||||
buffer_copy(
|
||||
buffer(&s[0], s.size()), streambuf.data());
|
||||
return s;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,520 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_BUFFERS_ADAPTER_IPP
|
||||
#define BEAST_IMPL_BUFFERS_ADAPTER_IPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class Buffers>
|
||||
class buffers_adapter<Buffers>::const_buffers_type
|
||||
{
|
||||
buffers_adapter const* ba_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
const_buffers_type() = default;
|
||||
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 Buffers>
|
||||
class buffers_adapter<Buffers>::const_buffers_type::const_iterator
|
||||
{
|
||||
iter_type it_;
|
||||
buffers_adapter const* ba_;
|
||||
|
||||
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 Buffers>
|
||||
inline
|
||||
auto
|
||||
buffers_adapter<Buffers>::const_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*ba_, ba_->begin_};
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
inline
|
||||
auto
|
||||
buffers_adapter<Buffers>::const_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*ba_, ba_->out_ ==
|
||||
ba_->end_ ? ba_->end_ : std::next(ba_->out_)};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Buffers>
|
||||
class buffers_adapter<Buffers>::mutable_buffers_type
|
||||
{
|
||||
buffers_adapter const* ba_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::mutable_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
mutable_buffers_type() = default;
|
||||
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 Buffers>
|
||||
class buffers_adapter<Buffers>::mutable_buffers_type::const_iterator
|
||||
{
|
||||
iter_type it_;
|
||||
buffers_adapter const* ba_;
|
||||
|
||||
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 Buffers>
|
||||
inline
|
||||
auto
|
||||
buffers_adapter<Buffers>::mutable_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*ba_, ba_->out_};
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
inline
|
||||
auto
|
||||
buffers_adapter<Buffers>::mutable_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*ba_, ba_->end_};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Buffers>
|
||||
buffers_adapter<Buffers>::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 Buffers>
|
||||
buffers_adapter<Buffers>::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 Buffers>
|
||||
auto
|
||||
buffers_adapter<Buffers>::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 Buffers>
|
||||
auto
|
||||
buffers_adapter<Buffers>::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 Buffers>
|
||||
buffers_adapter<Buffers>::buffers_adapter(
|
||||
Buffers const& bs)
|
||||
: bs_(bs)
|
||||
, begin_(bs_.begin())
|
||||
, out_(bs_.begin())
|
||||
, end_(bs_.begin())
|
||||
, max_size_(boost::asio::buffer_size(bs_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
auto
|
||||
buffers_adapter<Buffers>::prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
static_assert(is_mutable,
|
||||
"Operation not valid for ConstBufferSequence");
|
||||
end_ = out_;
|
||||
if(end_ != bs_.end())
|
||||
{
|
||||
auto size = buffer_size(*end_) - out_pos_;
|
||||
if(n > size)
|
||||
{
|
||||
n -= size;
|
||||
while(++end_ != bs_.end())
|
||||
{
|
||||
size = buffer_size(*end_);
|
||||
if(n < size)
|
||||
{
|
||||
out_end_ = n;
|
||||
n = 0;
|
||||
++end_;
|
||||
break;
|
||||
}
|
||||
n -= size;
|
||||
out_end_ = size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++end_;
|
||||
out_end_ = out_pos_ + n;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
if(n > 0)
|
||||
throw std::length_error(
|
||||
"no space in buffers_adapter");
|
||||
return mutable_buffers_type{*this};
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
void
|
||||
buffers_adapter<Buffers>::commit(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
static_assert(is_mutable,
|
||||
"Operation not valid for ConstBufferSequence");
|
||||
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 Buffers>
|
||||
inline
|
||||
auto
|
||||
buffers_adapter<Buffers>::data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type{*this};
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
void
|
||||
buffers_adapter<Buffers>::consume(std::size_t n)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(begin_ != out_)
|
||||
{
|
||||
auto const avail =
|
||||
buffer_size(*begin_) - in_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
in_size_ -= n;
|
||||
in_pos_ += n;
|
||||
break;
|
||||
}
|
||||
n -= avail;
|
||||
in_size_ -= avail;
|
||||
in_pos_ = 0;
|
||||
++begin_;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const avail = out_pos_ - in_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
in_size_ -= n;
|
||||
in_pos_ += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_size_ -= avail;
|
||||
if(out_pos_ != out_end_||
|
||||
out_ != std::prev(bs_.end()))
|
||||
{
|
||||
in_pos_ = out_pos_;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the whole buffer now.
|
||||
in_pos_ = 0;
|
||||
out_pos_ = 0;
|
||||
out_end_ = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP
|
||||
#define BEAST_IMPL_CONSUMING_BUFFERS_IPP
|
||||
|
||||
#include <beast/type_check.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
class consuming_buffers<Buffers, ValueType>::const_iterator
|
||||
{
|
||||
friend class consuming_buffers<Buffers, ValueType>;
|
||||
|
||||
using iter_type =
|
||||
typename Buffers::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
consuming_buffers const* b_;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename std::iterator_traits<iter_type>::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return b_ == other.b_ && it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
if(it_ == b_->begin_)
|
||||
return *it_ + b_->skip_;
|
||||
return *it_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
const_iterator(consuming_buffers const& b,
|
||||
iter_type it)
|
||||
: it_(it)
|
||||
, b_(&b)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
consuming_buffers<Buffers, ValueType>::
|
||||
consuming_buffers(consuming_buffers&& other)
|
||||
: consuming_buffers(std::move(other),
|
||||
std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
consuming_buffers<Buffers, ValueType>::
|
||||
consuming_buffers(consuming_buffers const& other)
|
||||
: consuming_buffers(other,
|
||||
std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
auto
|
||||
consuming_buffers<Buffers, ValueType>::
|
||||
operator=(consuming_buffers&& other) ->
|
||||
consuming_buffers&
|
||||
{
|
||||
auto const nbegin = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_);
|
||||
bs_ = std::move(other.bs_);
|
||||
begin_ = std::next(bs_.begin(), nbegin);
|
||||
skip_ = other.skip_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
auto
|
||||
consuming_buffers<Buffers, ValueType>::
|
||||
operator=(consuming_buffers const& other) ->
|
||||
consuming_buffers&
|
||||
{
|
||||
auto const nbegin = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.begin_);
|
||||
bs_ = other.bs_;
|
||||
begin_ = std::next(bs_.begin(), nbegin);
|
||||
skip_ = other.skip_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
consuming_buffers<Buffers, ValueType>::
|
||||
consuming_buffers(Buffers const& bs)
|
||||
: bs_(bs)
|
||||
, begin_(bs_.begin())
|
||||
{
|
||||
static_assert(is_BufferSequence<Buffers, ValueType>::value,
|
||||
"BufferSequence requirements not met");
|
||||
}
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
auto
|
||||
consuming_buffers<Buffers, ValueType>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, begin_};
|
||||
}
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
auto
|
||||
consuming_buffers<Buffers, ValueType>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, bs_.end()};
|
||||
}
|
||||
|
||||
template<class Buffers, class ValueType>
|
||||
void
|
||||
consuming_buffers<Buffers, ValueType>::consume(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
for(;n > 0 && begin_ != bs_.end(); ++begin_)
|
||||
{
|
||||
auto const len =
|
||||
buffer_size(*begin_) - skip_;
|
||||
if(n < len)
|
||||
{
|
||||
skip_ += n;
|
||||
break;
|
||||
}
|
||||
n -= len;
|
||||
skip_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers, typename Buffers::value_type>
|
||||
consumed_buffers(Buffers const& bs, std::size_t n)
|
||||
{
|
||||
consuming_buffers<Buffers> cb(bs);
|
||||
cb.consume(n);
|
||||
return cb;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_PREPARE_BUFFERS_IPP
|
||||
#define BEAST_IMPL_PREPARE_BUFFERS_IPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class BufferSequence>
|
||||
void
|
||||
prepared_buffers<BufferSequence>::
|
||||
setup(std::size_t n)
|
||||
{
|
||||
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
|
||||
{
|
||||
auto const len =
|
||||
boost::asio::buffer_size(*end_);
|
||||
if(n <= len)
|
||||
{
|
||||
size_ = n;
|
||||
back_ = end_++;
|
||||
return;
|
||||
}
|
||||
n -= len;
|
||||
}
|
||||
size_ = 0;
|
||||
back_ = end_;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
class prepared_buffers<BufferSequence>::const_iterator
|
||||
{
|
||||
friend class prepared_buffers<BufferSequence>;
|
||||
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
prepared_buffers const* b_;
|
||||
typename BufferSequence::const_iterator it_;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename std::iterator_traits<iter_type>::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return b_ == other.b_ && it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
if(it_ == b_->back_)
|
||||
return prepare_buffer(b_->size_, *it_);
|
||||
return *it_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
const_iterator(prepared_buffers const& b,
|
||||
bool at_end)
|
||||
: b_(&b)
|
||||
, it_(at_end ? b.end_ : b.bs_.begin())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(prepared_buffers&& other)
|
||||
: prepared_buffers(std::move(other),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.back_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(prepared_buffers const& other)
|
||||
: prepared_buffers(other,
|
||||
std::distance<iter_type>(other.bs_.begin(), other.back_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::
|
||||
operator=(prepared_buffers&& other) ->
|
||||
prepared_buffers&
|
||||
{
|
||||
auto const nback = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.back_);
|
||||
auto const nend = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_);
|
||||
bs_ = std::move(other.bs_);
|
||||
back_ = std::next(bs_.begin(), nback);
|
||||
end_ = std::next(bs_.begin(), nend);
|
||||
size_ = other.size_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::
|
||||
operator=(prepared_buffers const& other) ->
|
||||
prepared_buffers&
|
||||
{
|
||||
auto const nback = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.back_);
|
||||
auto const nend = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.end_);
|
||||
bs_ = other.bs_;
|
||||
back_ = std::next(bs_.begin(), nback);
|
||||
end_ = std::next(bs_.begin(), nend);
|
||||
size_ = other.size_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(std::size_t n, BufferSequence const& bs)
|
||||
: bs_(bs)
|
||||
{
|
||||
setup(n);
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
prepared_buffers<BufferSequence>
|
||||
prepare_buffers(std::size_t n, BufferSequence const& buffers)
|
||||
{
|
||||
return prepared_buffers<BufferSequence>(n, buffers);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,304 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STATIC_STREAMBUF_IPP
|
||||
#define BEAST_IMPL_STATIC_STREAMBUF_IPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
|
||||
class static_streambuf::const_buffers_type
|
||||
{
|
||||
std::size_t n_;
|
||||
std::uint8_t const* p_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
const_buffers_type() = default;
|
||||
const_buffers_type(
|
||||
const_buffers_type const&) = default;
|
||||
const_buffers_type& operator=(
|
||||
const_buffers_type const&) = default;
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
private:
|
||||
friend class static_streambuf;
|
||||
|
||||
const_buffers_type(
|
||||
std::uint8_t const* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class static_streambuf::const_buffers_type::const_iterator
|
||||
{
|
||||
std::size_t n_;
|
||||
std::uint8_t const* p_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_buffer;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return p_ == other.p_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return value_type{p_, n_};
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
p_ += n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
p_ -= n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class const_buffers_type;
|
||||
|
||||
const_iterator(
|
||||
std::uint8_t const* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::const_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_, n_};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::const_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_ + n_, n_};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class static_streambuf::mutable_buffers_type
|
||||
{
|
||||
std::size_t n_;
|
||||
std::uint8_t* p_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::mutable_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
mutable_buffers_type() = default;
|
||||
mutable_buffers_type(
|
||||
mutable_buffers_type const&) = default;
|
||||
mutable_buffers_type& operator=(
|
||||
mutable_buffers_type const&) = default;
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
private:
|
||||
friend class static_streambuf;
|
||||
|
||||
mutable_buffers_type(
|
||||
std::uint8_t* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class static_streambuf::mutable_buffers_type::const_iterator
|
||||
{
|
||||
std::size_t n_;
|
||||
std::uint8_t* p_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::mutable_buffer;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return p_ == other.p_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return value_type{p_, n_};
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
p_ += n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
p_ -= n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class mutable_buffers_type;
|
||||
|
||||
const_iterator(std::uint8_t* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::mutable_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_, n_};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::mutable_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_ + n_, n_};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(n > static_cast<std::size_t>(end_ - out_))
|
||||
throw std::length_error("no space in streambuf");
|
||||
last_ = out_ + n;
|
||||
return mutable_buffers_type{out_, n};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type{in_,
|
||||
static_cast<std::size_t>(out_ - in_)};
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,256 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STREAMBUF_READSTREAM_IPP
|
||||
#define BEAST_IMPL_STREAMBUF_READSTREAM_IPP
|
||||
|
||||
#include <beast/bind_handler.hpp>
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/type_check.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class Stream, class Streambuf>
|
||||
template<class MutableBufferSequence, class Handler>
|
||||
class streambuf_readstream<
|
||||
Stream, Streambuf>::read_some_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
streambuf_readstream& brs;
|
||||
MutableBufferSequence bs;
|
||||
Handler h;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_,
|
||||
streambuf_readstream& brs_,
|
||||
MutableBufferSequence const& bs_)
|
||||
: brs(brs_)
|
||||
, bs(bs_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
read_some_op(read_some_op&&) = default;
|
||||
read_some_op(read_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_some_op(DeducedHandler&& h, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h),
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_some_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_some_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_some_op* op)
|
||||
{
|
||||
return boost_asio_handler_cont_helpers::
|
||||
is_continuation(op->d_->h);
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class Streambuf>
|
||||
template<class MutableBufferSequence, class Handler>
|
||||
void
|
||||
streambuf_readstream<Stream, Streambuf>::
|
||||
read_some_op<MutableBufferSequence, Handler>::operator()(
|
||||
error_code const& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
auto& d = *d_;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.brs.sb_.size() == 0)
|
||||
{
|
||||
d.state =
|
||||
d.brs.size_ > 0 ? 2 : 1;
|
||||
break;
|
||||
}
|
||||
d.state = 4;
|
||||
d.brs.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// read (unbuffered)
|
||||
d.state = 99;
|
||||
d.brs.next_layer_.async_read_some(
|
||||
d.bs, std::move(*this));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
// read
|
||||
d.state = 3;
|
||||
d.brs.next_layer_.async_read_some(
|
||||
d.brs.sb_.prepare(d.brs.size_),
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got data
|
||||
case 3:
|
||||
d.state = 4;
|
||||
d.brs.sb_.commit(bytes_transferred);
|
||||
break;
|
||||
|
||||
// copy
|
||||
case 4:
|
||||
bytes_transferred =
|
||||
boost::asio::buffer_copy(
|
||||
d.bs, d.brs.sb_.data());
|
||||
d.brs.sb_.consume(bytes_transferred);
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.h(ec, bytes_transferred);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class Streambuf>
|
||||
template<class... Args>
|
||||
streambuf_readstream<Stream, Streambuf>::
|
||||
streambuf_readstream(Args&&... args)
|
||||
: next_layer_(std::forward<Args>(args)...)
|
||||
{
|
||||
static_assert(is_Stream<next_layer_type>::value,
|
||||
"Stream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
}
|
||||
|
||||
template<class Stream, class Streambuf>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
auto
|
||||
streambuf_readstream<Stream, Streambuf>::
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler) ->
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
{
|
||||
static_assert(is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(is_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 Streambuf>
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
streambuf_readstream<Stream, Streambuf>::
|
||||
read_some(
|
||||
MutableBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_MutableBufferSequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
auto n = read_some(buffers, ec);
|
||||
if(ec)
|
||||
throw boost::system::system_error{ec};
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class Stream, class Streambuf>
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
streambuf_readstream<Stream, Streambuf>::
|
||||
read_some(MutableBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_MutableBufferSequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
if(buffer_size(buffers) == 0)
|
||||
return 0;
|
||||
if(size_ == 0)
|
||||
return next_layer_.read_some(buffers, ec);
|
||||
if(sb_.size() == 0)
|
||||
{
|
||||
sb_.commit(next_layer_.read_some(
|
||||
sb_.prepare(size_), ec));
|
||||
if(ec)
|
||||
return 0;
|
||||
}
|
||||
auto bytes_transferred =
|
||||
buffer_copy(buffers, sb_.data());
|
||||
sb_.consume(bytes_transferred);
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<class Stream, class Streambuf>
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
auto
|
||||
streambuf_readstream<Stream, Streambuf>::
|
||||
async_read_some(
|
||||
MutableBufferSequence const& buffers,
|
||||
ReadHandler&& handler) ->
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
{
|
||||
static_assert(is_MutableBufferSequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
ReadHandler, void(error_code, std::size_t)
|
||||
> completion(handler);
|
||||
read_some_op<MutableBufferSequence,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, *this, buffers};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PLACEHOLDERS_HPP
|
||||
#define BEAST_PLACEHOLDERS_HPP
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace beast {
|
||||
namespace asio {
|
||||
|
||||
namespace placeholders {
|
||||
// asio placeholders that work with std::bind
|
||||
namespace {
|
||||
static auto const error (std::placeholders::_1);
|
||||
static auto const bytes_transferred (std::placeholders::_2);
|
||||
static auto const iterator (std::placeholders::_2);
|
||||
static auto const signal_number (std::placeholders::_2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PREPARE_BUFFERS_HPP
|
||||
#define BEAST_PREPARE_BUFFERS_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Get a trimmed const buffer.
|
||||
|
||||
The new buffer starts at the beginning of the passed
|
||||
buffer. Ownership of the underlying memory is not
|
||||
transferred.
|
||||
*/
|
||||
inline
|
||||
boost::asio::const_buffer
|
||||
prepare_buffer(std::size_t n,
|
||||
boost::asio::const_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void const*>(buffer),
|
||||
std::min(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
/** Get a trimmed mutable buffer.
|
||||
|
||||
The new buffer starts at the beginning of the passed
|
||||
buffer. Ownership of the underlying memory is not
|
||||
transferred.
|
||||
*/
|
||||
inline
|
||||
boost::asio::mutable_buffer
|
||||
prepare_buffer(std::size_t n,
|
||||
boost::asio::mutable_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void*>(buffer),
|
||||
std::min(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
/** Wrapper to produce a trimmed buffer sequence.
|
||||
|
||||
This wraps a buffer sequence to efficiently present a shorter
|
||||
subset of the original list of buffers starting with the first
|
||||
byte of the original sequence.
|
||||
|
||||
@tparam BufferSequence The buffer sequence to wrap.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
class prepared_buffers
|
||||
{
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
BufferSequence bs_;
|
||||
iter_type back_;
|
||||
iter_type end_;
|
||||
std::size_t size_;
|
||||
|
||||
template<class Deduced>
|
||||
prepared_buffers(Deduced&& other,
|
||||
std::size_t nback, std::size_t nend)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, back_(std::next(bs_.begin(), nback))
|
||||
, end_(std::next(bs_.begin(), nend))
|
||||
, size_(other.size_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/// The type for each element in the list of buffers.
|
||||
using value_type =
|
||||
typename std::iterator_traits<iter_type>::value_type;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_iterator;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
prepared_buffers(prepared_buffers&&);
|
||||
|
||||
/// Copy constructor.
|
||||
prepared_buffers(prepared_buffers const&);
|
||||
|
||||
/// Move assignment.
|
||||
prepared_buffers& operator=(prepared_buffers&&);
|
||||
|
||||
/// Copy assignment.
|
||||
prepared_buffers& operator=(prepared_buffers const&);
|
||||
|
||||
/** Construct a wrapped buffer sequence.
|
||||
|
||||
@param n The maximum number of bytes in the wrapped sequence.
|
||||
If this is larger than the size of buffers, the wrapped
|
||||
sequence will represent the entire input sequence.
|
||||
|
||||
@param buffers The buffer sequence to wrap. A copy of the sequence
|
||||
will be made, but ownership of the underlying memory is not transferred.
|
||||
*/
|
||||
prepared_buffers(std::size_t n, BufferSequence const& buffers);
|
||||
|
||||
/// Get a bidirectional iterator to the first element.
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Get a bidirectional iterator for one past the last element.
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
private:
|
||||
void
|
||||
setup(std::size_t n);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Return a trimmed, wrapped buffer sequence.
|
||||
|
||||
This function returns a new buffer sequence which wraps the provided
|
||||
buffer sequence and efficiently presents a shorter subset of the
|
||||
original list of buffers starting with the first byte of the original
|
||||
sequence.
|
||||
|
||||
@param n The maximum number of bytes in the wrapped sequence. If this
|
||||
is larger than the size of buffers, the wrapped sequence will represent
|
||||
the entire input sequence.
|
||||
|
||||
@param buffers The buffer sequence to wrap. A copy of the sequence
|
||||
will be made, but ownership of the underlying memory is not transferred.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>
|
||||
prepare_buffers(std::size_t n, BufferSequence const& buffers);
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/impl/prepare_buffers.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STATIC_STREAMBUF_HPP
|
||||
#define BEAST_STATIC_STREAMBUF_HPP
|
||||
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A `Streambuf` with a fixed size internal buffer.
|
||||
|
||||
Ownership of the underlying storage belongs to the derived class.
|
||||
|
||||
@note Variables are usually declared using the template class
|
||||
`static_streambuf_n`; however, to reduce the number of instantiations
|
||||
of template functions receiving static stream buffer arguments in a
|
||||
deduced context, the signature of the receiving function should use
|
||||
`static_streambuf`.
|
||||
*/
|
||||
class static_streambuf
|
||||
{
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#else
|
||||
protected:
|
||||
#endif
|
||||
std::uint8_t* in_;
|
||||
std::uint8_t* out_;
|
||||
std::uint8_t* last_;
|
||||
std::uint8_t* end_;
|
||||
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_buffers_type;
|
||||
class mutable_buffers_type;
|
||||
|
||||
static_streambuf(
|
||||
static_streambuf const& other) noexcept = delete;
|
||||
|
||||
static_streambuf& operator=(
|
||||
static_streambuf const&) noexcept = delete;
|
||||
|
||||
#endif
|
||||
|
||||
/// Returns the largest size output sequence possible.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return end_ - in_;
|
||||
}
|
||||
|
||||
/// Get the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return out_ - in_;
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/// Move bytes from the output sequence to the input sequence.
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
out_ += std::min<std::size_t>(n, last_ - out_);
|
||||
}
|
||||
|
||||
/// Get a list of buffers that represents the input sequence.
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(std::size_t n)
|
||||
{
|
||||
in_ += std::min<std::size_t>(n, out_ - in_);
|
||||
}
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#else
|
||||
protected:
|
||||
#endif
|
||||
static_streambuf(std::uint8_t* p, std::size_t n)
|
||||
{
|
||||
reset(p, n);
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::uint8_t* p, std::size_t n)
|
||||
{
|
||||
in_ = p;
|
||||
out_ = p;
|
||||
last_ = p;
|
||||
end_ = p + n;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A `Streambuf` with a fixed size internal buffer.
|
||||
|
||||
@tparam N The number of bytes in the internal buffer.
|
||||
|
||||
@note To reduce the number of template instantiations when passing
|
||||
objects of this type in a deduced context, the signature of the
|
||||
receiving function should use `static_streambuf` instead.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
class static_streambuf_n
|
||||
: private boost::base_from_member<
|
||||
std::array<std::uint8_t, N>>
|
||||
, public static_streambuf
|
||||
{
|
||||
using member_type = boost::base_from_member<
|
||||
std::array<std::uint8_t, N>>;
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
static_streambuf_n(
|
||||
static_streambuf_n const&) = delete;
|
||||
static_streambuf_n& operator=(
|
||||
static_streambuf_n const&) = delete;
|
||||
#if GENERATING_DOCS
|
||||
public:
|
||||
#endif
|
||||
|
||||
/// Construct a static stream buffer.
|
||||
static_streambuf_n()
|
||||
: static_streambuf(
|
||||
member_type::member.data(),
|
||||
member_type::member.size())
|
||||
{
|
||||
}
|
||||
|
||||
/** Reset the stream buffer.
|
||||
|
||||
Postconditions:
|
||||
The input sequence and output sequence are empty,
|
||||
`max_size()` returns `N`.
|
||||
*/
|
||||
void
|
||||
reset()
|
||||
{
|
||||
static_streambuf::reset(
|
||||
member_type::member.data(),
|
||||
member_type::member.size());
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/impl/static_streambuf.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STREAMBUF_HPP
|
||||
#define BEAST_STREAMBUF_HPP
|
||||
|
||||
#include <beast/basic_streambuf.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
using streambuf = basic_streambuf<std::allocator<char>>;
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,250 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STREAMBUF_READSTREAM_HPP
|
||||
#define BEAST_STREAMBUF_READSTREAM_HPP
|
||||
|
||||
#include <beast/async_completion.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A `Stream` with attached `Streambuf` to buffer reads.
|
||||
|
||||
This wraps a `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 `Streambuf` 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:
|
||||
|
||||
* Transparently leave untouched input acquired in calls
|
||||
to `boost::asio::read_until` behind for subsequent callers.
|
||||
|
||||
* "Preload" a stream with handshake input data acquired
|
||||
from other sources.
|
||||
|
||||
Example:
|
||||
@code
|
||||
// Process the next HTTP headers on the stream,
|
||||
// leaving excess bytes behind for the next call.
|
||||
//
|
||||
template<class Streambuf>
|
||||
void process_http_message(
|
||||
streambuf_readstream<Streambuf>& stream)
|
||||
{
|
||||
// Read up to and including the end of the HTTP
|
||||
// headers, leaving the sequence in the stream's
|
||||
// buffer. read_until may read past the end of the
|
||||
// headers; the return value will include only the
|
||||
// part up to the end of the delimiter.
|
||||
//
|
||||
std::size_t bytes_transferred =
|
||||
boost::asio::read_until(
|
||||
stream.next_layer(), stream.buffer(), "\r\n\r\n");
|
||||
|
||||
// Use prepare_buffers() to limit the input
|
||||
// sequence to only the data up to and including
|
||||
// the trailing "\r\n\r\n".
|
||||
//
|
||||
auto header_buffers = prepare_buffers(
|
||||
bytes_transferred, stream.buffer().data());
|
||||
|
||||
...
|
||||
|
||||
// Discard the portion of the input corresponding
|
||||
// to the HTTP headers.
|
||||
//
|
||||
stream.buffer().consume(bytes_transferred);
|
||||
|
||||
// Everything we read from the stream
|
||||
// is part of the content-body.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@tparam Stream The type of stream to wrap.
|
||||
|
||||
@tparam Streambuf The type of stream buffer to use.
|
||||
*/
|
||||
template<class Stream,
|
||||
class Streambuf = streambuf>
|
||||
class streambuf_readstream
|
||||
{
|
||||
using error_code = boost::system::error_code;
|
||||
|
||||
template<class Buffers, class Handler>
|
||||
class read_some_op;
|
||||
|
||||
Streambuf sb_;
|
||||
std::size_t size_ = 0;
|
||||
Stream next_layer_;
|
||||
|
||||
public:
|
||||
/// The type of the internal buffer
|
||||
using streambuf_type = Streambuf;
|
||||
|
||||
/// 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 next_layer_type::lowest_layer_type;
|
||||
|
||||
/// Move constructor.
|
||||
streambuf_readstream(streambuf_readstream&&) = default;
|
||||
|
||||
/** Construct the wrapping stream.
|
||||
|
||||
@param args Parameters forwarded to the `Stream` constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
streambuf_readstream(Args&&... args);
|
||||
|
||||
/// Get a reference to the next layer.
|
||||
next_layer_type&
|
||||
next_layer()
|
||||
{
|
||||
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.
|
||||
*/
|
||||
Streambuf&
|
||||
buffer()
|
||||
{
|
||||
return sb_;
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
Streambuf 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
|
||||
reserve(std::size_t size)
|
||||
{
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written.
|
||||
/// Throws an exception on failure.
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers)
|
||||
{
|
||||
return next_layer_.write_some(buffers);
|
||||
}
|
||||
|
||||
/// Write the given data to the stream. Returns the number of bytes written,
|
||||
/// or 0 if an error occurred.
|
||||
template <class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
return next_layer_.write_some(buffers, ec);
|
||||
}
|
||||
|
||||
/// Start an asynchronous write. The data being written must be valid for the
|
||||
/// lifetime of the asynchronous operation.
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler);
|
||||
|
||||
/// Read some data from the stream. Returns the number of bytes read.
|
||||
/// Throws an exception on failure.
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
read_some(MutableBufferSequence const& buffers);
|
||||
|
||||
/// Read some data from the stream. Returns the number of bytes read
|
||||
/// or 0 if an error occurred.
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
read_some(MutableBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
|
||||
/// Start an asynchronous read. The buffer into which the data will be read
|
||||
/// must be valid for the lifetime of the asynchronous operation.
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_read_some(MutableBufferSequence const& buffers,
|
||||
ReadHandler&& handler);
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/impl/streambuf_readstream.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,348 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_TYPE_CHECK_HPP
|
||||
#define BEAST_TYPE_CHECK_HPP
|
||||
|
||||
#include <beast/detail/is_call_possible.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Types that meet the requirements,
|
||||
// for use with std::declval only.
|
||||
//
|
||||
|
||||
#if GENERATING_DOCS
|
||||
namespace detail {
|
||||
#else
|
||||
namespace concept {
|
||||
#endif
|
||||
|
||||
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>;
|
||||
|
||||
struct StreamHandler
|
||||
{
|
||||
StreamHandler(StreamHandler const&) = default;
|
||||
void
|
||||
operator()(boost::system::error_code ec,
|
||||
std::size_t);
|
||||
};
|
||||
|
||||
using ReadHandler = StreamHandler;
|
||||
using WriteHandler = StreamHandler;
|
||||
|
||||
} // concept
|
||||
|
||||
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html
|
||||
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html
|
||||
//
|
||||
/// Determine if `T` meets the requirements of `BufferSequence`.
|
||||
template<class T, class BufferType>
|
||||
class is_BufferSequence
|
||||
{
|
||||
template<class U, class R = std::is_convertible<
|
||||
typename U::value_type, BufferType> >
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_base_of<
|
||||
#if 0
|
||||
std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename U::const_iterator>::iterator_category>>
|
||||
#else
|
||||
// workaround:
|
||||
// boost::asio::detail::consuming_buffers::const_iterator
|
||||
// is not bidirectional
|
||||
std::forward_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename U::const_iterator>::iterator_category>>
|
||||
#endif
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
template<class U, class R = typename
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().begin()),
|
||||
typename U::const_iterator>::type>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
template<class U, class R = typename std::is_convertible<decltype(
|
||||
std::declval<U>().end()),
|
||||
typename U::const_iterator>::type>
|
||||
static R check4(int);
|
||||
template<class>
|
||||
static std::false_type check4(...);
|
||||
using type4 = decltype(check4<T>(0));
|
||||
|
||||
public:
|
||||
/// `true` if `T` meets the requirements.
|
||||
static bool const value =
|
||||
std::is_copy_constructible<T>::value &&
|
||||
std::is_destructible<T>::value &&
|
||||
type1::value && type2::value &&
|
||||
type3::value && type4::value;
|
||||
};
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
|
||||
/// Determine if `T` meets the requirements of `ConstBufferSequence`.
|
||||
template<class T>
|
||||
using is_ConstBufferSequence =
|
||||
is_BufferSequence<T, boost::asio::const_buffer>;
|
||||
static_assert(is_ConstBufferSequence<concept::ConstBufferSequence>::value, "");
|
||||
static_assert(! is_ConstBufferSequence<int>::value, "");
|
||||
|
||||
/// Determine if `T` meets the requirements of `MutableBufferSequence`.
|
||||
template<class C>
|
||||
using is_MutableBufferSequence =
|
||||
is_BufferSequence<C, boost::asio::mutable_buffer>;
|
||||
static_assert(is_MutableBufferSequence<concept::MutableBufferSequence>::value, "");
|
||||
static_assert(! is_MutableBufferSequence<int>::value, "");
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Determine if `T` has the `get_io_service` member.
|
||||
template<class T>
|
||||
class has_get_io_service
|
||||
{
|
||||
template<class U, class R = typename std::is_same<
|
||||
decltype(std::declval<U>().get_io_service()),
|
||||
boost::asio::io_service&>>
|
||||
static R check(int);
|
||||
template <class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
|
||||
public:
|
||||
/// `true` if `T` meets the requirements.
|
||||
static bool const value = type::value;
|
||||
};
|
||||
static_assert(! has_get_io_service<int>::value, "");
|
||||
|
||||
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html
|
||||
//
|
||||
/// Determine if `T` meets the requirements of `AsyncReadStream`.
|
||||
template<class T>
|
||||
class is_AsyncReadStream
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().async_read_some(
|
||||
std::declval<concept::MutableBufferSequence>(),
|
||||
std::declval<concept::ReadHandler>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
|
||||
public:
|
||||
/// `true` if `T` meets the requirements.
|
||||
static bool const value =
|
||||
has_get_io_service<T>::value && type::value;
|
||||
};
|
||||
static_assert(! is_AsyncReadStream<int>::value, "");
|
||||
|
||||
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html
|
||||
//
|
||||
/// Determine if `T` meets the requirements of `AsyncWriteStream`.
|
||||
template<class T>
|
||||
class is_AsyncWriteStream
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().async_write_some(
|
||||
std::declval<concept::ConstBufferSequence>(),
|
||||
std::declval<concept::WriteHandler>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
|
||||
public:
|
||||
/// `true` if `T` meets the requirements.
|
||||
static bool const value =
|
||||
has_get_io_service<T>::value && type::value;
|
||||
};
|
||||
static_assert(! is_AsyncWriteStream<int>::value, "");
|
||||
|
||||
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html
|
||||
//
|
||||
/// Determine if `T` meets the requirements of `SyncReadStream`.
|
||||
template<class T>
|
||||
class is_SyncReadStream
|
||||
{
|
||||
using error_code =
|
||||
boost::system::error_code;
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().read_some(
|
||||
std::declval<concept::MutableBufferSequence>())),
|
||||
std::size_t>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().read_some(
|
||||
std::declval<concept::MutableBufferSequence>(),
|
||||
std::declval<error_code&>())), std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
public:
|
||||
/// `true` if `T` meets the requirements.
|
||||
static bool const value =
|
||||
type1::value && type2::value;
|
||||
};
|
||||
static_assert(! is_SyncReadStream<int>::value, "");
|
||||
|
||||
// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html
|
||||
//
|
||||
/// Determine if `T` meets the requirements of `SyncWriterStream`.
|
||||
template<class T>
|
||||
class is_SyncWriteStream
|
||||
{
|
||||
using error_code =
|
||||
boost::system::error_code;
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().write_some(
|
||||
std::declval<concept::ConstBufferSequence>())),
|
||||
std::size_t>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().write_some(
|
||||
std::declval<concept::ConstBufferSequence>(),
|
||||
std::declval<error_code&>())), std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
public:
|
||||
/// `true` if `T` meets the requirements.
|
||||
static bool const value =
|
||||
type1::value && type2::value;
|
||||
};
|
||||
static_assert(! is_SyncWriteStream<int>::value, "");
|
||||
|
||||
/// Determine if `T` meets the requirements of `Stream`.
|
||||
template<class T>
|
||||
struct is_Stream
|
||||
{
|
||||
/// `true` if `T` meets the requirements.
|
||||
static bool const value =
|
||||
is_AsyncReadStream<T>::value &&
|
||||
is_AsyncWriteStream<T>::value &&
|
||||
is_SyncReadStream<T>::value &&
|
||||
is_SyncWriteStream<T>::value;
|
||||
};
|
||||
|
||||
/// Determine if `T` meets the requirements of `Streambuf`.
|
||||
template<class T>
|
||||
class is_Streambuf
|
||||
{
|
||||
template<class U, class R = std::integral_constant<
|
||||
bool, is_MutableBufferSequence<decltype(
|
||||
std::declval<U>().prepare(1))>::value>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::integral_constant<
|
||||
bool, is_ConstBufferSequence<decltype(
|
||||
std::declval<U>().data())>::value>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().commit(1), std::true_type{})>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().consume(1), std::true_type{})>
|
||||
static R check4(int);
|
||||
template<class>
|
||||
static std::false_type check4(...);
|
||||
using type4 = decltype(check4<T>(0));
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().size()), std::size_t>>
|
||||
static R check5(int);
|
||||
template<class>
|
||||
static std::false_type check5(...);
|
||||
using type5 = decltype(check5<T>(0));
|
||||
|
||||
public:
|
||||
/// `true` if `T` meets the requirements.
|
||||
static bool const value =
|
||||
type1::value && type2::value &&
|
||||
type3::value && type4::value &&
|
||||
type5::value;
|
||||
};
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
|
||||
/// Determine if `T` meets the requirements of `CompletionHandler`.
|
||||
template<class T, class Signature>
|
||||
using is_Handler = std::integral_constant<bool,
|
||||
std::is_copy_constructible<typename std::decay<T>::type>::value &&
|
||||
detail::is_call_possible<T, Signature>::value>;
|
||||
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_VERSION_HPP
|
||||
#define BEAST_VERSION_HPP
|
||||
|
||||
// follows http://semver.org
|
||||
|
||||
// BEAST_VERSION % 100 is the patch level
|
||||
// BEAST_VERSION / 100 % 1000 is the minor version
|
||||
// BEAST_VERSION / 100000 is the major version
|
||||
//
|
||||
#define BEAST_VERSION 100000
|
||||
|
||||
#define BEAST_VERSION_STRING "1.0.0-b2"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_HPP
|
||||
#define BEAST_WEBSOCKET_HPP
|
||||
|
||||
#include <beast/websocket/error.hpp>
|
||||
#include <beast/websocket/option.hpp>
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/stream.hpp>
|
||||
#include <beast/websocket/static_string.hpp>
|
||||
#include <beast/websocket/teardown.hpp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WSPROTO_DEBUG_H_INCLUDED
|
||||
#define BEAST_WSPROTO_DEBUG_H_INCLUDED
|
||||
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
to_hex(boost::asio::const_buffer b)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
std::stringstream ss;
|
||||
auto p = buffer_cast<std::uint8_t const*>(b);
|
||||
auto n = buffer_size(b);
|
||||
while(n--)
|
||||
{
|
||||
ss <<
|
||||
std::setfill('0') <<
|
||||
std::setw(2) <<
|
||||
std::hex << int(*p++) << " ";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
std::string
|
||||
to_hex(Buffers const& bs)
|
||||
{
|
||||
std::string s;
|
||||
for(auto const& b : bs)
|
||||
s.append(to_hex(boost::asio::const_buffer(b)));
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
std::string
|
||||
buffers_to_string(Buffers const& bs)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
std::string s;
|
||||
s.reserve(buffer_size(bs));
|
||||
for(auto const& b : bs)
|
||||
s.append(buffer_cast<char const*>(b),
|
||||
buffer_size(b));
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
format(std::string s)
|
||||
{
|
||||
auto const w = 84;
|
||||
for(int n = w*(s.size()/w); n>0; n-=w)
|
||||
s.insert(n, 1, '\n');
|
||||
return s;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // wsproto
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
|
||||
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
using request_type = http::request<http::empty_body>;
|
||||
|
||||
using response_type = http::response<http::string_body>;
|
||||
|
||||
struct abstract_decorator
|
||||
{
|
||||
virtual
|
||||
~abstract_decorator() = default;
|
||||
|
||||
virtual
|
||||
void
|
||||
operator()(request_type& req) = 0;
|
||||
|
||||
virtual
|
||||
void
|
||||
operator()(response_type& resp) = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class decorator : public abstract_decorator
|
||||
{
|
||||
T t_;
|
||||
|
||||
public:
|
||||
decorator() = default;
|
||||
|
||||
decorator(T&& t)
|
||||
: t_(std::move(t))
|
||||
{
|
||||
}
|
||||
|
||||
decorator(T const& t)
|
||||
: t_(t)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(request_type& req) override
|
||||
{
|
||||
t_(req);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(response_type& resp) override
|
||||
{
|
||||
t_(resp);
|
||||
}
|
||||
};
|
||||
|
||||
struct default_decorator
|
||||
{
|
||||
static
|
||||
char const*
|
||||
version()
|
||||
{
|
||||
return "Beast.WSProto/1.0";
|
||||
}
|
||||
|
||||
template<class Body, class Headers>
|
||||
void
|
||||
operator()(http::message<true, Body, Headers>& req)
|
||||
{
|
||||
req.headers.replace("User-Agent", version());
|
||||
}
|
||||
|
||||
template<class Body, class Headers>
|
||||
void
|
||||
operator()(http::message<false, Body, Headers>& resp)
|
||||
{
|
||||
resp.headers.replace("Server", version());
|
||||
}
|
||||
};
|
||||
|
||||
using decorator_type =
|
||||
std::unique_ptr<abstract_decorator>;
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_ENDIAN_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_ENDIAN_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
std::uint16_t
|
||||
big_uint16_to_native(void const* buf)
|
||||
{
|
||||
auto const p = reinterpret_cast<
|
||||
std::uint8_t const*>(buf);
|
||||
return (p[0]<<8) + p[1];
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
big_uint64_to_native(void const* buf)
|
||||
{
|
||||
auto const p = reinterpret_cast<
|
||||
std::uint8_t const*>(buf);
|
||||
return
|
||||
(static_cast<std::uint64_t>(p[0])<<56) +
|
||||
(static_cast<std::uint64_t>(p[1])<<48) +
|
||||
(static_cast<std::uint64_t>(p[2])<<40) +
|
||||
(static_cast<std::uint64_t>(p[3])<<32) +
|
||||
(static_cast<std::uint64_t>(p[4])<<24) +
|
||||
(static_cast<std::uint64_t>(p[5])<<16) +
|
||||
(static_cast<std::uint64_t>(p[6])<< 8) +
|
||||
p[7];
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint32_t
|
||||
little_uint32_to_native(void const* buf)
|
||||
{
|
||||
auto const p = reinterpret_cast<
|
||||
std::uint8_t const*>(buf);
|
||||
return
|
||||
p[0] +
|
||||
(static_cast<std::uint64_t>(p[1])<< 8) +
|
||||
(static_cast<std::uint64_t>(p[2])<<16) +
|
||||
(static_cast<std::uint64_t>(p[3])<<24);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_ERROR_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_ERROR_HPP
|
||||
|
||||
#include <beast/websocket/error.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::websocket::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
class error_category : public boost::system::error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "wsproto";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
case error::closed: return "WebSocket connection closed normally";
|
||||
case error::failed: return "WebSocket connection failed due to a protocol violation";
|
||||
case error::handshake_failed: return "WebSocket Upgrade handshake failed";
|
||||
case error::keep_alive: return "WebSocket Upgrade handshake failed but connection is still open";
|
||||
|
||||
case error::response_malformed: return "malformed HTTP response";
|
||||
case error::response_failed: return "upgrade request failed";
|
||||
case error::response_denied: return "upgrade request denied";
|
||||
case error::request_malformed: return "malformed HTTP request";
|
||||
case error::request_invalid: return "upgrade request invalid";
|
||||
case error::request_denied: return "upgrade request denied";
|
||||
default:
|
||||
return "wsproto.error";
|
||||
}
|
||||
}
|
||||
|
||||
boost::system::error_condition
|
||||
default_error_condition(int ev) const noexcept override
|
||||
{
|
||||
return boost::system::error_condition(ev, *this);
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
boost::system::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
|
||||
boost::system::error_category const&
|
||||
get_error_category()
|
||||
{
|
||||
static detail::error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,385 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_FRAME_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_FRAME_HPP
|
||||
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/static_string.hpp>
|
||||
#include <beast/websocket/detail/endian.hpp>
|
||||
#include <beast/websocket/detail/utf8_checker.hpp>
|
||||
#include <beast/consuming_buffers.hpp>
|
||||
#include <beast/static_streambuf.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Contents of a WebSocket frame header
|
||||
struct frame_header
|
||||
{
|
||||
opcode op;
|
||||
bool fin;
|
||||
bool mask;
|
||||
bool rsv1;
|
||||
bool rsv2;
|
||||
bool rsv3;
|
||||
std::uint64_t len;
|
||||
std::uint32_t key;
|
||||
};
|
||||
|
||||
// holds the largest possible frame header
|
||||
using fh_streambuf =
|
||||
static_streambuf_n<14>;
|
||||
|
||||
// holds the largest possible control frame
|
||||
using frame_streambuf =
|
||||
static_streambuf_n< 2 + 8 + 4 + 125 >;
|
||||
|
||||
inline
|
||||
bool constexpr
|
||||
is_reserved(opcode op)
|
||||
{
|
||||
return
|
||||
(op >= opcode::rsv3 && op <= opcode::rsv7) ||
|
||||
(op >= opcode::crsvb && op <= opcode::crsvf);
|
||||
}
|
||||
|
||||
inline
|
||||
bool constexpr
|
||||
is_valid(opcode op)
|
||||
{
|
||||
return op <= opcode::crsvf;
|
||||
}
|
||||
|
||||
inline
|
||||
bool constexpr
|
||||
is_control(opcode op)
|
||||
{
|
||||
return op >= opcode::close;
|
||||
}
|
||||
|
||||
// Returns `true` if a close code is valid
|
||||
inline
|
||||
bool
|
||||
is_valid(close_code code)
|
||||
{
|
||||
auto const v = static_cast<
|
||||
std::uint16_t>(code);
|
||||
switch(v)
|
||||
{
|
||||
case 1000:
|
||||
case 1001:
|
||||
case 1002:
|
||||
case 1003:
|
||||
case 1007:
|
||||
case 1008:
|
||||
case 1009:
|
||||
case 1010:
|
||||
case 1011:
|
||||
case 1012:
|
||||
case 1013:
|
||||
return true;
|
||||
|
||||
// explicitly reserved
|
||||
case 1004:
|
||||
case 1005:
|
||||
case 1006:
|
||||
case 1014:
|
||||
case 1015:
|
||||
return false;
|
||||
}
|
||||
// reserved
|
||||
if(v >= 1016 && v <= 2999)
|
||||
return false;
|
||||
// not used
|
||||
if(v >= 0 && v <= 999)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Write frame header to streambuf
|
||||
//
|
||||
template<class Streambuf>
|
||||
void
|
||||
write(Streambuf& sb, frame_header const& fh)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using namespace boost::endian;
|
||||
std::size_t n;
|
||||
std::uint8_t b[14];
|
||||
b[0] = (fh.fin ? 0x80 : 0x00) | static_cast<std::uint8_t>(fh.op);
|
||||
b[1] = fh.mask ? 0x80 : 0x00;
|
||||
if (fh.len <= 125)
|
||||
{
|
||||
b[1] |= fh.len;
|
||||
n = 2;
|
||||
}
|
||||
else if (fh.len <= 65535)
|
||||
{
|
||||
b[1] |= 126;
|
||||
::new(&b[2]) big_uint16_buf_t{
|
||||
(std::uint16_t)fh.len};
|
||||
n = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
b[1] |= 127;
|
||||
::new(&b[2]) big_uint64_buf_t{fh.len};
|
||||
n = 10;
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
little_uint32_buf_t key(fh.key);
|
||||
std::copy(key.data(),
|
||||
key.data() + 4, &b[n]);
|
||||
n += 4;
|
||||
}
|
||||
sb.commit(buffer_copy(
|
||||
sb.prepare(n), buffer(b)));
|
||||
}
|
||||
|
||||
// Read fixed frame header
|
||||
// Requires at least 2 bytes
|
||||
//
|
||||
template<class Streambuf>
|
||||
std::size_t
|
||||
read_fh1(frame_header& fh, Streambuf& sb,
|
||||
role_type role, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
std::uint8_t b[2];
|
||||
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||
std::size_t need;
|
||||
fh.len = b[1] & 0x7f;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126: need = 2; break;
|
||||
case 127: need = 8; break;
|
||||
default:
|
||||
need = 0;
|
||||
}
|
||||
if((fh.mask = (b[1] & 0x80) != 0))
|
||||
need += 4;
|
||||
fh.op = static_cast<opcode>(b[0] & 0x0f);
|
||||
fh.fin = (b[0] & 0x80) != 0;
|
||||
fh.rsv1 = (b[0] & 0x40) != 0;
|
||||
fh.rsv2 = (b[0] & 0x20) != 0;
|
||||
fh.rsv3 = (b[0] & 0x10) != 0;
|
||||
// invalid length for control message
|
||||
if(is_control(fh.op) && fh.len > 125)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// reserved bits not cleared
|
||||
if(fh.rsv1 || fh.rsv2 || fh.rsv3)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// reserved opcode
|
||||
if(is_reserved(fh.op))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// invalid opcode
|
||||
// (only in locally generated headers)
|
||||
if(! is_valid(fh.op))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// fragmented control message
|
||||
if(is_control(fh.op) && ! fh.fin)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// unmasked frame from client
|
||||
if(role == role_type::server && ! fh.mask)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
// masked frame from server
|
||||
if(role == role_type::client && fh.mask)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return 0;
|
||||
}
|
||||
code = close_code::none;
|
||||
return need;
|
||||
}
|
||||
|
||||
// Decode variable frame header from stream
|
||||
//
|
||||
template<class Streambuf>
|
||||
void
|
||||
read_fh2(frame_header& fh, Streambuf& sb,
|
||||
role_type role, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using namespace boost::endian;
|
||||
switch(fh.len)
|
||||
{
|
||||
case 126:
|
||||
{
|
||||
std::uint8_t b[2];
|
||||
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||
#if 0
|
||||
// Causes strict-aliasing warning in gcc
|
||||
fh.len = reinterpret_cast<
|
||||
big_uint16_buf_t const*>(&b[0])->value();
|
||||
#else
|
||||
fh.len = big_uint16_to_native(&b[0]);
|
||||
#endif
|
||||
// length not canonical
|
||||
if(fh.len < 126)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 127:
|
||||
{
|
||||
std::uint8_t b[8];
|
||||
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||
#if 0
|
||||
// Causes strict-aliasing warning in gcc
|
||||
fh.len = reinterpret_cast<
|
||||
big_uint64_buf_t const*>(&b[0])->value();
|
||||
#else
|
||||
fh.len = big_uint64_to_native(&b[0]);
|
||||
#endif
|
||||
// length not canonical
|
||||
if(fh.len < 65536)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(fh.mask)
|
||||
{
|
||||
std::uint8_t b[4];
|
||||
assert(buffer_size(sb.data()) >= sizeof(b));
|
||||
sb.consume(buffer_copy(buffer(b), sb.data()));
|
||||
#if 0
|
||||
// Causes strict-aliasing warning in gcc
|
||||
fh.key = reinterpret_cast<
|
||||
little_uint32_buf_t const*>(&b[0])->value();
|
||||
#else
|
||||
fh.key = little_uint32_to_native(&b[0]);
|
||||
#endif
|
||||
}
|
||||
code = close_code::none;
|
||||
}
|
||||
|
||||
// Read data from buffers
|
||||
// This is for ping and pong payloads
|
||||
//
|
||||
template<class Buffers>
|
||||
void
|
||||
read(ping_payload_type& data,
|
||||
Buffers const& bs, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::mutable_buffers_1;
|
||||
assert(buffer_size(bs) <= data.max_size());
|
||||
data.resize(buffer_size(bs));
|
||||
buffer_copy(mutable_buffers_1{
|
||||
data.data(), data.size()}, bs);
|
||||
}
|
||||
|
||||
// Read close_reason, return true on success
|
||||
// This is for the close payload
|
||||
//
|
||||
template<class Buffers>
|
||||
void
|
||||
read(close_reason& cr,
|
||||
Buffers const& bs, close_code& code)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using namespace boost::endian;
|
||||
auto n = buffer_size(bs);
|
||||
assert(n <= 125);
|
||||
if(n == 0)
|
||||
{
|
||||
cr = close_reason{};
|
||||
code = close_code::none;
|
||||
return;
|
||||
}
|
||||
if(n == 1)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
consuming_buffers<Buffers> cb(bs);
|
||||
{
|
||||
std::uint8_t b[2];
|
||||
buffer_copy(buffer(b), cb);
|
||||
#if 0
|
||||
// Causes strict-aliasing warning in gcc
|
||||
cr.code = static_cast<close_code>(
|
||||
reinterpret_cast<
|
||||
big_uint16_buf_t const*>(&b[0])->value());
|
||||
#else
|
||||
cr.code = static_cast<close_code>(
|
||||
big_uint16_to_native(&b[0]));
|
||||
#endif
|
||||
cb.consume(2);
|
||||
n -= 2;
|
||||
if(! is_valid(cr.code))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(n > 0)
|
||||
{
|
||||
cr.reason.resize(n);
|
||||
buffer_copy(buffer(&cr.reason[0], n), cb);
|
||||
if(! detail::check_utf8(
|
||||
cr.reason.data(), cr.reason.size()))
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cr.reason = "";
|
||||
}
|
||||
code = close_code::none;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_HYBI13_HPP
|
||||
|
||||
#include <beast/detail/base64.hpp>
|
||||
#include <beast/detail/sha1.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class Gen>
|
||||
std::string
|
||||
make_sec_ws_key(Gen& g)
|
||||
{
|
||||
union U
|
||||
{
|
||||
std::array<std::uint32_t, 4> a4;
|
||||
std::array<std::uint8_t, 16> a16;
|
||||
};
|
||||
U u;
|
||||
for(int i = 0; i < 4; ++i)
|
||||
u.a4[i] = g();
|
||||
return beast::detail::base64_encode(u.a16.data(), u.a16.size());
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
make_sec_ws_accept(boost::string_ref const& key)
|
||||
{
|
||||
std::string s(key.data(), key.size());
|
||||
s += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
beast::detail::sha1_context ctx;
|
||||
beast::detail::init(ctx);
|
||||
beast::detail::update(ctx, s.data(), s.size());
|
||||
std::array<std::uint8_t,
|
||||
beast::detail::sha1_context::digest_size> digest;
|
||||
beast::detail::finish(ctx, digest.data());
|
||||
return beast::detail::base64_encode(
|
||||
digest.data(), digest.size());
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_INVOKABLE_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_INVOKABLE_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// "Parks" a composed operation, to invoke later
|
||||
//
|
||||
class invokable
|
||||
{
|
||||
struct base
|
||||
{
|
||||
base() = default;
|
||||
base(base &&) = default;
|
||||
virtual ~base() = default;
|
||||
virtual void move(void* p) = 0;
|
||||
virtual void operator()() = 0;
|
||||
};
|
||||
|
||||
template<class F>
|
||||
struct holder : base
|
||||
{
|
||||
F f;
|
||||
|
||||
holder(holder&&) = default;
|
||||
|
||||
template<class U>
|
||||
explicit
|
||||
holder(U&& u)
|
||||
: f(std::forward<U>(u))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
move(void* p) override
|
||||
{
|
||||
::new(p) holder(std::move(*this));
|
||||
}
|
||||
|
||||
void
|
||||
operator()() override
|
||||
{
|
||||
F f_(std::move(f));
|
||||
this->~holder();
|
||||
// invocation of f_() can
|
||||
// assign a new invokable.
|
||||
f_();
|
||||
}
|
||||
};
|
||||
|
||||
struct exemplar
|
||||
{
|
||||
std::shared_ptr<int> _;
|
||||
void operator()(){}
|
||||
};
|
||||
|
||||
using buf_type = char[sizeof(holder<exemplar>)];
|
||||
|
||||
base* base_ = nullptr;
|
||||
alignas(holder<exemplar>) buf_type buf_;
|
||||
|
||||
public:
|
||||
#ifndef NDEBUG
|
||||
~invokable()
|
||||
{
|
||||
// Engaged invokables must be invoked before
|
||||
// destruction otherwise the io_service
|
||||
// invariants are broken w.r.t completions.
|
||||
assert(! base_);
|
||||
}
|
||||
#endif
|
||||
|
||||
invokable() = default;
|
||||
invokable(invokable const&) = delete;
|
||||
invokable& operator=(invokable const&) = delete;
|
||||
|
||||
invokable(invokable&& other)
|
||||
{
|
||||
if(other.base_)
|
||||
{
|
||||
base_ = reinterpret_cast<base*>(&buf_[0]);
|
||||
other.base_->move(buf_);
|
||||
other.base_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
invokable&
|
||||
operator=(invokable&& other)
|
||||
{
|
||||
// Engaged invokables must be invoked before
|
||||
// assignment otherwise the io_service
|
||||
// invariants are broken w.r.t completions.
|
||||
assert(! base_);
|
||||
|
||||
if(other.base_)
|
||||
{
|
||||
base_ = reinterpret_cast<base*>(&buf_[0]);
|
||||
other.base_->move(buf_);
|
||||
other.base_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class F>
|
||||
void
|
||||
emplace(F&& f);
|
||||
|
||||
void
|
||||
maybe_invoke()
|
||||
{
|
||||
if(base_)
|
||||
{
|
||||
auto const basep = base_;
|
||||
base_ = nullptr;
|
||||
(*basep)();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class F>
|
||||
void
|
||||
invokable::emplace(F&& f)
|
||||
{
|
||||
static_assert(sizeof(buf_type) >= sizeof(holder<F>),
|
||||
"buffer too small");
|
||||
assert(! base_);
|
||||
::new(buf_) holder<F>(std::forward<F>(f));
|
||||
base_ = reinterpret_cast<base*>(&buf_[0]);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,389 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_WEBSOCKETDETAIL_MASKGEN_HPP
|
||||
#define BEAST_WEBSOCKETDETAIL_MASKGEN_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Pseudo-random source of mask keys
|
||||
//
|
||||
template<class = void>
|
||||
class maskgen_t
|
||||
{
|
||||
std::mt19937 g_;
|
||||
|
||||
public:
|
||||
using result_type = typename std::mt19937::result_type;
|
||||
|
||||
maskgen_t(maskgen_t const&) = delete;
|
||||
maskgen_t& operator=(maskgen_t const&) = delete;
|
||||
|
||||
maskgen_t();
|
||||
|
||||
result_type
|
||||
operator()() noexcept;
|
||||
|
||||
void
|
||||
rekey();
|
||||
};
|
||||
|
||||
template<class _>
|
||||
maskgen_t<_>::maskgen_t()
|
||||
{
|
||||
rekey();
|
||||
}
|
||||
|
||||
template<class _>
|
||||
auto
|
||||
maskgen_t<_>::operator()() noexcept ->
|
||||
result_type
|
||||
{
|
||||
for(;;)
|
||||
if(auto key = g_())
|
||||
return key;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
void
|
||||
maskgen_t<_>::rekey()
|
||||
{
|
||||
std::random_device rng;
|
||||
std::array<std::uint32_t, 32> e;
|
||||
for(auto& i : e)
|
||||
i = rng();
|
||||
std::seed_seq ss(e.begin(), e.end());
|
||||
g_.seed(ss);
|
||||
}
|
||||
|
||||
using maskgen = maskgen_t<>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//using prepared_key_type = std::size_t;
|
||||
using prepared_key_type = std::uint32_t;
|
||||
//using prepared_key_type = std::uint64_t;
|
||||
|
||||
inline
|
||||
void
|
||||
prepare_key(std::uint32_t& prepared, std::uint32_t key)
|
||||
{
|
||||
prepared = key;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
prepare_key(std::uint64_t& prepared, std::uint32_t key)
|
||||
{
|
||||
prepared =
|
||||
(static_cast<std::uint64_t>(key) << 32) | key;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline
|
||||
typename std::enable_if<std::is_integral<T>::value, T>::type
|
||||
rol(T t, unsigned n = 1)
|
||||
{
|
||||
auto constexpr bits =
|
||||
static_cast<unsigned>(
|
||||
sizeof(T) * CHAR_BIT);
|
||||
n &= bits-1;
|
||||
return static_cast<T>((t << n) | (
|
||||
static_cast<typename std::make_unsigned<T>::type>(t) >> (bits - n)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
typename std::enable_if<std::is_integral<T>::value, T>::type
|
||||
ror(T t, unsigned n = 1)
|
||||
{
|
||||
auto constexpr bits =
|
||||
static_cast<unsigned>(
|
||||
sizeof(T) * CHAR_BIT);
|
||||
n &= bits-1;
|
||||
return static_cast<T>((t << (bits - n)) | (
|
||||
static_cast<typename std::make_unsigned<T>::type>(t) >> n));
|
||||
}
|
||||
|
||||
// 32-bit Uuoptimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_safe(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*p ^= key ; ++p;
|
||||
*p ^= (key >> 8); ++p;
|
||||
*p ^= (key >>16); ++p;
|
||||
*p ^= (key >>24); ++p;
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 64-bit unoptimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_safe(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*p ^= key ; ++p;
|
||||
*p ^= (key >> 8); ++p;
|
||||
*p ^= (key >>16); ++p;
|
||||
*p ^= (key >>24); ++p;
|
||||
*p ^= (key >>32); ++p;
|
||||
*p ^= (key >>40); ++p;
|
||||
*p ^= (key >>48); ++p;
|
||||
*p ^= (key >>56); ++p;
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 7: p[6] ^= (key >>16);
|
||||
case 6: p[5] ^= (key >> 8);
|
||||
case 5: p[4] ^= key;
|
||||
case 4: p[3] ^= (key >>24);
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 32-bit optimized
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_32(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
auto m = reinterpret_cast<
|
||||
uintptr_t>(p) % sizeof(key);
|
||||
switch(m)
|
||||
{
|
||||
case 1: *p ^= key ; ++p; --n;
|
||||
case 2: *p ^= (key >> 8); ++p; --n;
|
||||
case 3: *p ^= (key >>16); ++p; --n;
|
||||
key = ror(key, m * 8);
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*reinterpret_cast<
|
||||
std::uint32_t*>(p) ^= key;
|
||||
p += sizeof(key);
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 64-bit optimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_64(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
auto m = reinterpret_cast<
|
||||
uintptr_t>(p) % sizeof(key);
|
||||
switch(m)
|
||||
{
|
||||
case 1: *p ^= key ; ++p; --n;
|
||||
case 2: *p ^= (key >> 8); ++p; --n;
|
||||
case 3: *p ^= (key >>16); ++p; --n;
|
||||
case 4: *p ^= (key >>24); ++p; --n;
|
||||
case 5: *p ^= (key >>32); ++p; --n;
|
||||
case 6: *p ^= (key >>40); ++p; --n;
|
||||
case 7: *p ^= (key >>48); ++p; --n;
|
||||
key = ror(key, m * 8);
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*reinterpret_cast<
|
||||
std::uint64_t*>(p) ^= key;
|
||||
p += sizeof(key);
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 32-bit x86 optimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_x86(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*reinterpret_cast<
|
||||
std::uint32_t*>(p) ^= key;
|
||||
p += sizeof(key);
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 64-bit amd64 optimized
|
||||
//
|
||||
template<class = void>
|
||||
void
|
||||
mask_inplace_amd(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto n = buffer_size(b);
|
||||
auto p = buffer_cast<std::uint8_t*>(b);
|
||||
for(auto i = n / sizeof(key); i; --i)
|
||||
{
|
||||
*reinterpret_cast<
|
||||
std::uint64_t*>(p) ^= key;
|
||||
p += sizeof(key);
|
||||
}
|
||||
n %= sizeof(key);
|
||||
switch(n)
|
||||
{
|
||||
case 7: p[6] ^= (key >>16);
|
||||
case 6: p[5] ^= (key >> 8);
|
||||
case 5: p[4] ^= key;
|
||||
case 4: p[3] ^= (key >>24);
|
||||
case 3: p[2] ^= (key >>16);
|
||||
case 2: p[1] ^= (key >> 8);
|
||||
case 1: p[0] ^= key;
|
||||
key = ror(key, n*8);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
mask_inplace(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint32_t& key)
|
||||
{
|
||||
mask_inplace_safe(b, key);
|
||||
//mask_inplace_32(b, key);
|
||||
//mask_inplace_x86(b, key);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
mask_inplace(
|
||||
boost::asio::mutable_buffer const& b,
|
||||
std::uint64_t& key)
|
||||
{
|
||||
mask_inplace_safe(b, key);
|
||||
//mask_inplace_64(b, key);
|
||||
//mask_inplace_amd(b, key);
|
||||
}
|
||||
|
||||
// Apply mask in place
|
||||
//
|
||||
template<class MutableBuffers, class KeyType>
|
||||
void
|
||||
mask_inplace(
|
||||
MutableBuffers const& bs, KeyType& key)
|
||||
{
|
||||
for(auto const& b : bs)
|
||||
mask_inplace(b, key);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_STREAM_BASE_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_STREAM_BASE_HPP
|
||||
|
||||
#include <beast/websocket/error.hpp>
|
||||
#include <beast/websocket/rfc6455.hpp>
|
||||
#include <beast/websocket/detail/decorator.hpp>
|
||||
#include <beast/websocket/detail/frame.hpp>
|
||||
#include <beast/websocket/detail/invokable.hpp>
|
||||
#include <beast/websocket/detail/mask.hpp>
|
||||
#include <beast/websocket/detail/utf8_checker.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
template<class String>
|
||||
inline
|
||||
void
|
||||
maybe_throw(error_code const& ec, String const&)
|
||||
{
|
||||
if(ec)
|
||||
throw boost::system::system_error{ec};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct stream_base
|
||||
{
|
||||
protected:
|
||||
struct op {};
|
||||
|
||||
detail::maskgen maskgen_; // source of mask keys
|
||||
decorator_type d_; // adorns http messages
|
||||
bool keep_alive_ = false; // close on failed upgrade
|
||||
role_type role_; // server or client
|
||||
bool error_ = false; // non-zero ec was delivered
|
||||
|
||||
std::size_t rd_msg_max_ =
|
||||
16 * 1024 * 1024; // max message size
|
||||
detail::frame_header rd_fh_; // current frame header
|
||||
detail::prepared_key_type rd_key_; // prepared masking key
|
||||
detail::utf8_checker rd_utf8_check_;// for current text msg
|
||||
std::uint64_t rd_size_; // size of the current message so far
|
||||
std::uint64_t rd_need_ = 0; // bytes left in msg frame payload
|
||||
opcode rd_opcode_; // opcode of current msg
|
||||
bool rd_cont_ = false; // expecting a continuation frame
|
||||
bool rd_close_ = false; // got close frame
|
||||
op* rd_block_ = nullptr; // op currently reading
|
||||
|
||||
std::size_t
|
||||
wr_frag_size_ = 16 * 1024; // size of auto-fragments
|
||||
std::size_t wr_buf_size_ = 4096; // write buffer size
|
||||
opcode wr_opcode_ = opcode::text; // outgoing message type
|
||||
bool wr_close_ = false; // sent close frame
|
||||
bool wr_cont_ = false; // next write is continuation frame
|
||||
op* wr_block_ = nullptr; // op currenly writing
|
||||
|
||||
invokable rd_op_; // invoked after write completes
|
||||
invokable wr_op_; // invoked after read completes
|
||||
close_reason cr_; // set from received close frame
|
||||
|
||||
stream_base()
|
||||
: d_(new decorator<default_decorator>{})
|
||||
{
|
||||
}
|
||||
|
||||
stream_base(stream_base&&) = default;
|
||||
stream_base(stream_base const&) = delete;
|
||||
stream_base& operator=(stream_base&&) = default;
|
||||
stream_base& operator=(stream_base const&) = delete;
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
prepare_fh(close_code& code);
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_close(Streambuf& sb,
|
||||
close_reason const& rc);
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
write_ping(Streambuf& sb, opcode op,
|
||||
ping_payload_type const& data);
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_UTF8_CHECKER_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <string> // DEPRECATED
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Code adapted from
|
||||
// http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
/*
|
||||
Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject
|
||||
to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
*/
|
||||
template<class = void>
|
||||
class utf8_checker_t
|
||||
{
|
||||
// Table for the UTF8 decode state machine
|
||||
using lut_type = std::uint8_t[400];
|
||||
static
|
||||
lut_type const&
|
||||
lut()
|
||||
{
|
||||
// 400 elements
|
||||
static std::uint8_t 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, // 00..1f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
|
||||
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
|
||||
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
|
||||
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
|
||||
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
|
||||
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
|
||||
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // s7..s8
|
||||
};
|
||||
return tab;
|
||||
}
|
||||
|
||||
std::uint32_t state_ = 0;
|
||||
std::uint32_t codepoint_ = 0;
|
||||
|
||||
public:
|
||||
utf8_checker_t() = default;
|
||||
utf8_checker_t(utf8_checker_t&&) = default;
|
||||
utf8_checker_t(utf8_checker_t const&) = default;
|
||||
utf8_checker_t& operator=(utf8_checker_t&&) = default;
|
||||
utf8_checker_t& operator=(utf8_checker_t const&) = default;
|
||||
|
||||
void
|
||||
reset();
|
||||
|
||||
// Returns `true` on success
|
||||
bool
|
||||
write(void const* buffer, std::size_t size);
|
||||
|
||||
// Returns `true` on success
|
||||
template<class BufferSequence>
|
||||
bool
|
||||
write(BufferSequence const& bs);
|
||||
|
||||
// Returns `true` on success
|
||||
bool
|
||||
finish();
|
||||
};
|
||||
|
||||
template<class _>
|
||||
void
|
||||
utf8_checker_t<_>::reset()
|
||||
{
|
||||
state_ = 0;
|
||||
codepoint_ = 0;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
bool
|
||||
utf8_checker_t<_>::write(void const* buffer, std::size_t size)
|
||||
{
|
||||
auto p = static_cast<std::uint8_t const*>(buffer);
|
||||
auto plut = &lut()[0];
|
||||
while(size)
|
||||
{
|
||||
auto const byte = *p;
|
||||
auto const type = plut[byte];
|
||||
if(state_)
|
||||
codepoint_ = (byte & 0x3fu) | (codepoint_ << 6);
|
||||
else
|
||||
codepoint_ = (0xff >> type) & byte;
|
||||
state_ = plut[256 + state_ * 16 + type];
|
||||
if(state_ == 1)
|
||||
{
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
++p;
|
||||
--size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
template<class BufferSequence>
|
||||
bool
|
||||
utf8_checker_t<_>::write(BufferSequence const& bs)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for (auto const& b : bs)
|
||||
if(! write(buffer_cast<void const*>(b),
|
||||
buffer_size(b)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class _>
|
||||
bool
|
||||
utf8_checker_t<_>::finish()
|
||||
{
|
||||
auto const success = state_ == 0;
|
||||
reset();
|
||||
return success;
|
||||
}
|
||||
|
||||
using utf8_checker = utf8_checker_t<>;
|
||||
|
||||
template<class = void>
|
||||
bool
|
||||
check_utf8(char const* p, std::size_t n)
|
||||
{
|
||||
utf8_checker c;
|
||||
if(! c.write(p, n))
|
||||
return false;
|
||||
return c.finish();
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_ERROR_HPP
|
||||
#define BEAST_WEBSOCKET_ERROR_HPP
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/// The type of error used by functions and completion handlers.
|
||||
using error_code = boost::system::error_code;
|
||||
|
||||
/// Error values
|
||||
enum class error
|
||||
{
|
||||
/// Both sides performed a WebSocket close
|
||||
closed = 1,
|
||||
|
||||
/// WebSocket connection failed, protocol violation
|
||||
failed,
|
||||
|
||||
/// Upgrade request failed, connection is closed
|
||||
handshake_failed,
|
||||
|
||||
/// Upgrade request failed, but connection is still open
|
||||
keep_alive,
|
||||
|
||||
/// HTTP response is malformed
|
||||
response_malformed,
|
||||
|
||||
/// HTTP response failed the upgrade
|
||||
response_failed,
|
||||
|
||||
/// Upgrade request denied for invalid fields.
|
||||
response_denied,
|
||||
|
||||
/// Upgrade request is malformed
|
||||
request_malformed,
|
||||
|
||||
/// Upgrade request fields incorrect
|
||||
request_invalid,
|
||||
|
||||
/// Upgrade request denied
|
||||
request_denied
|
||||
};
|
||||
|
||||
error_code
|
||||
make_error_code(error e);
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#include <beast/websocket/impl/error.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP
|
||||
|
||||
#include <beast/websocket/impl/response_op.ipp>
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/prepare_buffers.hpp>
|
||||
#include <beast/http/parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// read and respond to an upgrade request
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::accept_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
http::request<http::empty_body> req;
|
||||
Handler h;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler, class Buffers>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||
Buffers const& buffers)
|
||||
: ws(ws_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
ws.stream_.buffer().commit(buffer_copy(
|
||||
ws.stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
accept_op(accept_op&&) = default;
|
||||
accept_op(accept_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
accept_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, accept_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, accept_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(accept_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, accept_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::accept_op<Handler>::
|
||||
operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
// read message
|
||||
d.state = 1;
|
||||
http::async_read(d.ws.next_layer_,
|
||||
d.ws.stream_.buffer(), d.req,
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got message
|
||||
case 1:
|
||||
// respond to request
|
||||
response_op<Handler>{
|
||||
std::move(d.h), d.ws, d.req, true};
|
||||
return;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP
|
||||
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/static_streambuf.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// send the close message and wait for the response
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::close_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
using fb_type =
|
||||
detail::frame_streambuf;
|
||||
using fmb_type =
|
||||
typename fb_type::mutable_buffers_type;
|
||||
|
||||
struct data : op
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
close_reason cr;
|
||||
Handler h;
|
||||
fb_type fb;
|
||||
fmb_type fmb;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||
close_reason const& cr_)
|
||||
: ws(ws_)
|
||||
, cr(cr_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
ws.template write_close<
|
||||
static_streambuf>(fb, cr);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
close_op(close_op&&) = default;
|
||||
close_op(close_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
close_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = false;
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, close_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, close_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(close_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, close_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::close_op<Handler>::operator()(
|
||||
error_code ec, std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 1;
|
||||
d.ws.rd_op_.template emplace<
|
||||
close_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted, 0));
|
||||
return;
|
||||
}
|
||||
d.state = 2;
|
||||
break;
|
||||
|
||||
// resume
|
||||
case 1:
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
}
|
||||
d.state = 2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// send close
|
||||
d.state = 99;
|
||||
assert(! d.ws.wr_close_);
|
||||
d.ws.wr_close_ = true;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(ec)
|
||||
d.ws.error_ = true;
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
d.h(ec);
|
||||
d.ws.rd_op_.maybe_invoke();
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_ERROR_IPP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_ERROR_IPP_HPP
|
||||
|
||||
#include <beast/websocket/detail/error.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_error_code(error e)
|
||||
{
|
||||
return error_code(
|
||||
static_cast<int>(e), detail::get_error_category());
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP
|
||||
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// send the upgrade request and process the response
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::handshake_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
Handler h;
|
||||
std::string key;
|
||||
http::request<http::empty_body> req;
|
||||
http::response<http::string_body> resp;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||
boost::string_ref const& host,
|
||||
boost::string_ref const& resource)
|
||||
: ws(ws_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, req(ws.build_request(host, resource, key))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
handshake_op(handshake_op&&) = default;
|
||||
handshake_op(handshake_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
handshake_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0);
|
||||
}
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, handshake_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, handshake_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(handshake_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, handshake_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::handshake_op<
|
||||
Handler>::operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// send http upgrade
|
||||
d.state = 1;
|
||||
// VFALCO Do we need the ability to move
|
||||
// a message on the async_write?
|
||||
http::async_write(d.ws.stream_,
|
||||
d.req, std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
// sent upgrade
|
||||
case 1:
|
||||
// read http response
|
||||
d.state = 2;
|
||||
http::async_read(d.ws.next_layer_,
|
||||
d.ws.stream_.buffer(), d.resp,
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got response
|
||||
case 2:
|
||||
{
|
||||
d.ws.do_response(d.resp, d.key, ec);
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,507 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP
|
||||
|
||||
#include <beast/websocket/teardown.hpp>
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/prepare_buffers.hpp>
|
||||
#include <beast/static_streambuf.hpp>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// Reads a single message frame,
|
||||
// processes any received control frames.
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Streambuf, class Handler>
|
||||
class stream<NextLayer>::read_frame_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
using fb_type =
|
||||
detail::frame_streambuf;
|
||||
|
||||
using fmb_type =
|
||||
typename fb_type::mutable_buffers_type;
|
||||
|
||||
using smb_type =
|
||||
typename Streambuf::mutable_buffers_type;
|
||||
|
||||
struct data : op
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
frame_info& fi;
|
||||
Streambuf& sb;
|
||||
smb_type smb;
|
||||
Handler h;
|
||||
fb_type fb;
|
||||
fmb_type fmb;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||
frame_info& fi_, Streambuf& sb_)
|
||||
: ws(ws_)
|
||||
, fi(fi_)
|
||||
, sb(sb_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
read_frame_op(read_frame_op&&) = default;
|
||||
read_frame_op(read_frame_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_frame_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = false;
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec)
|
||||
{
|
||||
(*this)(ec, 0);
|
||||
}
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_frame_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_frame_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_frame_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_frame_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::read_frame_op<Buffers, Handler>::
|
||||
operator()(error_code ec,std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
close_code code;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted, 0));
|
||||
return;
|
||||
}
|
||||
if(d.ws.rd_need_ > 0)
|
||||
{
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
d.state = 2;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// read payload
|
||||
d.state = 3;
|
||||
d.smb = d.sb.prepare(
|
||||
detail::clamp(d.ws.rd_need_));
|
||||
d.ws.stream_.async_read_some(
|
||||
d.smb, std::move(*this));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
// read fixed header
|
||||
d.state = 5;
|
||||
boost::asio::async_read(d.ws.stream_,
|
||||
d.fb.prepare(2), std::move(*this));
|
||||
return;
|
||||
|
||||
// got payload
|
||||
case 3:
|
||||
{
|
||||
d.ws.rd_need_ -= bytes_transferred;
|
||||
auto const pb = prepare_buffers(
|
||||
bytes_transferred, d.smb);
|
||||
if(d.ws.rd_fh_.mask)
|
||||
detail::mask_inplace(pb, d.ws.rd_key_);
|
||||
if(d.ws.rd_opcode_ == opcode::text)
|
||||
{
|
||||
if(! d.ws.rd_utf8_check_.write(pb) ||
|
||||
(d.ws.rd_need_ == 0 && d.ws.rd_fh_.fin &&
|
||||
! d.ws.rd_utf8_check_.finish()))
|
||||
{
|
||||
// invalid utf8
|
||||
d.state = 16;
|
||||
code = close_code::bad_payload;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.sb.commit(bytes_transferred);
|
||||
d.state = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
// call handler
|
||||
case 4:
|
||||
d.state = 99;
|
||||
d.fi.op = d.ws.rd_opcode_;
|
||||
d.fi.fin = d.ws.rd_fh_.fin &&
|
||||
d.ws.rd_need_ == 0;
|
||||
break;
|
||||
|
||||
// got fixed header
|
||||
case 5:
|
||||
{
|
||||
d.fb.commit(bytes_transferred);
|
||||
code = close_code::none;
|
||||
auto const n = detail::read_fh1(
|
||||
d.ws.rd_fh_, d.fb, d.ws.role_, code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// protocol error
|
||||
d.state = 16;
|
||||
break;
|
||||
}
|
||||
d.state = 6;
|
||||
if (n == 0)
|
||||
{
|
||||
bytes_transferred = 0;
|
||||
break;
|
||||
}
|
||||
// read variable header
|
||||
boost::asio::async_read(d.ws.stream_,
|
||||
d.fb.prepare(n), std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
// got variable header
|
||||
case 6:
|
||||
d.fb.commit(bytes_transferred);
|
||||
code = close_code::none;
|
||||
detail::read_fh2(d.ws.rd_fh_,
|
||||
d.fb, d.ws.role_, code);
|
||||
if(code == close_code::none)
|
||||
d.ws.prepare_fh(code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// protocol error
|
||||
d.state = 16;
|
||||
break;
|
||||
}
|
||||
if(detail::is_control(d.ws.rd_fh_.op))
|
||||
{
|
||||
if(d.ws.rd_fh_.len > 0)
|
||||
{
|
||||
// read control payload
|
||||
d.state = 7;
|
||||
d.fmb = d.fb.prepare(static_cast<
|
||||
std::size_t>(d.ws.rd_fh_.len));
|
||||
boost::asio::async_read(d.ws.stream_,
|
||||
d.fmb, std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = 8;
|
||||
break;
|
||||
}
|
||||
if(d.ws.rd_need_ > 0)
|
||||
{
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
if(! d.ws.rd_fh_.fin)
|
||||
{
|
||||
d.state = 2;
|
||||
break;
|
||||
}
|
||||
// empty frame with fin
|
||||
d.state = 4;
|
||||
break;
|
||||
|
||||
// got control payload
|
||||
case 7:
|
||||
if(d.ws.rd_fh_.mask)
|
||||
detail::mask_inplace(
|
||||
d.fmb, d.ws.rd_key_);
|
||||
d.fb.commit(bytes_transferred);
|
||||
d.state = 8;
|
||||
break;
|
||||
|
||||
// do control
|
||||
case 8:
|
||||
if(d.ws.rd_fh_.op == opcode::ping)
|
||||
{
|
||||
code = close_code::none;
|
||||
ping_payload_type data;
|
||||
detail::read(data, d.fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// protocol error
|
||||
d.state = 16;
|
||||
break;
|
||||
}
|
||||
d.fb.reset();
|
||||
if(d.ws.wr_close_)
|
||||
{
|
||||
d.state = 2;
|
||||
break;
|
||||
}
|
||||
d.ws.template write_ping<static_streambuf>(
|
||||
d.fb, opcode::pong, data);
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
assert(d.ws.wr_block_ != &d);
|
||||
// suspend
|
||||
d.state = 13;
|
||||
d.ws.rd_op_.template emplace<
|
||||
read_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = 14;
|
||||
break;
|
||||
}
|
||||
else if(d.ws.rd_fh_.op == opcode::pong)
|
||||
{
|
||||
code = close_code::none;
|
||||
ping_payload_type data;
|
||||
detail::read(data, d.fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// protocol error
|
||||
d.state = 16;
|
||||
break;
|
||||
}
|
||||
d.fb.reset();
|
||||
// VFALCO TODO maybe_invoke an async pong handler
|
||||
// For now just ignore the pong.
|
||||
d.state = 2;
|
||||
break;
|
||||
}
|
||||
assert(d.ws.rd_fh_.op == opcode::close);
|
||||
{
|
||||
detail::read(d.ws.cr_, d.fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
{
|
||||
d.state = 16;
|
||||
break;
|
||||
}
|
||||
if(! d.ws.wr_close_)
|
||||
{
|
||||
auto cr = d.ws.cr_;
|
||||
if(cr.code == close_code::none)
|
||||
cr.code = close_code::normal;
|
||||
cr.reason = "";
|
||||
d.fb.reset();
|
||||
d.ws.template write_close<
|
||||
static_streambuf>(d.fb, cr);
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 9;
|
||||
d.ws.rd_op_.template emplace<
|
||||
read_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = 10;
|
||||
break;
|
||||
}
|
||||
// call handler;
|
||||
d.state = 99;
|
||||
ec = error::closed;
|
||||
break;
|
||||
}
|
||||
|
||||
// resume
|
||||
case 9:
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
}
|
||||
if(d.ws.wr_close_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = error::closed;
|
||||
break;
|
||||
}
|
||||
d.state = 10;
|
||||
break;
|
||||
|
||||
// send close
|
||||
case 10:
|
||||
d.state = 11;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;;
|
||||
|
||||
// teardown
|
||||
case 11:
|
||||
d.state = 12;
|
||||
wsproto_helpers::call_async_teardown(
|
||||
d.ws.next_layer_, std::move(*this));
|
||||
return;
|
||||
|
||||
case 12:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = error::closed;
|
||||
break;
|
||||
|
||||
// resume
|
||||
case 13:
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
}
|
||||
if(d.ws.wr_close_)
|
||||
{
|
||||
d.fb.reset();
|
||||
d.state = 2;
|
||||
break;
|
||||
}
|
||||
d.state = 14;
|
||||
break;
|
||||
|
||||
case 14:
|
||||
// write ping/pong
|
||||
d.state = 15;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
|
||||
// sent ping/pong
|
||||
case 15:
|
||||
d.fb.reset();
|
||||
d.state = 2;
|
||||
d.ws.wr_block_ = nullptr;
|
||||
break;
|
||||
|
||||
// fail the connection
|
||||
case 16:
|
||||
if(! d.ws.wr_close_)
|
||||
{
|
||||
d.fb.reset();
|
||||
d.ws.template write_close<
|
||||
static_streambuf>(d.fb, code);
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 17;
|
||||
d.ws.rd_op_.template emplace<
|
||||
read_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
d.state = 18;
|
||||
break;
|
||||
}
|
||||
|
||||
// resume
|
||||
case 17:
|
||||
if(d.ws.wr_close_)
|
||||
{
|
||||
d.state = 19;
|
||||
break;
|
||||
}
|
||||
d.state = 18;
|
||||
break;
|
||||
|
||||
case 18:
|
||||
// send close
|
||||
d.state = 19;
|
||||
d.ws.wr_close_ = true;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
d.fb.data(), std::move(*this));
|
||||
return;
|
||||
|
||||
// teardown
|
||||
case 19:
|
||||
d.state = 20;
|
||||
wsproto_helpers::call_async_teardown(
|
||||
d.ws.next_layer_, std::move(*this));
|
||||
return;
|
||||
|
||||
case 20:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = error::failed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ec)
|
||||
d.ws.error_ = true;
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
d.h(ec);
|
||||
d.ws.wr_op_.maybe_invoke();
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_READ_OP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_READ_OP_HPP
|
||||
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// read an entire message
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Streambuf, class Handler>
|
||||
class stream<NextLayer>::read_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
opcode& op;
|
||||
Streambuf& sb;
|
||||
Handler h;
|
||||
frame_info fi;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_,
|
||||
stream<NextLayer>& ws_, opcode& op_,
|
||||
Streambuf& sb_)
|
||||
: ws(ws_)
|
||||
, op(op_)
|
||||
, sb(sb_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()(
|
||||
error_code const& ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Streambuf, class Handler>
|
||||
void
|
||||
stream<NextLayer>::read_op<Streambuf, Handler>::
|
||||
operator()(error_code const& ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
// read payload
|
||||
d.state = 1;
|
||||
d.ws.async_read_frame(
|
||||
d.fi, d.sb, std::move(*this));
|
||||
return;
|
||||
|
||||
// got payload
|
||||
case 1:
|
||||
d.op = d.fi.op;
|
||||
d.state = d.fi.fin ? 99 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP
|
||||
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// Respond to an upgrade HTTP request
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
class stream<NextLayer>::response_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
http::response<http::string_body> resp;
|
||||
Handler h;
|
||||
error_code final_ec;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler,
|
||||
class Body, class Headers>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||
http::message<true, Body, Headers> const& req,
|
||||
bool cont_)
|
||||
: ws(ws_)
|
||||
, resp(ws_.build_response(req))
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(cont_)
|
||||
{
|
||||
if(resp.status != 101)
|
||||
final_ec = error::handshake_failed;
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
response_op(response_op&&) = default;
|
||||
response_op(response_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
response_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()(
|
||||
error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, response_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, response_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(response_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, response_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::response_op<Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
// send response
|
||||
d.state = 1;
|
||||
http::async_write(d.ws.next_layer_,
|
||||
d.resp, std::move(*this));
|
||||
return;
|
||||
|
||||
// sent response
|
||||
case 1:
|
||||
d.state = 99;
|
||||
ec = d.final_ec;
|
||||
if(! ec)
|
||||
d.ws.role_ = role_type::server;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
|
||||
#define BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED
|
||||
|
||||
#include <beast/async_completion.hpp>
|
||||
#include <beast/type_check.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
|
||||
See
|
||||
http://stackoverflow.com/questions/32046034/what-is-the-proper-way-to-securely-disconnect-an-asio-ssl-socket/32054476#32054476
|
||||
|
||||
Behavior of ssl::stream regarding close_
|
||||
|
||||
If the remote host calls async_shutdown then the
|
||||
local host's async_read will complete with eof.
|
||||
|
||||
If both hosts call async_shutdown then the calls
|
||||
to async_shutdown will complete with eof.
|
||||
|
||||
*/
|
||||
template<class AsyncStream, class Handler>
|
||||
class teardown_ssl_op
|
||||
{
|
||||
using stream_type =
|
||||
boost::asio::ssl::stream<AsyncStream>;
|
||||
|
||||
struct data
|
||||
{
|
||||
stream_type& stream;
|
||||
Handler h;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_,
|
||||
stream_type& stream_)
|
||||
: stream(stream_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
template<class DeducedHandler>
|
||||
explicit
|
||||
teardown_ssl_op(
|
||||
DeducedHandler&& h,
|
||||
stream_type& stream)
|
||||
: d_(std::make_shared<data>(
|
||||
std::forward<DeducedHandler>(h),
|
||||
stream))
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(std::size_t size,
|
||||
teardown_ssl_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(void* p,
|
||||
std::size_t size, teardown_ssl_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(
|
||||
teardown_ssl_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f,
|
||||
teardown_ssl_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class AsyncStream, class Handler>
|
||||
void
|
||||
teardown_ssl_op<AsyncStream, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(!ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 99;
|
||||
d.stream.async_shutdown(*this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class AsyncStream>
|
||||
void
|
||||
teardown(
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
error_code& ec)
|
||||
{
|
||||
stream.shutdown(ec);
|
||||
}
|
||||
|
||||
template<class AsyncStream, class TeardownHandler>
|
||||
void
|
||||
async_teardown(
|
||||
boost::asio::ssl::stream<AsyncStream>& stream,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_Handler<
|
||||
TeardownHandler, void(error_code)>::value,
|
||||
"TeardownHandler requirements not met");
|
||||
detail::teardown_ssl_op<AsyncStream, typename std::decay<
|
||||
TeardownHandler>::type>{std::forward<TeardownHandler>(
|
||||
handler), stream};
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,819 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_STREAM_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_STREAM_IPP
|
||||
|
||||
#include <beast/websocket/teardown.hpp>
|
||||
#include <beast/websocket/detail/hybi13.hpp>
|
||||
#include <beast/websocket/impl/accept_op.ipp>
|
||||
#include <beast/websocket/impl/close_op.ipp>
|
||||
#include <beast/websocket/impl/handshake_op.ipp>
|
||||
#include <beast/websocket/impl/read_op.ipp>
|
||||
#include <beast/websocket/impl/read_frame_op.ipp>
|
||||
#include <beast/websocket/impl/response_op.ipp>
|
||||
#include <beast/websocket/impl/write_op.ipp>
|
||||
#include <beast/websocket/impl/write_frame_op.ipp>
|
||||
#include <beast/buffer_cat.hpp>
|
||||
#include <beast/consuming_buffers.hpp>
|
||||
#include <beast/prepare_buffers.hpp>
|
||||
#include <beast/static_streambuf.hpp>
|
||||
#include <beast/streambuf.hpp>
|
||||
#include <beast/type_check.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/http/reason.hpp>
|
||||
#include <beast/http/rfc2616.hpp>
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class _>
|
||||
void
|
||||
stream_base::prepare_fh(close_code& code)
|
||||
{
|
||||
// continuation without an active message
|
||||
if(! rd_cont_ && rd_fh_.op == opcode::cont)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
// new data frame when continuation expected
|
||||
if(rd_cont_ && ! is_control(rd_fh_.op) &&
|
||||
rd_fh_.op != opcode::cont)
|
||||
{
|
||||
code = close_code::protocol_error;
|
||||
return;
|
||||
}
|
||||
if(rd_fh_.mask)
|
||||
prepare_key(rd_key_, rd_fh_.key);
|
||||
if(! is_control(rd_fh_.op))
|
||||
{
|
||||
if(rd_fh_.op != opcode::cont)
|
||||
{
|
||||
rd_size_ = rd_fh_.len;
|
||||
rd_opcode_ = rd_fh_.op;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rd_size_ > std::numeric_limits<
|
||||
std::uint64_t>::max() - rd_fh_.len)
|
||||
{
|
||||
code = close_code::too_big;
|
||||
return;
|
||||
}
|
||||
rd_size_ += rd_fh_.len;
|
||||
}
|
||||
if(rd_size_ > rd_msg_max_)
|
||||
{
|
||||
code = close_code::too_big;
|
||||
return;
|
||||
}
|
||||
rd_need_ = rd_fh_.len;
|
||||
rd_cont_ = ! rd_fh_.fin;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
stream_base::write_close(
|
||||
Streambuf& sb, close_reason const& cr)
|
||||
{
|
||||
using namespace boost::endian;
|
||||
frame_header fh;
|
||||
fh.op = opcode::close;
|
||||
fh.fin = true;
|
||||
fh.rsv1 = false;
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
fh.len = cr.code == close_code::none ?
|
||||
0 : 2 + cr.reason.size();
|
||||
if((fh.mask = (role_ == role_type::client)))
|
||||
fh.key = maskgen_();
|
||||
detail::write(sb, fh);
|
||||
if(cr.code != close_code::none)
|
||||
{
|
||||
detail::prepared_key_type key;
|
||||
if(fh.mask)
|
||||
detail::prepare_key(key, fh.key);
|
||||
{
|
||||
std::uint8_t b[2];
|
||||
::new(&b[0]) big_uint16_buf_t{
|
||||
(std::uint16_t)cr.code};
|
||||
auto d = sb.prepare(2);
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::buffer(b));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
sb.commit(2);
|
||||
}
|
||||
if(! cr.reason.empty())
|
||||
{
|
||||
auto d = sb.prepare(cr.reason.size());
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::const_buffer(
|
||||
cr.reason.data(), cr.reason.size()));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
sb.commit(cr.reason.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Streambuf>
|
||||
void
|
||||
stream_base::write_ping(Streambuf& sb,
|
||||
opcode op, ping_payload_type const& data)
|
||||
{
|
||||
frame_header fh;
|
||||
fh.op = op;
|
||||
fh.fin = true;
|
||||
fh.rsv1 = false;
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
fh.len = data.size();
|
||||
if((fh.mask = (role_ == role_type::client)))
|
||||
fh.key = maskgen_();
|
||||
detail::write(sb, fh);
|
||||
if(data.empty())
|
||||
return;
|
||||
detail::prepared_key_type key;
|
||||
if(fh.mask)
|
||||
detail::prepare_key(key, fh.key);
|
||||
auto d = sb.prepare(data.size());
|
||||
boost::asio::buffer_copy(d,
|
||||
boost::asio::const_buffers_1(
|
||||
data.data(), data.size()));
|
||||
if(fh.mask)
|
||||
detail::mask_inplace(d, key);
|
||||
sb.commit(data.size());
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
template<class... Args>
|
||||
stream<NextLayer>::
|
||||
stream(Args&&... args)
|
||||
: next_layer_(std::forward<Args>(args)...)
|
||||
, stream_(next_layer_)
|
||||
{
|
||||
static_assert(is_Stream<next_layer_type>::value,
|
||||
"Stream requirements not met");
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(error_code& ec)
|
||||
{
|
||||
accept(boost::asio::null_buffers{}, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class AcceptHandler>
|
||||
typename async_completion<
|
||||
AcceptHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_accept(AcceptHandler&& handler)
|
||||
{
|
||||
return async_accept(boost::asio::null_buffers{},
|
||||
std::forward<AcceptHandler>(handler));
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
accept(buffers, ec);
|
||||
detail::maybe_throw(ec, "accept");
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
stream_.buffer().commit(buffer_copy(
|
||||
stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
http::request<http::empty_body> m;
|
||||
http::read(next_layer_, stream_.buffer(), m, ec);
|
||||
if(ec)
|
||||
return;
|
||||
accept(m, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class AcceptHandler>
|
||||
typename async_completion<
|
||||
AcceptHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
AcceptHandler, void(error_code)
|
||||
> completion(handler);
|
||||
accept_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, bs};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Headers>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::message<true, Body, Headers> const& request)
|
||||
{
|
||||
error_code ec;
|
||||
accept(request, ec);
|
||||
detail::maybe_throw(ec, "accept");
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Headers>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::message<true, Body, Headers> const& req,
|
||||
error_code& ec)
|
||||
{
|
||||
auto resp = build_response(req);
|
||||
http::write(stream_, resp, ec);
|
||||
if(resp.status != 101)
|
||||
{
|
||||
ec = error::handshake_failed;
|
||||
// VFALCO TODO Respect keep alive setting, perform
|
||||
// teardown if Connection: close.
|
||||
return;
|
||||
}
|
||||
role_ = role_type::server;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Headers, class AcceptHandler>
|
||||
typename async_completion<
|
||||
AcceptHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_accept(http::message<true, Body, Headers> const& req,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
beast::async_completion<
|
||||
AcceptHandler, void(error_code)
|
||||
> completion(handler);
|
||||
response_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, req,
|
||||
boost_asio_handler_cont_helpers::
|
||||
is_continuation(completion.handler)};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
handshake(boost::string_ref const& host,
|
||||
boost::string_ref const& resource, error_code& ec)
|
||||
{
|
||||
std::string key;
|
||||
http::write(stream_,
|
||||
build_request(host, resource, key), ec);
|
||||
if(ec)
|
||||
return;
|
||||
http::response<http::string_body> resp;
|
||||
http::read(next_layer_, stream_.buffer(), resp, ec);
|
||||
if(ec)
|
||||
return;
|
||||
do_response(resp, key, ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class HandshakeHandler>
|
||||
typename async_completion<
|
||||
HandshakeHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_handshake(boost::string_ref const& host,
|
||||
boost::string_ref const& resource, HandshakeHandler&& handler)
|
||||
{
|
||||
beast::async_completion<
|
||||
HandshakeHandler, void(error_code)
|
||||
> completion(handler);
|
||||
handshake_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, host, resource};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
close(close_reason const& cr, error_code& ec)
|
||||
{
|
||||
assert(! wr_close_);
|
||||
wr_close_ = true;
|
||||
detail::frame_streambuf fb;
|
||||
write_close<static_streambuf>(fb, cr);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
error_ = ec != 0;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class CloseHandler>
|
||||
typename async_completion<
|
||||
CloseHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_close(close_reason const& cr, CloseHandler&& handler)
|
||||
{
|
||||
beast::async_completion<
|
||||
CloseHandler, void(error_code)
|
||||
> completion(handler);
|
||||
close_op<decltype(completion.handler)>{
|
||||
completion.handler, *this, cr};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Streambuf>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
read(opcode& op, Streambuf& streambuf, error_code& ec)
|
||||
{
|
||||
frame_info fi;
|
||||
for(;;)
|
||||
{
|
||||
read_frame(fi, streambuf, ec);
|
||||
if(ec)
|
||||
break;
|
||||
op = fi.op;
|
||||
if(fi.fin)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Streambuf, class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_read(opcode& op,
|
||||
Streambuf& streambuf, ReadHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
beast::async_completion<
|
||||
ReadHandler, void(error_code)
|
||||
> completion(handler);
|
||||
read_op<Streambuf, decltype(completion.handler)>{
|
||||
completion.handler, *this, op, streambuf};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Streambuf>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec)
|
||||
{
|
||||
close_code code{};
|
||||
for(;;)
|
||||
{
|
||||
if(rd_need_ == 0)
|
||||
{
|
||||
// read header
|
||||
detail::frame_streambuf fb;
|
||||
do_read_fh(fb, code, ec);
|
||||
if((error_ = ec != 0))
|
||||
return;
|
||||
if(code != close_code::none)
|
||||
break;
|
||||
if(detail::is_control(rd_fh_.op))
|
||||
{
|
||||
// read control payload
|
||||
if(rd_fh_.len > 0)
|
||||
{
|
||||
auto const mb = fb.prepare(
|
||||
static_cast<std::size_t>(rd_fh_.len));
|
||||
fb.commit(boost::asio::read(stream_, mb, ec));
|
||||
if((error_ = ec != 0))
|
||||
return;
|
||||
if(rd_fh_.mask)
|
||||
detail::mask_inplace(mb, rd_key_);
|
||||
fb.commit(static_cast<std::size_t>(rd_fh_.len));
|
||||
}
|
||||
if(rd_fh_.op == opcode::ping)
|
||||
{
|
||||
ping_payload_type data;
|
||||
detail::read(data, fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
break;
|
||||
fb.reset();
|
||||
write_ping<static_streambuf>(
|
||||
fb, opcode::pong, data);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
if((error_ = ec != 0))
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
else if(rd_fh_.op == opcode::pong)
|
||||
{
|
||||
ping_payload_type data;
|
||||
detail::read(data, fb.data(), code);
|
||||
if((error_ = ec != 0))
|
||||
break;
|
||||
// VFALCO How to notify callers using
|
||||
// the synchronous interface?
|
||||
continue;
|
||||
}
|
||||
assert(rd_fh_.op == opcode::close);
|
||||
{
|
||||
detail::read(cr_, fb.data(), code);
|
||||
if(code != close_code::none)
|
||||
break;
|
||||
if(! wr_close_)
|
||||
{
|
||||
auto cr = cr_;
|
||||
if(cr.code == close_code::none)
|
||||
cr.code = close_code::normal;
|
||||
cr.reason = "";
|
||||
fb.reset();
|
||||
wr_close_ = true;
|
||||
write_close<static_streambuf>(fb, cr);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
if((error_ = ec != 0))
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(rd_need_ == 0 && ! rd_fh_.fin)
|
||||
{
|
||||
// empty frame
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// read payload
|
||||
auto smb = streambuf.prepare(
|
||||
detail::clamp(rd_need_));
|
||||
auto const bytes_transferred =
|
||||
stream_.read_some(smb, ec);
|
||||
if((error_ = ec != 0))
|
||||
return;
|
||||
rd_need_ -= bytes_transferred;
|
||||
auto const pb = prepare_buffers(
|
||||
bytes_transferred, smb);
|
||||
if(rd_fh_.mask)
|
||||
detail::mask_inplace(pb, rd_key_);
|
||||
if(rd_opcode_ == opcode::text)
|
||||
{
|
||||
if(! rd_utf8_check_.write(pb) ||
|
||||
(rd_need_ == 0 && rd_fh_.fin &&
|
||||
! rd_utf8_check_.finish()))
|
||||
{
|
||||
code = close_code::bad_payload;
|
||||
break;
|
||||
}
|
||||
}
|
||||
streambuf.commit(bytes_transferred);
|
||||
fi.op = rd_opcode_;
|
||||
fi.fin = rd_fh_.fin && rd_need_ == 0;
|
||||
return;
|
||||
}
|
||||
if(code != close_code::none)
|
||||
{
|
||||
// Fail the connection (per rfc6455)
|
||||
if(! wr_close_)
|
||||
{
|
||||
wr_close_ = true;
|
||||
detail::frame_streambuf fb;
|
||||
write_close<static_streambuf>(fb, code);
|
||||
boost::asio::write(stream_, fb.data(), ec);
|
||||
if((error_ = ec != 0))
|
||||
return;
|
||||
}
|
||||
wsproto_helpers::call_teardown(next_layer_, ec);
|
||||
if((error_ = ec != 0))
|
||||
return;
|
||||
ec = error::failed;
|
||||
error_ = true;
|
||||
return;
|
||||
}
|
||||
if(! ec)
|
||||
wsproto_helpers::call_teardown(next_layer_, ec);
|
||||
if(! ec)
|
||||
ec = error::closed;
|
||||
error_ = ec != 0;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Streambuf, class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_read_frame(frame_info& fi,
|
||||
Streambuf& streambuf, ReadHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
beast::async_completion<
|
||||
ReadHandler, void(error_code)> completion(handler);
|
||||
read_frame_op<Streambuf, decltype(completion.handler)>{
|
||||
completion.handler, *this, fi, streambuf};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write(ConstBufferSequence const& bs, error_code& ec)
|
||||
{
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_size;
|
||||
consuming_buffers<ConstBufferSequence> cb(bs);
|
||||
auto remain = buffer_size(cb);
|
||||
for(;;)
|
||||
{
|
||||
auto const n =
|
||||
detail::clamp(remain, wr_frag_size_);
|
||||
remain -= n;
|
||||
auto const fin = remain <= 0;
|
||||
write_frame(fin, prepare_buffers(n, cb), ec);
|
||||
cb.consume(n);
|
||||
if(ec)
|
||||
return;
|
||||
if(fin)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_write(ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
WriteHandler, void(error_code)> completion(handler);
|
||||
write_op<ConstBufferSequence, decltype(completion.handler)>{
|
||||
completion.handler, *this, bs};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec)
|
||||
{
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::mutable_buffers_1;
|
||||
detail::frame_header fh;
|
||||
fh.op = wr_cont_ ? opcode::cont : wr_opcode_;
|
||||
wr_cont_ = ! fin;
|
||||
fh.fin = fin;
|
||||
fh.rsv1 = false;
|
||||
fh.rsv2 = false;
|
||||
fh.rsv3 = false;
|
||||
fh.len = buffer_size(bs);
|
||||
if((fh.mask = (role_ == role_type::client)))
|
||||
fh.key = maskgen_();
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::write<static_streambuf>(fh_buf, fh);
|
||||
if(! fh.mask)
|
||||
{
|
||||
// send header and payload
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), bs), ec);
|
||||
error_ = ec != 0;
|
||||
return;
|
||||
}
|
||||
detail::prepared_key_type key;
|
||||
detail::prepare_key(key, fh.key);
|
||||
auto const tmp_size = detail::clamp(
|
||||
fh.len, wr_buf_size_);
|
||||
std::unique_ptr<std::uint8_t[]> up(
|
||||
new std::uint8_t[tmp_size]);
|
||||
auto const tmp = up.get();
|
||||
std::uint64_t remain = fh.len;
|
||||
consuming_buffers<ConstBufferSequence> cb(bs);
|
||||
{
|
||||
auto const n =
|
||||
detail::clamp(remain, tmp_size);
|
||||
mutable_buffers_1 mb{tmp, n};
|
||||
buffer_copy(mb, cb);
|
||||
cb.consume(n);
|
||||
remain -= n;
|
||||
detail::mask_inplace(mb, key);
|
||||
// send header and payload
|
||||
boost::asio::write(stream_,
|
||||
buffer_cat(fh_buf.data(), mb), ec);
|
||||
if(ec)
|
||||
{
|
||||
error_ = ec != 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
while(remain > 0)
|
||||
{
|
||||
auto const n =
|
||||
detail::clamp(remain, tmp_size);
|
||||
mutable_buffers_1 mb{tmp, n};
|
||||
buffer_copy(mb, cb);
|
||||
cb.consume(n);
|
||||
remain -= n;
|
||||
detail::mask_inplace(mb, key);
|
||||
// send payload
|
||||
boost::asio::write(stream_, mb, ec);
|
||||
if(ec)
|
||||
{
|
||||
error_ = ec != 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_write_frame(bool fin,
|
||||
ConstBufferSequence const& bs, WriteHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_ConstBufferSequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
WriteHandler, void(error_code)
|
||||
> completion(handler);
|
||||
write_frame_op<ConstBufferSequence, decltype(
|
||||
completion.handler)>{completion.handler,
|
||||
*this, fin, bs};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class NextLayer>
|
||||
http::request<http::empty_body>
|
||||
stream<NextLayer>::
|
||||
build_request(boost::string_ref const& host,
|
||||
boost::string_ref const& resource, std::string& key)
|
||||
{
|
||||
http::request<http::empty_body> req;
|
||||
req.url = "/";
|
||||
req.version = 11;
|
||||
req.method = http::method_t::http_get;
|
||||
req.headers.insert("Host", host);
|
||||
req.headers.insert("Connection", "upgrade");
|
||||
req.headers.insert("Upgrade", "websocket");
|
||||
key = detail::make_sec_ws_key(maskgen_);
|
||||
req.headers.insert("Sec-WebSocket-Key", key);
|
||||
req.headers.insert("Sec-WebSocket-Version", "13");
|
||||
(*d_)(req);
|
||||
return req;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Headers>
|
||||
http::response<http::string_body>
|
||||
stream<NextLayer>::
|
||||
build_response(http::message<true, Body, Headers> const& req)
|
||||
{
|
||||
auto err =
|
||||
[&](std::string const& text)
|
||||
{
|
||||
http::response<http::string_body> resp(
|
||||
{400, http::reason_string(400), req.version});
|
||||
resp.body = text;
|
||||
// VFALCO TODO respect keep-alive here
|
||||
return resp;
|
||||
};
|
||||
if(req.version < 11)
|
||||
return err("HTTP version 1.1 required");
|
||||
if(req.method != http::method_t::http_get)
|
||||
return err("Wrong method");
|
||||
if(! is_upgrade(req))
|
||||
return err("Expected Upgrade request");
|
||||
if(! req.headers.exists("Host"))
|
||||
return err("Missing Host");
|
||||
if(! req.headers.exists("Sec-WebSocket-Key"))
|
||||
return err("Missing Sec-WebSocket-Key");
|
||||
{
|
||||
auto const version =
|
||||
req.headers["Sec-WebSocket-Version"];
|
||||
if(version.empty())
|
||||
return err("Missing Sec-WebSocket-Version");
|
||||
if(version != "13")
|
||||
return err("Unsupported Sec-WebSocket-Version");
|
||||
}
|
||||
if(! rfc2616::token_in_list(
|
||||
req.headers["Upgrade"], "websocket"))
|
||||
return err("Missing websocket Upgrade token");
|
||||
http::response<http::string_body> resp(
|
||||
{101, http::reason_string(101), req.version});
|
||||
resp.headers.insert("Upgrade", "websocket");
|
||||
resp.headers.insert("Connection", "upgrade");
|
||||
{
|
||||
auto const key =
|
||||
req.headers["Sec-WebSocket-Key"];
|
||||
resp.headers.insert("Sec-WebSocket-Key", key);
|
||||
resp.headers.insert("Sec-WebSocket-Accept",
|
||||
detail::make_sec_ws_accept(key));
|
||||
}
|
||||
resp.headers.replace("Server", "Beast.WSProto");
|
||||
(*d_)(resp);
|
||||
return resp;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Headers>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_response(http::message<false, Body, Headers> const& resp,
|
||||
boost::string_ref const& key, error_code& ec)
|
||||
{
|
||||
// VFALCO Review these error codes
|
||||
auto fail = [&]{ ec = error::response_failed; };
|
||||
if(resp.status != 101)
|
||||
return fail();
|
||||
if(! is_upgrade(resp))
|
||||
return fail();
|
||||
if(! rfc2616::ci_equal(
|
||||
resp.headers["Upgrade"], "websocket"))
|
||||
return fail();
|
||||
if(! resp.headers.exists("Sec-WebSocket-Accept"))
|
||||
return fail();
|
||||
if(resp.headers["Sec-WebSocket-Accept"] !=
|
||||
detail::make_sec_ws_accept(key))
|
||||
return fail();
|
||||
role_ = role_type::client;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_read_fh(detail::frame_streambuf& fb,
|
||||
close_code& code, error_code& ec)
|
||||
{
|
||||
fb.commit(boost::asio::read(
|
||||
stream_, fb.prepare(2), ec));
|
||||
if(ec)
|
||||
return;
|
||||
auto const n = detail::read_fh1(
|
||||
rd_fh_, fb, role_, code);
|
||||
if(code != close_code::none)
|
||||
return;
|
||||
if(n > 0)
|
||||
{
|
||||
fb.commit(boost::asio::read(
|
||||
stream_, fb.prepare(n), ec));
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
detail::read_fh2(
|
||||
rd_fh_, fb, role_, code);
|
||||
if(code != close_code::none)
|
||||
return;
|
||||
prepare_fh(code);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP
|
||||
|
||||
#include <beast/async_completion.hpp>
|
||||
#include <beast/type_check.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Handler>
|
||||
class teardown_tcp_op
|
||||
{
|
||||
using socket_type =
|
||||
boost::asio::ip::tcp::socket;
|
||||
|
||||
struct data
|
||||
{
|
||||
socket_type& socket;
|
||||
Handler h;
|
||||
char buf[8192];
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_,
|
||||
socket_type& socket_)
|
||||
: socket(socket_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
template<class DeducedHandler>
|
||||
teardown_tcp_op(
|
||||
DeducedHandler&& h,
|
||||
socket_type& socket)
|
||||
: d_(std::make_shared<data>(
|
||||
std::forward<DeducedHandler>(h),
|
||||
socket))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec, std::size_t, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(std::size_t size,
|
||||
teardown_tcp_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(void* p,
|
||||
std::size_t size, teardown_tcp_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(teardown_tcp_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f,
|
||||
teardown_tcp_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Handler>
|
||||
void
|
||||
teardown_tcp_op<Handler>::
|
||||
operator()(error_code ec, std::size_t, bool again)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
d.socket.shutdown(
|
||||
boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
d.socket.async_read_some(
|
||||
buffer(d.buf), std::move(*this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
d.socket.close(ec);
|
||||
ec = error_code{};
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
void
|
||||
teardown(
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
socket.shutdown(
|
||||
boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||
while(! ec)
|
||||
{
|
||||
char buf[8192];
|
||||
auto const n = socket.read_some(
|
||||
buffer(buf), ec);
|
||||
if(! n)
|
||||
break;
|
||||
}
|
||||
if(ec == boost::asio::error::eof)
|
||||
ec = error_code{};
|
||||
socket.close(ec);
|
||||
}
|
||||
|
||||
template<class TeardownHandler>
|
||||
inline
|
||||
void
|
||||
async_teardown(
|
||||
boost::asio::ip::tcp::socket& socket,
|
||||
TeardownHandler&& handler)
|
||||
{
|
||||
static_assert(beast::is_Handler<
|
||||
TeardownHandler, void(error_code)>::value,
|
||||
"TeardownHandler requirements not met");
|
||||
detail::teardown_tcp_op<typename std::decay<
|
||||
TeardownHandler>::type>{std::forward<
|
||||
TeardownHandler>(handler), socket};
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,265 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP
|
||||
|
||||
#include <beast/buffer_cat.hpp>
|
||||
#include <beast/bind_handler.hpp>
|
||||
#include <beast/consuming_buffers.hpp>
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/static_streambuf.hpp>
|
||||
#include <beast/websocket/detail/frame.hpp>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// write a frame
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
class stream<NextLayer>::write_frame_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data : op
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
consuming_buffers<Buffers> cb;
|
||||
Handler h;
|
||||
detail::frame_header fh;
|
||||
detail::fh_streambuf fh_buf;
|
||||
detail::prepared_key_type key;
|
||||
void* tmp;
|
||||
std::size_t tmp_size;
|
||||
std::uint64_t remain;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, stream<NextLayer>& ws_,
|
||||
bool fin, Buffers const& bs)
|
||||
: ws(ws_)
|
||||
, cb(bs)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
fh.op = ws.wr_cont_ ?
|
||||
opcode::cont : ws.wr_opcode_;
|
||||
ws.wr_cont_ = ! fin;
|
||||
fh.fin = fin;
|
||||
fh.rsv1 = 0;
|
||||
fh.rsv2 = 0;
|
||||
fh.rsv3 = 0;
|
||||
fh.len = boost::asio::buffer_size(cb);
|
||||
if((fh.mask = (ws.role_ == role_type::client)))
|
||||
{
|
||||
fh.key = ws.maskgen_();
|
||||
detail::prepare_key(key, fh.key);
|
||||
tmp_size = detail::clamp(
|
||||
fh.len, ws.wr_buf_size_);
|
||||
tmp = boost_asio_handler_alloc_helpers::
|
||||
allocate(tmp_size, h);
|
||||
remain = fh.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = nullptr;
|
||||
}
|
||||
detail::write<static_streambuf>(fh_buf, fh);
|
||||
}
|
||||
|
||||
~data()
|
||||
{
|
||||
if(tmp)
|
||||
boost_asio_handler_alloc_helpers::
|
||||
deallocate(tmp, tmp_size, h);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
write_frame_op(write_frame_op&&) = default;
|
||||
write_frame_op(write_frame_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_frame_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::make_shared<data>(
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = false;
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_frame_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_frame_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_frame_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_frame_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_frame_op<Buffers, Handler>::
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::mutable_buffers_1;
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.ws.wr_block_)
|
||||
{
|
||||
// suspend
|
||||
d.state = 1;
|
||||
d.ws.wr_op_.template emplace<
|
||||
write_frame_op>(std::move(*this));
|
||||
return;
|
||||
}
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.ws.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
boost::asio::error::operation_aborted, 0));
|
||||
return;
|
||||
}
|
||||
assert(! d.ws.wr_close_);
|
||||
d.state = 2;
|
||||
break;
|
||||
|
||||
// resume
|
||||
case 1:
|
||||
if(d.ws.error_)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
ec = boost::asio::error::operation_aborted;
|
||||
break;
|
||||
}
|
||||
d.state = 2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
if(! d.fh.mask)
|
||||
{
|
||||
// send header and payload
|
||||
d.state = 99;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(d.fh_buf.data(), d.cb),
|
||||
std::move(*this));
|
||||
return;
|
||||
}
|
||||
auto const n =
|
||||
detail::clamp(d.remain, d.tmp_size);
|
||||
mutable_buffers_1 mb{d.tmp, n};
|
||||
buffer_copy(mb, d.cb);
|
||||
d.cb.consume(n);
|
||||
d.remain -= n;
|
||||
detail::mask_inplace(mb, d.key);
|
||||
// send header and payload
|
||||
d.state = d.remain > 0 ? 3 : 99;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(d.ws.stream_,
|
||||
buffer_cat(d.fh_buf.data(),
|
||||
mb), std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
// sent masked payload
|
||||
case 3:
|
||||
{
|
||||
auto const n =
|
||||
detail::clamp(d.remain, d.tmp_size);
|
||||
mutable_buffers_1 mb{d.tmp,
|
||||
static_cast<std::size_t>(n)};
|
||||
buffer_copy(mb, d.cb);
|
||||
d.cb.consume(n);
|
||||
d.remain -= n;
|
||||
detail::mask_inplace(mb, d.key);
|
||||
// send payload
|
||||
if(d.remain == 0)
|
||||
d.state = 99;
|
||||
assert(! d.ws.wr_block_);
|
||||
d.ws.wr_block_ = &d;
|
||||
boost::asio::async_write(
|
||||
d.ws.stream_, mb, std::move(*this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ec)
|
||||
d.ws.error_ = true;
|
||||
if(d.ws.wr_block_ == &d)
|
||||
d.ws.wr_block_ = nullptr;
|
||||
if(d.tmp)
|
||||
{
|
||||
boost_asio_handler_alloc_helpers::
|
||||
deallocate(d.tmp, d.tmp_size, d.h);
|
||||
d.tmp = nullptr;
|
||||
}
|
||||
d.h(ec);
|
||||
d.ws.rd_op_.maybe_invoke();
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP
|
||||
#define BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP
|
||||
|
||||
#include <beast/consuming_buffers.hpp>
|
||||
#include <beast/prepare_buffers.hpp>
|
||||
#include <beast/handler_alloc.hpp>
|
||||
#include <beast/websocket/detail/frame.hpp>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
// write a message
|
||||
//
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
class stream<NextLayer>::write_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data : op
|
||||
{
|
||||
stream<NextLayer>& ws;
|
||||
consuming_buffers<Buffers> cb;
|
||||
Handler h;
|
||||
std::size_t remain;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_,
|
||||
stream<NextLayer>& ws_, Buffers const& bs)
|
||||
: ws(ws_)
|
||||
, cb(bs)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, remain(boost::asio::buffer_size(cb))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
explicit
|
||||
write_op(DeducedHandler&& h,
|
||||
stream<NextLayer>& ws, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), ws,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template <class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Buffers, class Handler>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
write_op<Buffers, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto const n = std::min(
|
||||
d.remain, d.ws.wr_frag_size_);
|
||||
d.remain -= n;
|
||||
auto const fin = d.remain <= 0;
|
||||
if(fin)
|
||||
d.state = 99;
|
||||
d.ws.async_write_frame(fin,
|
||||
prepare_buffers(n, d.cb), std::move(*this));
|
||||
d.cb.consume(n);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,293 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WEBSOCKET_OPTION_HPP
|
||||
#define BEAST_WEBSOCKET_OPTION_HPP
|
||||
|
||||
#include <beast/websocket/detail/stream_base.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace websocket {
|
||||
|
||||
/** Automatic fragmentation size option.
|
||||
|
||||
Sets the maximum size of fragments generated when sending
|
||||
messages on a WebSocket socket.
|
||||
|
||||
When the automatic fragmentation size is non-zero, messages
|
||||
exceeding the size will be split into multiple frames no
|
||||
larger than the size. This setting does not affect frames
|
||||
send explicitly using `write_frame` or `async_write_frame`.
|
||||
|
||||
The default setting is to fragment messages into 16KB frames.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the automatic fragmentation size option:
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> stream(ios);
|
||||
stream.set_option(auto_fragment_size{8192});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using auto_fragment_size = implementation_defined;
|
||||
#else
|
||||
struct auto_fragment_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
auto_fragment_size(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** HTTP decorator option.
|
||||
|
||||
The decorator transforms the HTTP requests and responses used
|
||||
when requesting or responding to the WebSocket Upgrade. This may
|
||||
be used to set or change header fields. For example to set the
|
||||
Server or User-Agent fields. The default setting applies no
|
||||
transformation to the HTTP message.
|
||||
|
||||
For synchronous operations, the implementation will call the
|
||||
decorator before the function call to perform the operation
|
||||
returns.
|
||||
|
||||
For asynchronous operations, the implementation guarantees that
|
||||
calls to the decorator will be made from the same implicit or
|
||||
explicit strand used to call the asynchronous initiation
|
||||
function.
|
||||
|
||||
The default setting is no decorator.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the decorator.
|
||||
@code
|
||||
struct identity
|
||||
{
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
void
|
||||
operator()(http::message<isRequest, Body, Headers>& m)
|
||||
{
|
||||
if(isRequest)
|
||||
m.headers.replace("User-Agent", "MyClient");
|
||||
else
|
||||
m.headers.replace("Server", "MyServer");
|
||||
}
|
||||
};
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(decorate(identity{}));
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using decorate = implementation_defined;
|
||||
#else
|
||||
template<class Decorator>
|
||||
inline
|
||||
detail::decorator_type
|
||||
decorate(Decorator&& d)
|
||||
{
|
||||
return detail::decorator_type{new
|
||||
detail::decorator<typename std::decay<Decorator>::type>{
|
||||
std::forward<Decorator>(d)}};
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Keep-alive option.
|
||||
|
||||
Determines if the connection is closed after a failed upgrade
|
||||
request.
|
||||
|
||||
This setting only affects the behavior of HTTP requests that
|
||||
implicitly or explicitly ask for a keepalive. For HTTP requests
|
||||
that indicate the connection should be closed, the connection is
|
||||
closed as per rfc2616.
|
||||
|
||||
The default setting is to close connections after a failed
|
||||
upgrade request.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the keep alive option.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(keep_alive{8192});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using keep_alive = implementation_defined;
|
||||
#else
|
||||
struct keep_alive
|
||||
{
|
||||
bool value;
|
||||
|
||||
keep_alive(bool v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Message type option.
|
||||
|
||||
This controls the opcode set for outgoing messages. Valid
|
||||
choices are opcode::binary or opcode::text. The setting is
|
||||
only applied at the start when a caller begins a new message.
|
||||
Changing the opcode after a message is started will only
|
||||
take effect after the current message being sent is complete.
|
||||
|
||||
The default setting is opcode::text.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the message type to binary.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(message_type{opcode::binary});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using message_type = implementation_defined;
|
||||
#else
|
||||
struct message_type
|
||||
{
|
||||
opcode value;
|
||||
|
||||
explicit
|
||||
message_type(opcode op)
|
||||
{
|
||||
if(op != opcode::binary && op != opcode::text)
|
||||
throw std::domain_error("bad opcode");
|
||||
value = op;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Read buffer size option.
|
||||
|
||||
Sets the number of bytes allocated to the socket's read buffer.
|
||||
If this is zero, then reads are not buffered. Setting this
|
||||
higher can improve performance when expecting to receive
|
||||
many small frames.
|
||||
|
||||
The default is no buffering.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the read buffer size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(read_buffer_size{16 * 1024});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using read_buffer_size = implementation_defined;
|
||||
#else
|
||||
struct read_buffer_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
read_buffer_size(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Maximum incoming message size option.
|
||||
|
||||
Sets the largest permissible incoming message size. Message
|
||||
frame headers indicating a size that would bring the total
|
||||
message size over this limit will cause a protocol failure.
|
||||
|
||||
The default setting is 16 megabytes.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the maximum read message size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(read_message_max{65536});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using read_message_max = implementation_defined;
|
||||
#else
|
||||
struct read_message_max
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
read_message_max(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Write buffer size option.
|
||||
|
||||
Sets the number of bytes allocated to the socket's write buffer.
|
||||
This buffer is used to hold masked frame payload data. Lowering
|
||||
the size of the buffer can decrease the memory requirements for
|
||||
each connection, at the cost of an increased number of calls to
|
||||
perform socket writes.
|
||||
|
||||
This setting does not affect connections operating in the server
|
||||
role, since servers do not apply a masking key to frame payloads.
|
||||
|
||||
The default setting is 4096. The minimum value is 1024.
|
||||
|
||||
@note Objects of this type are passed to socket::set_option.
|
||||
|
||||
@par Example
|
||||
Setting the write buffer size.
|
||||
@code
|
||||
...
|
||||
websocket::stream<ip::tcp::socket> ws(ios);
|
||||
ws.set_option(write_buffer_size{8192});
|
||||
@endcode
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using write_buffer_size = implementation_defined;
|
||||
#else
|
||||
struct write_buffer_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
write_buffer_size(std::size_t n)
|
||||
: value(n)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user