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
* Parser callbacks may not throw
* Fix Reader concept doc typo
* Add is_Reader trait
API Changes:
* Added init() to Reader requirements
* Reader must be nothrow constructible
* 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">
<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_ReadableBody">is_ReadableBody</link></member>
<member><link linkend="beast.ref.http__is_WritableBody">is_WritableBody</link></member>
<member><link linkend="beast.ref.http__is_Reader">is_Reader</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>
</entry>
<entry valign="top">

View File

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

View File

@ -58,14 +58,15 @@ struct file_body
}
std::uint64_t
content_length() const
content_length() const noexcept
{
return size_;
}
template<class Write>
template<class WriteFunction>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
write(resume_context&&, error_code&,
WriteFunction&& wf) noexcept
{
if(size_ - offset_ < sizeof(buf_))
buf_len_ = static_cast<std::size_t>(
@ -75,7 +76,7 @@ struct file_body
auto const nread = fread(buf_, 1, sizeof(buf_), file_);
(void)nread;
offset_ += buf_len_;
write(boost::asio::buffer(buf_, buf_len_));
wf(boost::asio::buffer(buf_, buf_len_));
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_;
public:
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
explicit
writer(message<
isRequest, basic_dynabuf_body, Headers> const& m)
isRequest, basic_dynabuf_body, Headers> const& m) noexcept
: body_(m.body)
{
}
void
init(error_code& ec)
init(error_code& ec) noexcept
{
}
std::uint64_t
content_length() const
content_length() const noexcept
{
return body_.size();
}
template<class Write>
template<class WriteFunction>
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;
}
};

View File

@ -9,7 +9,10 @@
#define BEAST_HTTP_TYPE_CHECK_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/logic/tribool.hpp>
#include <type_traits>
#include <utility>
@ -18,63 +21,107 @@ namespace http {
namespace detail {
template<class T>
class has_value_type
struct write_function
{
template<class U, class R =
typename U::value_type>
static std::true_type check(int);
template<class ConstBufferSequence>
void
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>
static std::false_type check(...);
using type = decltype(check<T>(0));
public:
static bool constexpr value = type::value;
};
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class T, bool B = has_value_type<T>::value>
struct extract_value_type
{
using type = void;
};
template<class T>
struct extract_value_type<T, true>
{
using type = typename T::value_type;
};
template<class T>
class has_reader
{
// VFALCO This is unfortunate, we have to provide the template
// argument type because this is not a deduced context?
//
template<class U, class R =
typename U::reader>
static std::true_type check(int);
std::is_convertible<decltype(
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>
static std::false_type check(...);
public:
using type = decltype(check<T>(0));
};
static std::false_type check2(...);
using type2 = decltype(check2<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:
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,
has_value_type<T>::value
std::is_nothrow_constructible<T, M const&>::value
&& type1::value
&& type2::value
>;
};
#endif
template<class T>
class is_Parser
{
@ -116,31 +163,97 @@ public:
} // detail
/// Determine if `T` meets the requirements of `Body`.
/// Determine if `T` meets the requirements of @b Body.
template<class T>
#if GENERATING_DOCS
struct is_Body : std::integral_constant<bool, ...>{};
#else
using is_Body = typename detail::is_Body<T>::type;
using is_Body = detail::has_value_type<T>;
#endif
/// Determine if `T` meets the requirements of `ReadableBody`.
template<class T>
/** Determine if a @ref Body has a nested type `reader`.
@tparam T The type to check, which must meet the
requirements of @b Body.
*/
#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>
#if GENERATING_DOCS
struct is_WritableBody : std::integral_constant<bool, ...>{};
struct has_reader : std::integral_constant<bool, ...>{};
#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
/// 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>
#if GENERATING_DOCS
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
{
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
explicit
writer(message<isRequest, empty_body, Headers> const& m)
writer(message<isRequest, empty_body, Headers> const& m) noexcept
{
}
void
init(error_code& ec)
init(error_code& ec) noexcept
{
}
std::uint64_t
content_length() const
content_length() const noexcept
{
return 0;
}
template<class Write>
template<class WriteFunction>
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;
}
};

View File

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

View File

@ -150,8 +150,13 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
"SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
static_assert(is_Body<Body>::value,
"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;
beast::http::read(stream, dynabuf, msg, ec);
if(ec)
@ -169,8 +174,13 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
"SyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
static_assert(is_Body<Body>::value,
"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;
beast::http::parse(stream, dynabuf, p, ec);
if(ec)
@ -192,8 +202,13 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
"AsyncReadStream requirements not met");
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_ReadableBody<Body>::value,
"ReadableBody requirements not met");
static_assert(is_Body<Body>::value,
"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,
void(error_code)> completion(handler);
detail::read_op<AsyncReadStream, DynamicBuffer,

View File

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

View File

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

View File

@ -62,33 +62,31 @@ private:
value_type const& body_;
public:
writer(writer const&) = delete;
writer& operator=(writer const&) = delete;
template<bool isRequest, class Headers>
explicit
writer(message<
isRequest, string_body, Headers> const& msg)
isRequest, string_body, Headers> const& msg) noexcept
: body_(msg.body)
{
}
void
init(error_code& ec)
init(error_code& ec) noexcept
{
}
std::uint64_t
content_length() const
content_length() const noexcept
{
return body_.size();
}
template<class Write>
template<class WriteFunction>
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;
}
};

View File

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