Merge subtree Beast 1.0.0-b2:

Merge commit '6c8b4b2f8dde72b01507e4ac7fde4ffea57ebc99'
This commit is contained in:
Vinnie Falco
2016-04-29 05:24:40 -04:00
87 changed files with 7083 additions and 2490 deletions

View File

@@ -16,13 +16,12 @@
namespace beast {
/** Completion helper for implementing the extensible asynchronous model.
/** 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 type
of the final handler.
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,
@@ -30,14 +29,16 @@ namespace beast {
@tparam Signature The callable signature of the final completion handler.
Usage:
Example:
@code
...
template<class CompletionToken>
typename async_completion<CompletionToken, Signature>::result_type
typename async_completion<CompletionToken,
void(boost::system::error_code)>::result_type
async_initfn(..., CompletionToken&& token)
{
async_completion<CompletionToken, Signature> completion(token);
async_completion<CompletionToken,
void(boost::system::error_code)> completion(token);
...
return completion.result.get();
}
@@ -49,19 +50,19 @@ namespace beast {
template <class CompletionToken, class Signature>
struct async_completion
{
/** The type of the final handler.
/** The type of the final handler called by the asynchronous initiation function.
Objects of this type will be callable with the
specified signature.
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 completion helper.
/** Construct the helper.
@param token The completion token. Copies will be made as
required. If `CompletionToken` is movable, it may also be moved.
@@ -74,10 +75,10 @@ struct async_completion
"Handler requirements not met");
}
/** The final completion handler, callable with the specified signature. */
/// The final completion handler, callable with the specified signature.
handler_type handler;
/** The return value of the asynchronous initiation function. */
/// The return value of the asynchronous initiation function.
boost::asio::async_result<handler_type> result;
};

View File

