Rework HTTP concepts (API Change):

fix #139, fix #140

* Writer uses write instead of operator()
* Refactor traits to use void_t
* Remove is_ReadableBody, is_WritableBody
* Add has_reader, has_writer, is_Reader, is_Writer
* More friendly compile errors on failed concept checks
This commit is contained in:
Vinnie Falco
2016-10-15 13:40:17 -04:00
parent f110e51dd1
commit 056d6b94c2
15 changed files with 333 additions and 186 deletions

View File

@ -6,13 +6,20 @@
* Add basic_streambuf::alloc_size * Add basic_streambuf::alloc_size
* Parser callbacks may not throw * Parser callbacks may not throw
* Fix Reader concept doc typo * Fix Reader concept doc typo
* Add is_Reader trait
API Changes: API Changes:
* Added init() to Reader requirements * Added init() to Reader requirements
* Reader must be nothrow constructible * Reader must be nothrow constructible
* Reader is now constructed right before reading the body * Reader is now constructed right before reading the body
- The message passed on construction is filled in - The message passed on construction is filled in
* Rework HTTP concepts:
- Writer uses write instead of operator()
- Refactor traits to use void_t
- Remove is_ReadableBody, is_WritableBody
- Add has_reader, has_writer, is_Reader, is_Writer
- More friendly compile errors on failed concept checks
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -50,8 +50,10 @@
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_Body">is_Body</link></member> <member><link linkend="beast.ref.http__is_Body">is_Body</link></member>
<member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member> <member><link linkend="beast.ref.http__is_Parser">is_Parser</link></member>
<member><link linkend="beast.ref.http__is_ReadableBody">is_ReadableBody</link></member> <member><link linkend="beast.ref.http__is_Reader">is_Reader</link></member>
<member><link linkend="beast.ref.http__is_WritableBody">is_WritableBody</link></member> <member><link linkend="beast.ref.http__is_Writer">is_Reader</link></member>
<member><link linkend="beast.ref.http__has_reader">has_reader</link></member>
<member><link linkend="beast.ref.http__has_writer">has_writer</link></member>
</simplelist> </simplelist>
</entry> </entry>
<entry valign="top"> <entry valign="top">

View File

@ -72,7 +72,7 @@ In this table:
] ]
] ]
[ [
[`a(rc, ec, wf)`] [`a.write(rc, ec, wf)`]
[`boost::tribool`] [`boost::tribool`]
[ [
Called repeatedly after `init` succeeds. `wf` is a function object Called repeatedly after `init` succeeds. `wf` is a function object
@ -175,10 +175,10 @@ public:
*/ */
template<class WriteFunction> template<class WriteFunction>
boost::tribool boost::tribool
operator()( write(
resume_context&&, resume_context&&,
error_code&, error_code&,
WriteFunction&& write) noexcept; WriteFunction&& wf) noexcept;
}; };
``` ```

View File

@ -58,14 +58,15 @@ struct file_body
} }
std::uint64_t std::uint64_t
content_length() const content_length() const noexcept
{ {
return size_; return size_;
} }
template<class Write> template<class WriteFunction>
boost::tribool boost::tribool
operator()(resume_context&&, error_code&, Write&& write) write(resume_context&&, error_code&,
WriteFunction&& wf) noexcept
{ {
if(size_ - offset_ < sizeof(buf_)) if(size_ - offset_ < sizeof(buf_))
buf_len_ = static_cast<std::size_t>( buf_len_ = static_cast<std::size_t>(
@ -75,7 +76,7 @@ struct file_body
auto const nread = fread(buf_, 1, sizeof(buf_), file_); auto const nread = fread(buf_, 1, sizeof(buf_), file_);
(void)nread; (void)nread;
offset_ += buf_len_; offset_ += buf_len_;
write(boost::asio::buffer(buf_, buf_len_)); wf(boost::asio::buffer(buf_, buf_len_));
return offset_ >= size_; return offset_ >= size_;
} }
}; };

View File