@@ -36,6 +36,7 @@ class basic_streambuf
#endif
{
public:
/// The type of allocator used.
using allocator_type = typename
std::allocator_traits<Allocator>::
template rebind_alloc<std::uint8_t>;
@@ -73,10 +74,20 @@ private:
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();
@@ -175,7 +186,7 @@ public:
template<class OtherAlloc>
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const& other);
/** Construct a stream buffer.
/** 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
@@ -258,60 +269,6 @@ private:
debug_check() const;
};
/// The type used to represent the input sequence as a list of buffers.
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:
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;
};
/// The type used to represent the output sequence as a list of buffers.
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;
};
/** Format output to a stream buffer.
@param streambuf The streambuf to write to.
@@ -322,7 +279,7 @@ public:
*/
template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& buf, T const& t);
operator<<(basic_streambuf<Alloc>& streambuf, T const& t);
/** Convert the entire basic_streambuf to a string.

View File

@@ -121,7 +121,7 @@ public:
Example:
@code
template<class AsyncReadStream, ReadHandler>
template<class AsyncReadStream, class ReadHandler>
void
do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
{

View File

@@ -9,11 +9,6 @@
#define BEAST_BUFFERS_ADAPTER_HPP
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <stdexcept>
#include <type_traits>
namespace beast {
@@ -70,19 +65,30 @@ private:
}
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;
// Move constructor.
#endif
/// Move constructor.
buffers_adapter(buffers_adapter&& other);
// Copy constructor.
/// Copy constructor.
buffers_adapter(buffers_adapter const& other);
// Move assignment.
/// Move assignment.
buffers_adapter& operator=(buffers_adapter&& other);
// Copy assignment.
/// Copy assignment.
buffers_adapter& operator=(buffers_adapter const&);
/** Construct a buffers adapter.
@@ -129,508 +135,8 @@ public:
consume(std::size_t n);
};
//------------------------------------------------------------------------------
/// The type used to represent the input sequence as a list of buffers.
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_)};
}
//------------------------------------------------------------------------------
/// The type used to represent the output sequence as a list of buffers.
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
#include <beast/impl/buffers_adapter.ipp>
#endif

View File

@@ -14,6 +14,11 @@
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)

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_CONSUMING_BUFFERS_HPP
#define BEAST_CONSUMING_BUFFERS_HPP
#include <beast/type_check.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
@@ -64,8 +63,15 @@ 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&&);
@@ -104,194 +110,13 @@ public:
consume(std::size_t n);
};
/// A bidirectional iterator type that may be used to read elements.
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())
{
}
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;
}
}
/// Returns a consumed buffer
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;
}
consumed_buffers(Buffers const& bs, std::size_t n);
} // beast
#include <beast/impl/consuming_buffers.ipp>
#endif

View File

@@ -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

View File

@@ -22,12 +22,21 @@ namespace beast {
/** An allocator that uses handler customizations.
This allocator uses the handler customizations `asio_handler_allocate`
and `asio_handler_deallocate` to manage memory.
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 object
@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
{
@@ -132,6 +141,7 @@ public:
return !(lhs == rhs);
}
};
#endif
} // beast

View File

@@ -15,6 +15,7 @@
#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>
@@ -22,7 +23,6 @@
#include <beast/http/rfc2616.hpp>
#include <beast/http/streambuf_body.hpp>
#include <beast/http/string_body.hpp>
#include <beast/http/type_check.hpp>
#include <beast/http/write.hpp>
#endif

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_HTTP_BASIC_HEADERS_HPP
#define BEAST_HTTP_BASIC_HEADERS_HPP
#include <beast/http/detail/writes.hpp>
#include <beast/type_check.hpp>
#include <beast/detail/ci_char_traits.hpp>
#include <beast/detail/empty_base_optimization.hpp>

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -1,71 +0,0 @@
//
// 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_ERROR_HPP
#define BEAST_HTTP_DETAIL_ERROR_HPP
#include <beast/http/impl/http_parser.h>
#include <boost/system/error_code.hpp>
namespace beast {
namespace http {
namespace detail {
class message_category
: public boost::system::error_category
{
public:
const char*
name() const noexcept override
{
return "http error";
}
std::string
message(int ev) const override
{
return http_errno_description(
static_cast<http_errno>(ev));
}
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(boost::system::error_code const& error,
int ev) const noexcept override
{
return error.value() == ev &&
&error.category() == this;
}
};
template<class = void>
boost::system::error_code
make_error(int http_errno)
{
static message_category const mc{};
return boost::system::error_code{http_errno, mc};
}
} // detail
} // http
} // beast
#endif

View File

@@ -10,6 +10,7 @@
#include <beast/http/error.hpp>
#include <beast/streambuf.hpp>
#include <beast/write_streambuf.hpp>
namespace beast {
namespace http {
@@ -84,7 +85,7 @@ struct write_preparation
msg.write_firstline(sb);
write_fields(sb, h);
detail::write(sb, "\r\n");
beast::write(sb, "\r\n");
}
private:

View File

@@ -1,66 +0,0 @@
//
// 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_WRITES_HPP
#define BEAST_HTTP_DETAIL_WRITES_HPP
#include <beast/type_check.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/utility/string_ref.hpp>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
namespace detail {
template<class Streambuf, class T,
class = typename std::enable_if<
is_Streambuf<Streambuf>::value>::type>
void
write(Streambuf& streambuf, T&& t)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
auto const& s =
boost::lexical_cast<std::string>(
std::forward<T>(t));
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()), buffer(s)));
}
template<class Streambuf, std::size_t N,
class = typename std::enable_if< (N>0) &&
is_Streambuf<Streambuf>::value>::type>
void
write(Streambuf& streambuf, char const(&s)[N])
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
streambuf.commit(buffer_copy(
streambuf.prepare(N), buffer(s, N-1)));
}
template<class Streambuf,
class = typename std::enable_if<
is_Streambuf<Streambuf>::value>::type>
void
write(Streambuf& streambuf, boost::string_ref const& s)
{
using boost::asio::buffer;
using boost::asio::buffer_copy;
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()),
buffer(s.data(), s.size())));
}
} // detail
} // http
} // beast
#endif

View File

@@ -22,9 +22,16 @@ namespace http {
*/
struct empty_body
{
struct value_type
{
};
#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
{

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
#include <beast/http/detail/writes.hpp>
#include <beast/type_check.hpp>
namespace beast {

File diff suppressed because it is too large Load Diff

View File

@@ -1,362 +0,0 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* 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.
*/
#ifndef http_parser_h
#define http_parser_h
#ifdef __cplusplus
extern "C" {
#endif
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 7
#define HTTP_PARSER_VERSION_PATCH 0
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
#include <stddef.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -9,11 +9,11 @@
#define BEAST_HTTP_IMPL_MESSAGE_IPP
#include <beast/http/chunk_encode.hpp>
#include <beast/http/type_check.hpp>
#include <beast/http/detail/writes.hpp>
#include <beast/http/detail/write_preparation.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>
@@ -26,16 +26,12 @@ template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message()
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
}
template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message(request_params params)
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(isRequest, "message is not a request");
this->method = params.method;
this->url = std::move(params.url);
@@ -46,8 +42,6 @@ template<bool isRequest, class Body, class Headers>
message<isRequest, Body, Headers>::
message(response_params params)
{
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(! isRequest, "message is not a response");
this->status = params.status;
this->reason = std::move(params.reason);
@@ -61,23 +55,23 @@ message<isRequest, Body, Headers>::
write_firstline(Streambuf& streambuf,
std::true_type) const
{
detail::write(streambuf, to_string(this->method));
detail::write(streambuf, " ");
detail::write(streambuf, this->url);
write(streambuf, to_string(this->method));
write(streambuf, " ");
write(streambuf, this->url);
switch(version)
{
case 10:
detail::write(streambuf, " HTTP/1.0\r\n");
write(streambuf, " HTTP/1.0\r\n");
break;
case 11:
detail::write(streambuf, " HTTP/1.1\r\n");
write(streambuf, " HTTP/1.1\r\n");
break;
default:
detail::write(streambuf, " HTTP/");
detail::write(streambuf, version / 10);
detail::write(streambuf, ".");
detail::write(streambuf, version % 10);
detail::write(streambuf, "\r\n");
write(streambuf, " HTTP/");
write(streambuf, version / 10);
write(streambuf, ".");
write(streambuf, version % 10);
write(streambuf, "\r\n");
break;
}
}
@@ -92,23 +86,23 @@ write_firstline(Streambuf& streambuf,
switch(version)
{
case 10:
detail::write(streambuf, "HTTP/1.0 ");
write(streambuf, "HTTP/1.0 ");
break;
case 11:
detail::write(streambuf, "HTTP/1.1 ");
write(streambuf, "HTTP/1.1 ");
break;
default:
detail::write(streambuf, " HTTP/");
detail::write(streambuf, version / 10);
detail::write(streambuf, ".");
detail::write(streambuf, version % 10);
detail::write(streambuf, " ");
write(streambuf, " HTTP/");
write(streambuf, version / 10);
write(streambuf, ".");
write(streambuf, version % 10);
write(streambuf, " ");
break;
}
detail::write(streambuf, this->status);
detail::write(streambuf, " ");
detail::write(streambuf, this->reason);
detail::write(streambuf, "\r\n");
write(streambuf, this->status);
write(streambuf, " ");
write(streambuf, this->reason);
write(streambuf, "\r\n");
}
namespace detail {
@@ -160,8 +154,6 @@ std::ostream&
operator<<(std::ostream& os,
message<isRequest, Body, Headers> const& msg)
{
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
error_code ec;
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
@@ -282,10 +274,10 @@ write_fields(Streambuf& streambuf, FieldSequence const& fields)
// "FieldSequence requirements not met");
for(auto const& field : fields)
{
detail::write(streambuf, field.name());
detail::write(streambuf, ": ");
detail::write(streambuf, field.value());
detail::write(streambuf, "\r\n");
write(streambuf, field.name());
write(streambuf, ": ");
write(streambuf, field.value());
write(streambuf, "\r\n");
}
}

View File

@@ -8,7 +8,6 @@
#ifndef BEAST_HTTP_IMPL_READ_IPP_HPP
#define BEAST_HTTP_IMPL_READ_IPP_HPP
#include <beast/http/type_check.hpp>
#include <beast/bind_handler.hpp>
#include <beast/handler_alloc.hpp>
#include <cassert>
@@ -39,6 +38,7 @@ class read_op
message_type& m;
parser_type p;
Handler h;
bool started = false;
bool cont;
int state = 0;
@@ -130,6 +130,8 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
bind_handler(std::move(*this), ec, 0));
return;
}
if(used > 0)
d.started = true;
d.sb.consume(used);
if(d.p.complete())
{
@@ -157,7 +159,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
{
if(ec == boost::asio::error::eof)
{
if(! d.p.started())
if(! d.started)
{
// call handler
d.state = 99;
@@ -219,9 +221,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
"SyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
parser<isRequest, Body, Headers> p;
bool started = false;
for(;;)
{
auto used =
@@ -229,6 +230,8 @@ read(SyncReadStream& stream, Streambuf& streambuf,
if(ec)
return;
streambuf.consume(used);
if(used > 0)
started = true;
if(p.complete())
{
m = p.release();
@@ -241,7 +244,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
return;
if(ec == boost::asio::error::eof)
{
if(! p.started())
if(! started)
return;
// Caller will see eof on next read.
ec = {};
@@ -268,8 +271,6 @@ async_read(AsyncReadStream& stream, Streambuf& streambuf,
"AsyncReadStream requirements not met");
static_assert(is_Streambuf<Streambuf>::value,
"Streambuf requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
beast::async_completion<ReadHandler,
void(error_code)> completion(handler);
detail::read_op<AsyncReadStream, Streambuf,

View File

@@ -10,8 +10,6 @@
#include <beast/http/chunk_encode.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/http/type_check.hpp>
#include <beast/http/detail/writes.hpp>
#include <beast/http/detail/write_preparation.hpp>
#include <beast/buffer_cat.hpp>
#include <beast/bind_handler.hpp>
@@ -265,9 +263,9 @@ operator()(error_code ec, std::size_t, bool again)
case 4:
// VFALCO Unfortunately the current interface to the
// Writer concept prevents us from using coalescing 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,
@@ -359,8 +357,6 @@ write(SyncWriteStream& stream,
message<isRequest, Body, Headers> const& msg,
boost::system::error_code& ec)
{
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec);
if(ec)
@@ -447,8 +443,6 @@ async_write(AsyncWriteStream& stream,
static_assert(
is_AsyncWriteStream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value,
"WritableBody requirements not met");
beast::async_completion<WriteHandler,
void(error_code)> completion(handler);
detail::write_op<AsyncWriteStream, decltype(completion.handler),

View File

@@ -35,6 +35,8 @@ struct response_fields
} // detail
#if ! GENERATING_DOCS
struct request_params
{
http::method_t method;
@@ -49,6 +51,8 @@ struct response_params
int version;
};
#endif
/** A HTTP message.
A message can be a request or response, depending on the `isRequest`
@@ -110,7 +114,7 @@ struct message
/// Diagnostics only
template<bool, class, class>
friend
friend
std::ostream&
operator<<(std::ostream& os,
message const& m);

View File

@@ -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

View File

@@ -13,43 +13,51 @@
#include <beast/http/message.hpp>
#include <boost/optional.hpp>
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
namespace beast {
namespace http {
/** A HTTP parser.
namespace detail {
struct parser_request
{
std::string method_;
std::string uri_;
};
struct parser_response
{
std::string reason_;
};
} // detail
The parser may only be used once.
*/
template<bool isRequest, class Body, class Headers>
class parser
: public basic_parser<parser<isRequest, Body, Headers>>
: 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_;
bool started_ = false;
public:
parser(parser&&) = default;
parser()
: http::basic_parser<parser>(isRequest)
, r_(m_)
: r_(m_)
{
}
/// Returns `true` if at least one byte has been processed
bool
started()
{
return started_;
}
message_type
release()
{
@@ -57,94 +65,165 @@ public:
}
private:
friend class http::basic_parser<parser>;
friend class basic_parser<isRequest, parser>;
void
on_start()
void flush()
{
started_ = true;
if(! value_.empty())
{
rfc2616::trim_right_in_place(value_);
// VFALCO could std::move
m_.headers.insert(field_, value_);
field_.clear();
value_.clear();
}
}
void
on_field(std::string const& field, std::string const& value)
void on_method(boost::string_ref const& s, error_code&)
{
m_.headers.insert(field, value);
this->method_.append(s.data(), s.size());
}
void
on_headers_complete(error_code&)
void on_uri(boost::string_ref const& s, error_code&)
{
// vFALCO TODO Decode the Content-Length and
// Transfer-Encoding, see if we can reserve the buffer.
//
// r_.reserve(content_length)
this->uri_.append(s.data(), s.size());
}
bool
on_request(http::method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
void on_reason(boost::string_ref const& s, error_code&)
{
m_.method = method;
m_.url = url;
m_.version = major * 10 + minor;
return true;
this->reason_.append(s.data(), s.size());
}
bool
on_request(http::method_t, std::string const&,
int, int, bool, bool,
std::false_type)
void on_field(boost::string_ref const& s, error_code&)
{
return true;
flush();
field_.append(s.data(), s.size());
}
bool
on_request(http::method_t method, std::string const& url,
int major, int minor, bool keep_alive, bool upgrade)
void on_value(boost::string_ref const& s, error_code&)
{
return on_request(method, url,
major, minor, keep_alive, upgrade,
typename message_type::is_request{});
value_.append(s.data(), s.size());
}
bool
on_response(int status, std::string const& reason,
int major, int minor, bool keep_alive, bool upgrade,
std::true_type)
void set(std::true_type)
{
m_.status = status;
m_.reason = reason;
m_.version = major * 10 + minor;
// VFALCO TODO return expect_body_
return true;
}
bool
on_response(int, std::string const&, int, int, bool, bool,
std::false_type)
{
return true;
// 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_);
}
bool
on_response(int status, std::string const& reason,
int major, int minor, bool keep_alive, bool upgrade)
void set(std::false_type)
{
return on_response(
status, reason, major, minor, keep_alive, upgrade,
std::integral_constant<bool, ! message_type::is_request::value>{});
m_.status = this->status_code();
m_.reason = this->reason_;
}
void
on_body(void const* data,
std::size_t size, error_code& ec)
int on_headers(error_code&)
{
r_.write(data, size, ec);
flush();
m_.version = 10 * this->http_major() + this->http_minor();
return 0;
}
void
on_complete()
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&)
{
}
};

View File

@@ -11,7 +11,6 @@
#include <beast/async_completion.hpp>
#include <beast/http/error.hpp>
#include <beast/http/parser.hpp>
#include <beast/http/type_check.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/system/error_code.hpp>

View File

@@ -21,6 +21,8 @@
namespace beast {
#if ! GENERATING_DOCS
/** Routines for performing RFC2616 compliance.
RFC2616:
Hypertext Transfer Protocol -- HTTP/1.1
@@ -454,6 +456,8 @@ token_in_list(boost::string_ref const& value,
} // rfc2616
#endif
} // beast
#endif

View File

@@ -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

View File

@@ -23,8 +23,13 @@ namespace http {
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_;

View File

@@ -23,8 +23,13 @@ namespace http {
*/
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_;

View File

@@ -1,49 +0,0 @@
//
// 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_TYPE_CHECK_HPP
#define BEAST_HTTP_TYPE_CHECK_HPP
#include <type_traits>
namespace beast {
namespace http {
/// Evaluates to std::true_type if `T` models Body
template<class T>
struct is_Body : std::true_type
{
};
/// Evalulates to std::true_type if Body has a reader
template<class T>
struct is_ReadableBody : std::true_type
{
};
/// Evalulates to std::true_type if Body has a writer
template<class T>
struct is_WritableBody : std::true_type
{
};
/// Evaluates to std::true_type if `T` models HTTPMessage
template<class T>
struct is_HTTPMessage : std::false_type
{
};
/// Evaluates to std::true_type if `HTTPMessage` is a request
template<class HTTPMessage>
struct is_HTTPRequest : std::true_type
{
};
} // http
} // beast
#endif

View File

@@ -115,6 +115,59 @@ public:
}
};
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>
@@ -798,16 +851,16 @@ basic_streambuf<Allocator>::debug_check() const
template<class Alloc, class T>
basic_streambuf<Alloc>&
operator<<(basic_streambuf<Alloc>& buf, T const& t)
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();
buf.commit(buffer_copy(buf.prepare(s.size()),
boost::asio::buffer(s)));
return buf;
streambuf.commit(buffer_copy(
streambuf.prepare(s.size()), buffer(s)));
return streambuf;
}
//------------------------------------------------------------------------------

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -85,8 +85,15 @@ public:
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&&);
@@ -120,190 +127,9 @@ public:
private:
void
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_;
}
setup(std::size_t n);
};
/// A bidirectional iterator type that may be used to read elements.
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};
}
//------------------------------------------------------------------------------
/** Return a trimmed, wrapped buffer sequence.
@@ -321,13 +147,11 @@ prepared_buffers<BufferSequence>::end() const ->
will be made, but ownership of the underlying memory is not transferred.
*/
template<class BufferSequence>
inline
prepared_buffers<BufferSequence>
prepare_buffers(std::size_t n, BufferSequence const& buffers)
{
return prepared_buffers<BufferSequence>(n, buffers);
}
prepare_buffers(std::size_t n, BufferSequence const& buffers);
} // beast
#include <beast/impl/prepare_buffers.ipp>
#endif

View File

@@ -8,12 +8,10 @@
#ifndef BEAST_STATIC_STREAMBUF_HPP
#define BEAST_STATIC_STREAMBUF_HPP
#include <boost/asio/buffer.hpp>
#include <boost/utility/base_from_member.hpp>
#include <algorithm>
#include <array>
#include <cstring>
#include <iterator>
#include <stdexcept>
namespace beast {
@@ -40,18 +38,23 @@ protected:
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;
#if GENERATING_DOCS
private:
#endif
static_streambuf(
static_streambuf const& other) noexcept = delete;
static_streambuf& operator=(
static_streambuf const&) noexcept = delete;
#if GENERATING_DOCS
public:
#endif
/// Returns the largest size output sequence possible.
@@ -116,293 +119,6 @@ protected:
//------------------------------------------------------------------------------
/// The type used to represent the input sequence as a list of buffers.
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_};
}
//------------------------------------------------------------------------------
/// The type used to represent the output sequence as a list of buffers.
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_)};
}
//------------------------------------------------------------------------------
/** A `Streambuf` with a fixed size internal buffer.
@tparam N The number of bytes in the internal buffer.
@@ -456,4 +172,6 @@ public:
} // beast
#include <beast/impl/static_streambuf.ipp>
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -10,6 +10,7 @@
#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>
@@ -244,8 +245,13 @@ read_fh2(frame_header& fh, Streambuf& sb,
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)
{
@@ -259,8 +265,13 @@ read_fh2(frame_header& fh, Streambuf& sb,
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)
{
@@ -275,8 +286,13 @@ read_fh2(frame_header& fh, Streambuf& sb,
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;
}
@@ -327,9 +343,15 @@ read(close_reason& cr,
{
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))

View File

@@ -68,10 +68,9 @@ class invokable
void operator()(){}
};
using buf_type = std::uint8_t[
sizeof(holder<exemplar>)];
using buf_type = char[sizeof(holder<exemplar>)];
bool b_ = false;
base* base_ = nullptr;
alignas(holder<exemplar>) buf_type buf_;
public:
@@ -81,7 +80,7 @@ public:
// Engaged invokables must be invoked before
// destruction otherwise the io_service
// invariants are broken w.r.t completions.
assert(! b_);
assert(! base_);
}
#endif
@@ -90,12 +89,12 @@ public:
invokable& operator=(invokable const&) = delete;
invokable(invokable&& other)
: b_(other.b_)
{
if(other.b_)
if(other.base_)
{
other.get().move(buf_);
other.b_ = false;
base_ = reinterpret_cast<base*>(&buf_[0]);
other.base_->move(buf_);
other.base_ = nullptr;
}
}
@@ -105,13 +104,13 @@ public:
// Engaged invokables must be invoked before
// assignment otherwise the io_service
// invariants are broken w.r.t completions.
assert(! b_);
assert(! base_);
if(other.b_)
if(other.base_)
{
b_ = true;
other.get().move(buf_);
other.b_ = false;
base_ = reinterpret_cast<base*>(&buf_[0]);
other.base_->move(buf_);
other.base_ = nullptr;
}
return *this;
}
@@ -123,19 +122,13 @@ public:
void
maybe_invoke()
{
if(b_)
if(base_)
{
b_ = false;
get()();
auto const basep = base_;
base_ = nullptr;
(*basep)();
}
}
private:
base&
get()
{
return *reinterpret_cast<base*>(&buf_[0]);
}
};
template<class F>
@@ -144,9 +137,9 @@ invokable::emplace(F&& f)
{
static_assert(sizeof(buf_type) >= sizeof(holder<F>),
"buffer too small");
assert(! b_);
assert(! base_);
::new(buf_) holder<F>(std::forward<F>(f));
b_ = true;
base_ = reinterpret_cast<base*>(&buf_[0]);
}
} // detail

View File

@@ -13,6 +13,7 @@
namespace beast {
namespace websocket {
/// The type of error used by functions and completion handlers.
using error_code = boost::system::error_code;
/// Error values

View File

@@ -0,0 +1,63 @@
//
// 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_WRITE_STREAMBUF_HPP
#define BEAST_WRITE_STREAMBUF_HPP
#include <beast/type_check.hpp>
#include <beast/detail/write_streambuf.hpp>
#include <type_traits>
#include <utility>
namespace beast {
/** Write to a Streambuf.
This function appends the serialized representation of each provided
argument into the stream buffer. It is capable of converting the
following types of arguments:
@li `boost::asio::const_buffer`
@li `boost::asio::mutable_buffer`
@li A type for which the call to `boost::asio::buffer()` is defined
@li A type meeting the requirements of `ConstBufferSequence`
@li A type meeting the requirements of `MutableBufferSequence`
For all types not listed above, the function will invoke
`boost::lexical_cast` on the argument in an attempt to convert to
a string, which is then appended to the stream buffer.
When this function serializes numbers, it converts them to
their text representation as if by a call to `std::to_string`.
@param streambuf The stream buffer to write to.
@param args A list of one or more arguments to write.
@throws unspecified Any exceptions thrown by `boost::lexical_cast`.
@note This function participates in overload resolution only if
the `streambuf` parameter meets the requirements of Streambuf.
*/
template<class Streambuf, class... Args>
#if GENERATING_DOCS
void
#else
typename std::enable_if<is_Streambuf<Streambuf>::value>::type
#endif
write(Streambuf& streambuf, Args&&... args)
{
detail::write_streambuf(streambuf, std::forward<Args>(args)...);
}
} // beast
#endif