@ -0,0 +1,26 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_VOID_T_HPP
#define BEAST_DETAIL_VOID_T_HPP
namespace beast {
namespace detail {
template<class... Ts>
struct make_void
{
using type = void;
};
template<class... Ts>
using void_t = typename make_void<Ts...>::type;
} // detail
} // beast
#endif

View File

@ -62,33 +62,31 @@ private:
DynamicBuffer const& body_; DynamicBuffer const& body_;
public: public:
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers> template<bool isRequest, class Headers>
explicit explicit
writer(message< writer(message<
isRequest, basic_dynabuf_body, Headers> const& m) isRequest, basic_dynabuf_body, Headers> const& m) noexcept
: body_(m.body) : body_(m.body)
{ {
} }
void void
init(error_code& ec) init(error_code& ec) noexcept
{ {
} }
std::uint64_t std::uint64_t
content_length() const content_length() const noexcept
{ {
return body_.size(); return body_.size();
} }
template<class Write> template<class WriteFunction>
boost::tribool boost::tribool
operator()(resume_context&&, error_code&, Write&& write) write(resume_context&&, error_code&,
WriteFunction&& wf) noexcept
{ {
write(body_.data()); wf(body_.data());
return true; return true;
} }
}; };

View File

@ -9,7 +9,10 @@
#define BEAST_HTTP_TYPE_CHECK_HPP #define BEAST_HTTP_TYPE_CHECK_HPP
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <beast/http/resume_context.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -18,63 +21,107 @@ namespace http {
namespace detail { namespace detail {
template<class T> struct write_function
class has_value_type
{ {
template<class U, class R = template<class ConstBufferSequence>
typename U::value_type> void
static std::true_type check(int); operator()(ConstBufferSequence const&);
};
template<class T, class = beast::detail::void_t<>>
struct has_value_type : std::false_type {};
template<class T>
struct has_value_type<T, beast::detail::void_t<
typename T::value_type
> > : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct has_content_length : std::false_type {};
template<class T>
struct has_content_length<T, beast::detail::void_t<decltype(
std::declval<T>().content_length()
)> > : std::true_type
{
static_assert(std::is_convertible<
decltype(std::declval<T>().content_length()),
std::uint64_t>::value,
"Writer::content_length requirements not met");
};
#if 0
template<class T, class M, class = beast::detail::void_t<>>
struct is_Writer : std::false_type {};
template<class T, class M>
struct is_Writer<T, M, beast::detail::void_t<decltype(
std::declval<T>().init(
std::declval<error_code&>())
// VFALCO This is unfortunate, we have to provide the template
// argument type because this is not a deduced context?
//
,std::declval<T>().template write<detail::write_function>(
std::declval<resume_context>(),
std::declval<error_code&>(),
std::declval<detail::write_function>())
)> > : std::integral_constant<bool,
std::is_nothrow_constructible<T, M const&>::value &&
std::is_convertible<decltype(
std::declval<T>().template write<detail::write_function>(
std::declval<resume_context>(),
std::declval<error_code&>(),
std::declval<detail::write_function>())),
boost::tribool>::value
>
{
static_assert(std::is_same<
typename M::body_type::writer, T>::value,
"Mismatched writer and message");
};
#else
template<class T, class M>
class is_Writer
{
template<class U, class R = decltype(
std::declval<U>().init(std::declval<error_code&>()),
std::true_type{})>
static R check1(int);
template<class> template<class>
static std::false_type check(...); static std::false_type check1(...);
using type = decltype(check<T>(0)); using type1 = decltype(check1<T>(0));
public:
static bool constexpr value = type::value;
};
template<class T, bool B = has_value_type<T>::value> // VFALCO This is unfortunate, we have to provide the template
struct extract_value_type // argument type because this is not a deduced context?
{ //
using type = void;
};
template<class T>
struct extract_value_type<T, true>
{
using type = typename T::value_type;
};
template<class T>
class has_reader
{
template<class U, class R = template<class U, class R =
typename U::reader> std::is_convertible<decltype(
static std::true_type check(int); std::declval<U>().template write<detail::write_function>(
std::declval<resume_context>(),
std::declval<error_code&>(),
std::declval<detail::write_function>()))
, boost::tribool>>
static R check2(int);
template<class> template<class>
static std::false_type check(...); static std::false_type check2(...);
public: using type2 = decltype(check2<T>(0));
using type = decltype(check<T>(0));
};
template<class T>
class has_writer
{
template<class U, class R =
typename U::writer>
static std::true_type check(int);
template<class>
static std::false_type check(...);
public: public:
using type = decltype(check<T>(0)); static_assert(std::is_same<
}; typename M::body_type::writer, T>::value,
"Mismatched writer and message");
template<class T>
struct is_Body
{
using type = std::integral_constant<bool, using type = std::integral_constant<bool,
has_value_type<T>::value std::is_nothrow_constructible<T, M const&>::value
&& type1::value
&& type2::value
>; >;
}; };
#endif
template<class T> template<class T>
class is_Parser class is_Parser
{ {
@ -116,31 +163,97 @@ public:
} // detail } // detail
/// Determine if `T` meets the requirements of `Body`. /// Determine if `T` meets the requirements of @b Body.
template<class T> template<class T>
#if GENERATING_DOCS #if GENERATING_DOCS
struct is_Body : std::integral_constant<bool, ...>{}; struct is_Body : std::integral_constant<bool, ...>{};
#else #else
using is_Body = typename detail::is_Body<T>::type; using is_Body = detail::has_value_type<T>;
#endif #endif
/// Determine if `T` meets the requirements of `ReadableBody`. /** Determine if a @ref Body has a nested type `reader`.
template<class T>
@tparam T The type to check, which must meet the
requirements of @b Body.
*/
#if GENERATING_DOCS #if GENERATING_DOCS
struct is_ReadableBody : std::integral_constant<bool, ...>{};
#else
using is_ReadableBody = typename detail::has_reader<T>::type;
#endif
/// Determine if `T` meets the requirements of `WritableBody`.
template<class T> template<class T>
#if GENERATING_DOCS struct has_reader : std::integral_constant<bool, ...>{};
struct is_WritableBody : std::integral_constant<bool, ...>{};
#else #else
using is_WritableBody = typename detail::has_writer<T>::type; template<class T, class = beast::detail::void_t<>>
struct has_reader : std::false_type {};
template<class T>
struct has_reader<T, beast::detail::void_t<
typename T::reader
> > : std::true_type {};
#endif #endif
/// Determine if `T` meets the requirements of `Parser`. /** Determine if a @ref Body has a nested type `writer`.
@tparam T The type to check, which must meet the
requirements of @b Body.
*/
#if GENERATING_DOCS
template<class T>
struct has_writer : std::integral_constant<bool, ...>{};
#else
template<class T, class = beast::detail::void_t<>>
struct has_writer : std::false_type {};
template<class T>
struct has_writer<T, beast::detail::void_t<
typename T::writer
> > : std::true_type {};
#endif
/** Determine if `T` meets the requirements of @b Reader for `M`.
@tparam T The type to test.
@tparam M The message type to test with, which must be of
type `message`.
*/
#if GENERATING_DOCS
template<class T, class M>
struct is_Reader : std::integral_constant<bool, ...> {};
#else
template<class T, class M, class = beast::detail::void_t<>>
struct is_Reader : std::false_type {};
template<class T, class M>
struct is_Reader<T, M, beast::detail::void_t<decltype(
std::declval<T>().init(
std::declval<error_code&>()),
std::declval<T>().write(
std::declval<void const*>(),
std::declval<std::size_t>(),
std::declval<error_code&>())
)> > : std::integral_constant<bool,
std::is_nothrow_constructible<T, M&>::value
>
{
static_assert(std::is_same<
typename M::body_type::reader, T>::value,
"Mismatched reader and message");
};
#endif
/** Determine if `T` meets the requirements of @b Writer for `M`.
@tparam T The type to test.
@tparam M The message type to test with, which must be of
type `message`.
*/
template<class T, class M>
#if GENERATING_DOCS
struct is_Writer : std::integral_constant<bool, ...> {};
#else
using is_Writer = typename detail::is_Writer<T, M>::type;
#endif
/// Determine if `T` meets the requirements of @b Parser.
template<class T> template<class T>
#if GENERATING_DOCS #if GENERATING_DOCS
struct is_Parser : std::integral_constant<bool, ...>{}; struct is_Parser : std::integral_constant<bool, ...>{};

View File

@ -1,43 +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_HAS_CONTENT_LENGTH_HPP
#define BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP
#include <cstdint>
#include <type_traits>
namespace beast {
namespace http {
namespace detail {
template<class T>
class has_content_length_value
{
template<class U, class R = typename std::is_convertible<
decltype(std::declval<U>().content_length()),
std::uint64_t>>
static R check(int);
template<class>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
// `true` if `T` meets the requirements.
static bool const value = type::value;
};
// Determines if the writer can provide the content length
template<class T>
using has_content_length =
std::integral_constant<bool,
has_content_length_value<T>::value>;
} // detail
} // http
} // beast
#endif

View File

@ -35,31 +35,29 @@ private:
struct writer struct writer
{ {
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers> template<bool isRequest, class Headers>
explicit explicit
writer(message<isRequest, empty_body, Headers> const& m) writer(message<isRequest, empty_body, Headers> const& m) noexcept
{ {
} }
void void
init(error_code& ec) init(error_code& ec) noexcept
{ {
} }
std::uint64_t std::uint64_t
content_length() const content_length() const noexcept
{ {
return 0; return 0;
} }
template<class Write> template<class WriteFunction>
boost::tribool boost::tribool
operator()(resume_context&&, error_code&, Write&& write) write(resume_context&&, error_code&,
WriteFunction&& wf) noexcept
{ {
write(boost::asio::null_buffers{}); wf(boost::asio::null_buffers{});
return true; return true;
} }
}; };

View File

@ -9,9 +9,9 @@
#define BEAST_HTTP_IMPL_MESSAGE_V1_IPP #define BEAST_HTTP_IMPL_MESSAGE_V1_IPP
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/http/concepts.hpp>
#include <beast/http/rfc7230.hpp> #include <beast/http/rfc7230.hpp>
#include <beast/core/detail/ci_char_traits.hpp> #include <beast/core/detail/ci_char_traits.hpp>
#include <beast/http/detail/has_content_length.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <stdexcept> #include <stdexcept>
@ -116,8 +116,13 @@ prepare(message_v1<isRequest, Body, Headers>& msg,
Options&&... options) Options&&... options)
{ {
// VFALCO TODO // VFALCO TODO
//static_assert(is_WritableBody<Body>::value, static_assert(is_Body<Body>::value,
// "WritableBody requirements not met"); "Body requirements not met");
static_assert(has_writer<Body>::value,
"Body has no writer");
static_assert(is_Writer<typename Body::writer,
message_v1<isRequest, Body, Headers>>::value,
"Writer requirements not met");
detail::prepare_info pi; detail::prepare_info pi;
detail::prepare_content_length(pi, msg, detail::prepare_content_length(pi, msg,
detail::has_content_length<typename Body::writer>{}); detail::has_content_length<typename Body::writer>{});

View File

@ -150,8 +150,13 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value, static_assert(is_Body<Body>::value,
"ReadableBody requirements not met"); "Body requirements not met");
static_assert(has_reader<Body>::value,
"Body has no reader");
static_assert(is_Reader<typename Body::reader,
message_v1<isRequest, Body, Headers>>::value,
"Reader requirements not met");
error_code ec; error_code ec;
beast::http::read(stream, dynabuf, msg, ec); beast::http::read(stream, dynabuf, msg, ec);
if(ec) if(ec)
@ -169,8 +174,13 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
"SyncReadStream requirements not met"); "SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value, static_assert(is_Body<Body>::value,
"ReadableBody requirements not met"); "Body requirements not met");
static_assert(has_reader<Body>::value,
"Body has no reader");
static_assert(is_Reader<typename Body::reader,
message_v1<isRequest, Body, Headers>>::value,
"Reader requirements not met");
parser_v1<isRequest, Body, Headers> p; parser_v1<isRequest, Body, Headers> p;
beast::http::parse(stream, dynabuf, p, ec); beast::http::parse(stream, dynabuf, p, ec);
if(ec) if(ec)
@ -192,8 +202,13 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
"AsyncReadStream requirements not met"); "AsyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value, static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met"); "DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value, static_assert(is_Body<Body>::value,
"ReadableBody requirements not met"); "Body requirements not met");
static_assert(has_reader<Body>::value,
"Body has no reader");
static_assert(is_Reader<typename Body::reader,
message_v1<isRequest, Body, Headers>>::value,
"Reader requirements not met");
beast::async_completion<ReadHandler, beast::async_completion<ReadHandler,
void(error_code)> completion(handler); void(error_code)> completion(handler);
detail::read_op<AsyncReadStream, DynamicBuffer, detail::read_op<AsyncReadStream, DynamicBuffer,

View File

@ -11,7 +11,6 @@
#include <beast/http/concepts.hpp> #include <beast/http/concepts.hpp>
#include <beast/http/resume_context.hpp> #include <beast/http/resume_context.hpp>
#include <beast/http/detail/chunk_encode.hpp> #include <beast/http/detail/chunk_encode.hpp>
#include <beast/http/detail/has_content_length.hpp>
#include <beast/core/buffer_cat.hpp> #include <beast/core/buffer_cat.hpp>
#include <beast/core/bind_handler.hpp> #include <beast/core/bind_handler.hpp>
#include <beast/core/buffer_concepts.hpp> #include <beast/core/buffer_concepts.hpp>
@ -184,7 +183,7 @@ class write_op
} }
template<class ConstBufferSequence> template<class ConstBufferSequence>
void operator()(ConstBufferSequence const& buffers) void operator()(ConstBufferSequence const& buffers) const
{ {
auto& d = *self_.d_; auto& d = *self_.d_;
// write headers and body // write headers and body
@ -212,7 +211,7 @@ class write_op
} }
template<class ConstBufferSequence> template<class ConstBufferSequence>
void operator()(ConstBufferSequence const& buffers) void operator()(ConstBufferSequence const& buffers) const
{ {
auto& d = *self_.d_; auto& d = *self_.d_;
// write body // write body
@ -323,7 +322,7 @@ operator()(error_code ec, std::size_t, bool again)
case 1: case 1:
{ {
auto const result = d.wp.w( boost::tribool const result = d.wp.w.write(
std::move(d.copy), ec, writef0_lambda{*this}); std::move(d.copy), ec, writef0_lambda{*this});
if(ec) if(ec)
{ {
@ -354,7 +353,7 @@ operator()(error_code ec, std::size_t, bool again)
case 3: case 3:
{ {
auto const result = d.wp.w( boost::tribool result = d.wp.w.write(
std::move(d.copy), ec, writef_lambda{*this}); std::move(d.copy), ec, writef_lambda{*this});
if(ec) if(ec)
{ {
@ -420,7 +419,7 @@ public:
} }
template<class ConstBufferSequence> template<class ConstBufferSequence>
void operator()(ConstBufferSequence const& buffers) void operator()(ConstBufferSequence const& buffers) const
{ {
// write headers and body // write headers and body
if(chunked_) if(chunked_)
@ -449,7 +448,7 @@ public:
} }
template<class ConstBufferSequence> template<class ConstBufferSequence>
void operator()(ConstBufferSequence const& buffers) void operator()(ConstBufferSequence const& buffers) const
{ {
// write body // write body
if(chunked_) if(chunked_)
@ -472,8 +471,13 @@ write(SyncWriteStream& stream,
{ {
static_assert(is_SyncWriteStream<SyncWriteStream>::value, static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met"); "SyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value, static_assert(is_Body<Body>::value,
"WritableBody requirements not met"); "Body requirements not met");
static_assert(has_writer<Body>::value,
"Body has no writer");
static_assert(is_Writer<typename Body::writer,
message_v1<isRequest, Body, Headers>>::value,
"Writer requirements not met");
error_code ec; error_code ec;
write(stream, msg, ec); write(stream, msg, ec);
if(ec) if(ec)
@ -489,8 +493,13 @@ write(SyncWriteStream& stream,
{ {
static_assert(is_SyncWriteStream<SyncWriteStream>::value, static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met"); "SyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value, static_assert(is_Body<Body>::value,
"WritableBody requirements not met"); "Body requirements not met");
static_assert(has_writer<Body>::value,
"Body has no writer");
static_assert(is_Writer<typename Body::writer,
message_v1<isRequest, Body, Headers>>::value,
"Writer requirements not met");
detail::write_preparation<isRequest, Body, Headers> wp(msg); detail::write_preparation<isRequest, Body, Headers> wp(msg);
wp.init(ec); wp.init(ec);
if(ec) if(ec)
@ -506,9 +515,11 @@ write(SyncWriteStream& stream,
cv.notify_one(); cv.notify_one();
}}; }};
auto copy = resume; auto copy = resume;
boost::tribool result = wp.w(std::move(copy), boost::tribool result =
ec, detail::writef0_lambda<SyncWriteStream, wp.w.write(std::move(copy), ec,
decltype(wp.sb)>{stream, wp.sb, wp.chunked, ec}); detail::writef0_lambda<SyncWriteStream,
decltype(wp.sb)>{stream,
wp.sb, wp.chunked, ec});
if(ec) if(ec)
return; return;
if(boost::indeterminate(result)) if(boost::indeterminate(result))
@ -527,11 +538,16 @@ write(SyncWriteStream& stream,
wp.sb.consume(wp.sb.size()); wp.sb.consume(wp.sb.size());
if(! result) if(! result)
{ {
detail::writef_lambda<SyncWriteStream> wf{
stream, wp.chunked, ec};
for(;;) for(;;)
{ {
result = wp.w(std::move(copy), ec, #if 0
detail::writef_lambda<SyncWriteStream>{ result = wp.w.write(std::move(copy), ec, wf);
stream, wp.chunked, ec}); #else
result = wp.w.write(std::move(copy), ec,
detail::writef_lambda<SyncWriteStream>{stream, wp.chunked, ec});
#endif
if(ec) if(ec)
return; return;
if(result) if(result)
@ -573,8 +589,13 @@ async_write(AsyncWriteStream& stream,
{ {
static_assert(is_AsyncWriteStream<AsyncWriteStream>::value, static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met"); "AsyncWriteStream requirements not met");
static_assert(is_WritableBody<Body>::value, static_assert(is_Body<Body>::value,
"WritableBody requirements not met"); "Body requirements not met");
static_assert(has_writer<Body>::value,
"Body has no writer");
static_assert(is_Writer<typename Body::writer,
message_v1<isRequest, Body, Headers>>::value,
"Writer requirements not met");
beast::async_completion<WriteHandler, beast::async_completion<WriteHandler,
void(error_code)> completion(handler); void(error_code)> completion(handler);
detail::write_op<AsyncWriteStream, decltype(completion.handler), detail::write_op<AsyncWriteStream, decltype(completion.handler),
@ -636,8 +657,13 @@ std::ostream&
operator<<(std::ostream& os, operator<<(std::ostream& os,
message_v1<isRequest, Body, Headers> const& msg) message_v1<isRequest, Body, Headers> const& msg)
{ {
static_assert(is_WritableBody<Body>::value, static_assert(is_Body<Body>::value,
"WritableBody requirements not met"); "Body requirements not met");
static_assert(has_writer<Body>::value,
"Body has no writer");
static_assert(is_Writer<typename Body::writer,
message_v1<isRequest, Body, Headers>>::value,
"Writer requirements not met");
detail::ostream_SyncStream oss(os); detail::ostream_SyncStream oss(os);
error_code ec; error_code ec;
write(oss, msg, ec); write(oss, msg, ec);

View File

@ -91,13 +91,12 @@ private:
using reader = using reader =
typename message_type::body_type::reader; typename message_type::body_type::reader;
static_assert(is_ReadableBody<Body>::value, static_assert(is_Body<Body>::value,
"ReadableBody requirements not met"); "Body requirements not met");
static_assert(has_reader<Body>::value,
// Reader must be nothrow constructible "Body has no reader");
static_assert(std::is_nothrow_constructible< static_assert(is_Reader<reader, message_type>::value,
reader, message_type&>::value, "Reader requirements not met");
"Reader requirements not met");
std::string field_; std::string field_;
std::string value_; std::string value_;

View File

@ -62,33 +62,31 @@ private:
value_type const& body_; value_type const& body_;
public: public:
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers> template<bool isRequest, class Headers>
explicit explicit
writer(message< writer(message<
isRequest, string_body, Headers> const& msg) isRequest, string_body, Headers> const& msg) noexcept
: body_(msg.body) : body_(msg.body)
{ {
} }
void void
init(error_code& ec) init(error_code& ec) noexcept
{ {
} }
std::uint64_t std::uint64_t
content_length() const content_length() const noexcept
{ {
return body_.size(); return body_.size();
} }
template<class Write> template<class WriteFunction>
boost::tribool boost::tribool
operator()(resume_context&&, error_code&, Write&& write) write(resume_context&&, error_code&,
WriteFunction&& wf) noexcept
{ {
write(boost::asio::buffer(body_)); wf(boost::asio::buffer(body_));
return true; return true;
} }
}; };

View File

@ -104,21 +104,22 @@ public:
public: public:
template<bool isRequest, class Allocator> template<bool isRequest, class Allocator>
explicit explicit
writer(message<isRequest, unsized_body, Allocator> const& msg) writer(message<isRequest, unsized_body, Allocator> const& msg) noexcept
: body_(msg.body) : body_(msg.body)
{ {
} }
void void
init(error_code& ec) init(error_code& ec) noexcept
{ {
} }
template<class Write> template<class WriteFunction>
boost::tribool boost::tribool
operator()(resume_context&&, error_code&, Write&& write) write(resume_context&&, error_code&,
WriteFunction&& wf) noexcept
{ {
write(boost::asio::buffer(body_)); wf(boost::asio::buffer(body_));
return true; return true;
} }
}; };
@ -168,13 +169,13 @@ public:
public: public:
template<bool isRequest, class Allocator> template<bool isRequest, class Allocator>
explicit explicit
writer(message<isRequest, fail_body, Allocator> const& msg) writer(message<isRequest, fail_body, Allocator> const& msg) noexcept
: body_(msg.body) : body_(msg.body)
{ {
} }
void void
init(error_code& ec) init(error_code& ec) noexcept
{ {
body_.fc_.fail(ec); body_.fc_.fail(ec);
} }
@ -197,9 +198,10 @@ public:
} }
}; };
template<class Write> template<class WriteFunction>
boost::tribool boost::tribool
operator()(resume_context&& rc, error_code& ec, Write&& write) write(resume_context&& rc, error_code& ec,
WriteFunction&& wf) noexcept
{ {
if(body_.fc_.fail(ec)) if(body_.fc_.fail(ec))
return false; return false;
@ -211,7 +213,7 @@ public:
} }
if(n_ >= body_.s_.size()) if(n_ >= body_.s_.size())
return true; return true;
write(boost::asio::buffer(body_.s_.data() + n_, 1)); wf(boost::asio::buffer(body_.s_.data() + n_, 1));
++n_; ++n_;
return n_ == body_.s_.size(); return n_ == body_.s_.size();
} }