diff --git a/doc/quickref.xml b/doc/quickref.xml
index 1949e3d1..55b89655 100644
--- a/doc/quickref.xml
+++ b/doc/quickref.xml
@@ -61,6 +61,7 @@
async_read
async_read_some
async_write
+ async_write_some
is_keep_alive
is_upgrade
make_serializer
@@ -71,6 +72,7 @@
reason_string
swap
write
+ write_some
Type Traits
diff --git a/include/beast/http.hpp b/include/beast/http.hpp
index 5d67a23b..ce3044e8 100644
--- a/include/beast/http.hpp
+++ b/include/beast/http.hpp
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
diff --git a/include/beast/http/impl/serializer.ipp b/include/beast/http/impl/serializer.ipp
new file mode 100644
index 00000000..7b3600f4
--- /dev/null
+++ b/include/beast/http/impl/serializer.ipp
@@ -0,0 +1,407 @@
+//
+// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BEAST_HTTP_IMPL_SERIALIZER_IPP
+#define BEAST_HTTP_IMPL_SERIALIZER_IPP
+
+#include
+#include
+#include
+
+namespace beast {
+namespace http {
+namespace detail {
+
+template
+void
+write_start_line(std::ostream& os,
+ header const& msg)
+{
+ BOOST_ASSERT(msg.version == 10 || msg.version == 11);
+ os << msg.method() << " " << msg.target();
+ switch(msg.version)
+ {
+ case 10: os << " HTTP/1.0\r\n"; break;
+ case 11: os << " HTTP/1.1\r\n"; break;
+ }
+}
+
+template
+void
+write_start_line(std::ostream& os,
+ header const& msg)
+{
+ BOOST_ASSERT(msg.version == 10 || msg.version == 11);
+ switch(msg.version)
+ {
+ case 10: os << "HTTP/1.0 "; break;
+ case 11: os << "HTTP/1.1 "; break;
+ }
+ os << msg.status << " " << msg.reason() << "\r\n";
+}
+
+template
+void
+write_fields(std::ostream& os,
+ FieldSequence const& fields)
+{
+ //static_assert(is_FieldSequence::value,
+ // "FieldSequence requirements not met");
+ for(auto const& field : fields)
+ {
+ auto const name = field.name();
+ BOOST_ASSERT(! name.empty());
+ if(name[0] == ':')
+ continue;
+ os << field.name() << ": " << field.value() << "\r\n";
+ }
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+template
+serializer::
+serializer(message const& m,
+ Decorator const& d, Allocator const& alloc)
+ : m_(m)
+ , d_(d)
+ , b_(1024, alloc)
+ , chunked_(token_list{
+ m.fields["Transfer-Encoding"]}.exists("chunked"))
+ , close_(token_list{
+ m.fields["Connection"]}.exists("close") ||
+ (m.version < 11 && ! m.fields.exists(
+ "Content-Length")))
+{
+ s_ = chunked_ ? do_init_c : do_init;
+ // VFALCO Move this stuff to the switch?
+ auto os = ostream(b_);
+ detail::write_start_line(os, m_);
+ detail::write_fields(os, m_.fields);
+ os << "\r\n";
+}
+
+template
+template
+void
+serializer::
+get(error_code& ec, Visit&& visit)
+{
+ using boost::asio::buffer_size;
+ switch(s_)
+ {
+ case do_init:
+ {
+ if(split_)
+ goto go_header_only;
+ rd_.emplace(m_);
+ rd_->init(ec);
+ if(ec)
+ return;
+ auto result = rd_->get(ec);
+ if(ec)
+ {
+ // Can't use need_more when ! is_deferred
+ BOOST_ASSERT(ec != error::need_more);
+ return;
+ }
+ if(! result)
+ goto go_header_only;
+ more_ = result->second;
+ v_ = cb0_t{
+ boost::in_place_init,
+ b_.data(),
+ result->first};
+ s_ = do_header;
+ // [[fallthrough]]
+ }
+
+ case do_header:
+ visit(ec, boost::get(v_));
+ break;
+
+ go_header_only:
+ s_ = do_header_only;
+ case do_header_only:
+ visit(ec, b_.data());
+ break;
+
+ case do_body:
+ BOOST_ASSERT(! rd_);
+ rd_.emplace(m_);
+ rd_->init(ec);
+ if(ec)
+ return;
+ s_ = do_body + 1;
+ // [[fallthrough]]
+
+ case do_body + 1:
+ {
+ auto result = rd_->get(ec);
+ if(ec)
+ return;
+ if(! result)
+ goto go_complete;
+ more_ = result->second;
+ v_ = cb1_t{result->first};
+ s_ = do_body + 2;
+ // [[fallthrough]]
+ }
+
+ case do_body + 2:
+ visit(ec, boost::get(v_));
+ break;
+
+ //----------------------------------------------------------------------
+
+ case do_init_c:
+ {
+ if(split_)
+ goto go_header_only_c;
+ rd_.emplace(m_);
+ rd_->init(ec);
+ if(ec)
+ return;
+ auto result = rd_->get(ec);
+ if(ec)
+ {
+ // Can't use need_more when ! is_deferred
+ BOOST_ASSERT(ec != error::need_more);
+ return;
+ }
+ if(! result)
+ goto go_header_only_c;
+ more_ = result->second;
+ v_ = ch0_t{
+ boost::in_place_init,
+ b_.data(),
+ detail::chunk_header{
+ buffer_size(result->first)},
+ [&]()
+ {
+ auto sv = d_(result->first);
+ return boost::asio::const_buffers_1{
+ sv.data(), sv.size()};
+
+ }(),
+ result->first,
+ detail::chunk_crlf()};
+ s_ = do_header_c;
+ // [[fallthrough]]
+ }
+
+ case do_header_c:
+ visit(ec, boost::get(v_));
+ break;
+
+ go_header_only_c:
+ s_ = do_header_only_c;
+ case do_header_only_c:
+ visit(ec, b_.data());
+ break;
+
+ case do_body_c:
+ BOOST_ASSERT(! rd_);
+ rd_.emplace(m_);
+ rd_->init(ec);
+ if(ec)
+ return;
+ s_ = do_body_c + 1;
+ // [[fallthrough]]
+
+ case do_body_c + 1:
+ {
+ auto result = rd_->get(ec);
+ if(ec)
+ return;
+ if(! result)
+ goto go_final_c;
+ more_ = result->second;
+ v_ = ch1_t{
+ boost::in_place_init,
+ detail::chunk_header{
+ buffer_size(result->first)},
+ [&]()
+ {
+ auto sv = d_(result->first);
+ return boost::asio::const_buffers_1{
+ sv.data(), sv.size()};
+
+ }(),
+ result->first,
+ detail::chunk_crlf()};
+ s_ = do_body_c + 2;
+ // [[fallthrough]]
+ }
+
+ case do_body_c + 2:
+ visit(ec, boost::get(v_));
+ break;
+
+ go_final_c:
+ case do_final_c:
+ v_ = ch2_t{
+ boost::in_place_init,
+ detail::chunk_final(),
+ [&]()
+ {
+ auto sv = d_(
+ boost::asio::null_buffers{});
+ return boost::asio::const_buffers_1{
+ sv.data(), sv.size()};
+
+ }(),
+ detail::chunk_crlf()};
+ s_ = do_final_c + 1;
+ // [[fallthrough]]
+
+ case do_final_c + 1:
+ visit(ec, boost::get(v_));
+ break;
+
+ //----------------------------------------------------------------------
+
+ default:
+ case do_complete:
+ BOOST_ASSERT(false);
+ break;
+
+ go_complete:
+ s_ = do_complete;
+ break;
+ }
+}
+
+template
+void
+serializer::
+consume(std::size_t n)
+{
+ using boost::asio::buffer_size;
+ switch(s_)
+ {
+ case do_header:
+ BOOST_ASSERT(n <= buffer_size(
+ boost::get(v_)));
+ boost::get(v_).consume(n);
+ if(buffer_size(boost::get(v_)) > 0)
+ break;
+ header_done_ = true;
+ v_ = boost::blank{};
+ b_.consume(b_.size()); // VFALCO delete b_?
+ if(! more_)
+ goto go_complete;
+ s_ = do_body + 1;
+ break;
+
+ case do_header_only:
+ BOOST_ASSERT(n <= b_.size());
+ b_.consume(n);
+ if(buffer_size(b_.data()) > 0)
+ break;
+ // VFALCO delete b_?
+ header_done_ = true;
+ if(! is_deferred::value)
+ goto go_complete;
+ s_ = do_body;
+ break;
+
+ case do_body + 2:
+ {
+ BOOST_ASSERT(n <= buffer_size(
+ boost::get(v_)));
+ boost::get(v_).consume(n);
+ if(buffer_size(boost::get(v_)) > 0)
+ break;
+ v_ = boost::blank{};
+ if(! more_)
+ goto go_complete;
+ s_ = do_body + 1;
+ break;
+ }
+
+ //----------------------------------------------------------------------
+
+ case do_header_c:
+ BOOST_ASSERT(n <= buffer_size(
+ boost::get(v_)));
+ boost::get(v_).consume(n);
+ if(buffer_size(boost::get(v_)) > 0)
+ break;
+ header_done_ = true;
+ v_ = boost::blank{};
+ b_.consume(b_.size()); // VFALCO delete b_?
+ if(more_)
+ s_ = do_body_c + 1;
+ else
+ s_ = do_final_c;
+ break;
+
+ case do_header_only_c:
+ {
+ BOOST_ASSERT(n <= buffer_size(b_.data()));
+ b_.consume(n);
+ if(buffer_size(b_.data()) > 0)
+ break;
+ // VFALCO delete b_?
+ header_done_ = true;
+ if(! is_deferred::value)
+ {
+ s_ = do_final_c;
+ break;
+ }
+ s_ = do_body_c;
+ break;
+ }
+
+ case do_body_c + 2:
+ BOOST_ASSERT(n <= buffer_size(
+ boost::get(v_)));
+ boost::get(v_).consume(n);
+ if(buffer_size(boost::get(v_)) > 0)
+ break;
+ v_ = boost::blank{};
+ if(more_)
+ s_ = do_body_c + 1;
+ else
+ s_ = do_final_c;
+ break;
+
+ case do_final_c + 1:
+ BOOST_ASSERT(buffer_size(
+ boost::get(v_)));
+ boost::get(v_).consume(n);
+ if(buffer_size(boost::get(v_)) > 0)
+ break;
+ v_ = boost::blank{};
+ goto go_complete;
+
+ //----------------------------------------------------------------------
+
+ default:
+ BOOST_ASSERT(false);
+ case do_complete:
+ break;
+
+ go_complete:
+ s_ = do_complete;
+ break;
+ }
+}
+
+} // http
+} // beast
+
+#endif
diff --git a/include/beast/http/impl/write.ipp b/include/beast/http/impl/write.ipp
index 7b76bbf4..ecf3641a 100644
--- a/include/beast/http/impl/write.ipp
+++ b/include/beast/http/impl/write.ipp
@@ -9,128 +9,79 @@
#define BEAST_HTTP_IMPL_WRITE_IPP
#include
-#include
-#include
-#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include
-#include
+#include
#include
-#include
-#include
#include
#include
-#include
namespace beast {
namespace http {
-
namespace detail {
-template
-void
-write_start_line(std::ostream& os,
- header const& msg)
+template<
+ class Stream, class Serializer, class Handler>
+class write_some_op
{
- BOOST_ASSERT(msg.version == 10 || msg.version == 11);
- os << msg.method() << " " << msg.target();
- switch(msg.version)
- {
- case 10: os << " HTTP/1.0\r\n"; break;
- case 11: os << " HTTP/1.1\r\n"; break;
- }
-}
-
-template
-void
-write_start_line(std::ostream& os,
- header const& msg)
-{
- BOOST_ASSERT(msg.version == 10 || msg.version == 11);
- switch(msg.version)
- {
- case 10: os << "HTTP/1.0 "; break;
- case 11: os << "HTTP/1.1 "; break;
- }
- os << msg.status << " " << msg.reason() << "\r\n";
-}
-
-template
-void
-write_fields(std::ostream& os,
- FieldSequence const& fields)
-{
- //static_assert(is_FieldSequence::value,
- // "FieldSequence requirements not met");
- for(auto const& field : fields)
- {
- auto const name = field.name();
- BOOST_ASSERT(! name.empty());
- if(name[0] == ':')
- continue;
- os << field.name() << ": " << field.value() << "\r\n";
- }
-}
-
-} // detail
-
-//------------------------------------------------------------------------------
-
-template
-template
-class serializer::async_op
-{
- serializer& w_;
Stream& s_;
+ Serializer& sr_;
Handler h_;
- bool cont_;
+
+ class lambda
+ {
+ write_some_op& op_;
+
+ public:
+ bool empty = true;
+
+ explicit
+ lambda(write_some_op& op)
+ : op_(op)
+ {
+ }
+
+ template
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffer)
+ {
+ empty = false;
+ return op_.s_.async_write_some(
+ buffer, std::move(op_));
+ }
+ };
public:
- async_op(async_op&&) = default;
- async_op(async_op const&) = default;
+ write_some_op(write_some_op&&) = default;
+ write_some_op(write_some_op const&) = default;
- async_op(Handler&& h, Stream& s,
- serializer& w)
- : w_(w)
- , s_(s)
- , h_(std::move(h))
+ template
+ write_some_op(DeducedHandler&& h,
+ Stream& s, Serializer& sr)
+ : s_(s)
+ , sr_(sr)
+ , h_(std::forward(h))
{
- using boost::asio::asio_handler_is_continuation;
- cont_ = asio_handler_is_continuation(
- std::addressof(h_));
- }
-
- async_op(Handler const& h, Stream& s,
- serializer& w)
- : w_(w)
- , s_(s)
- , h_(h)
- {
- using boost::asio::asio_handler_is_continuation;
- cont_ = asio_handler_is_continuation(
- std::addressof(h_));
}
void
- operator()(error_code ec, std::size_t
- bytes_transferred, bool again = true);
+ operator()();
+
+ void
+ operator()(error_code ec,
+ std::size_t bytes_transferred);
friend
void* asio_handler_allocate(
- std::size_t size, async_op* op)
+ std::size_t size, write_some_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
@@ -139,7 +90,7 @@ public:
friend
void asio_handler_deallocate(
- void* p, std::size_t size, async_op* op)
+ void* p, std::size_t size, write_some_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
@@ -147,729 +98,50 @@ public:
}
friend
- bool asio_handler_is_continuation(async_op* op)
+ bool asio_handler_is_continuation(write_some_op* op)
{
- return op->cont_;
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->h_));
}
template
friend
- void asio_handler_invoke(
- Function&& f, async_op* op)
+ void asio_handler_invoke(Function&& f, write_some_op* op)
{
using boost::asio::asio_handler_invoke;
- asio_handler_invoke(
- f, std::addressof(op->h_));
+ asio_handler_invoke(f, std::addressof(op->h_));
}
};
-template
-template
+template<
+ class Stream, class Serializer, class Handler>
void
-serializer::
-async_op::
-operator()(error_code ec,
- std::size_t bytes_transferred, bool again)
+write_some_op::
+operator()()
{
- cont_ = again || cont_;
- using boost::asio::buffer_size;
- if(ec)
- goto upcall;
- switch(w_.s_)
- {
- case do_init:
- {
- if(w_.split_)
- goto go_header_only;
- w_.rd_.emplace(w_.m_);
- w_.rd_->init(ec);
- if(ec)
- return s_.get_io_service().post(
- bind_handler(std::move(*this), ec, 0));
- auto result = w_.rd_->get(ec);
- if(ec)
- {
- // Can't use need_more when ! is_deferred
- BOOST_ASSERT(ec != error::need_more);
- return s_.get_io_service().post(
- bind_handler(std::move(*this), ec, 0));
- }
- if(! result)
- goto go_header_only;
- w_.more_ = result->second;
- w_.v_ = cb0_t{
- boost::in_place_init,
- w_.b_.data(),
- result->first};
- // [[fallthrough]]
- }
+ lambda f{*this};
+ error_code ec;
+ sr_.get(ec, f);
+ if(! ec && ! f.empty)
+ return;
+ return s_.get_io_service().post(
+ bind_handler(std::move(*this), ec, 0));
+}
- case do_header:
- w_.s_ = do_header + 1;
- return s_.async_write_some(
- buffer_prefix(w_.limit_,
- boost::get(w_.v_)),
- std::move(*this));
-
- case do_header + 1:
- boost::get(w_.v_).consume(
- bytes_transferred);
- if(buffer_size(
- boost::get(w_.v_)) > 0)
- {
- w_.s_ = do_header;
- break;
- }
- w_.header_done_ = true;
- w_.v_ = boost::blank{};
- w_.b_.consume(w_.b_.size()); // VFALCO delete b_?
- if(! w_.more_)
- goto go_complete;
- w_.s_ = do_body;
- break;
-
- go_header_only:
- case do_header_only:
- w_.s_ = do_header_only + 1;
- return s_.async_write_some(
- buffer_prefix(w_.limit_,
- w_.b_.data()), std::move(*this));
-
- case do_header_only + 1:
- w_.b_.consume(bytes_transferred);
- if(buffer_size(w_.b_.data()) > 0)
- {
- w_.s_ = do_header_only;
- break;
- }
- // VFALCO delete b_?
- w_.header_done_ = true;
- if(! is_deferred::value)
- goto go_complete;
- BOOST_ASSERT(! w_.rd_);
- w_.rd_.emplace(w_.m_);
- w_.rd_->init(ec);
- if(ec)
- goto upcall;
- w_.s_ = do_body;
- break;
-
- case do_body:
- {
- auto result = w_.rd_->get(ec);
- if(ec)
- return s_.get_io_service().post(
- bind_handler(std::move(*this), ec, 0));
- if(! result)
-
- {
- w_.s_ = do_complete;
- return s_.get_io_service().post(
- bind_handler(std::move(*this),
- error_code{}, 0));
- }
- w_.more_ = result->second;
- w_.v_ = cb1_t{result->first};
- // [[fallthrough]]
- }
-
- case do_body + 1:
- w_.s_ = do_body + 2;
- return s_.async_write_some(
- buffer_prefix(w_.limit_,
- boost::get(w_.v_)),
- std::move(*this));
-
- case do_body + 2:
- boost::get(w_.v_).consume(
- bytes_transferred);
- if(buffer_size(
- boost::get(w_.v_)) > 0)
- {
- w_.s_ = do_body + 1;
- break;
- }
- w_.v_ = boost::blank{};
- if(! w_.more_)
- goto go_complete;
- w_.s_ = do_body;
- break;
-
- //----------------------------------------------------------------------
-
- case do_init_c:
- {
- if(w_.split_)
- goto go_header_only_c;
- w_.rd_.emplace(w_.m_);
- w_.rd_->init(ec);
- if(ec)
- return s_.get_io_service().post(
- bind_handler(std::move(*this), ec, 0));
- auto result = w_.rd_->get(ec);
- if(ec)
- {
- // Can't use need_more when ! is_deferred
- BOOST_ASSERT(ec != error::need_more);
- return s_.get_io_service().post(
- bind_handler(std::move(*this), ec, 0));
- }
- if(! result)
- goto go_header_only_c;
- w_.more_ = result->second;
- w_.v_ = ch0_t{
- boost::in_place_init,
- w_.b_.data(),
- detail::chunk_header{
- buffer_size(result->first)},
- [&]()
- {
- auto sv = w_.d_(result->first);
- return boost::asio::const_buffers_1{
- sv.data(), sv.size()};
-
- }(),
- result->first,
- detail::chunk_crlf()};
- // [[fallthrough]]
- }
-
- case do_header_c:
- w_.s_ = do_header_c + 1;
- return s_.async_write_some(
- buffer_prefix(w_.limit_,
- boost::get(w_.v_)),
- std::move(*this));
-
- case do_header_c + 1:
- boost::get(w_.v_).consume(
- bytes_transferred);
- if(buffer_size(
- boost::get(w_.v_)) > 0)
- {
- w_.s_ = do_header_c;
- break;
- }
- w_.header_done_ = true;
- w_.v_ = boost::blank{};
- w_.b_.consume(w_.b_.size()); // VFALCO delete b_?
- if(! w_.more_)
- w_.s_ = do_final_c;
- else
- w_.s_ = do_body_c;
- break;
-
- go_header_only_c:
- case do_header_only_c:
- w_.s_ = do_header_only_c + 1;
- return s_.async_write_some(
- buffer_prefix(w_.limit_,
- w_.b_.data()), std::move(*this));
-
- case do_header_only_c + 1:
- w_.b_.consume(bytes_transferred);
- if(buffer_size(w_.b_.data()) > 0)
- {
- w_.s_ = do_header_only_c;
- break;
- }
- // VFALCO delete b_?
- w_.header_done_ = true;
- if(! is_deferred::value)
- {
- w_.s_ = do_final_c;
- break;
- }
- BOOST_ASSERT(! w_.rd_);
- w_.rd_.emplace(w_.m_);
- w_.rd_->init(ec);
- if(ec)
- goto upcall;
- w_.s_ = do_body_c;
- break;
-
- case do_body_c:
- {
- auto result = w_.rd_->get(ec);
- if(ec)
- return s_.get_io_service().post(
- bind_handler(std::move(*this),
- ec, 0));
- if(! result)
- goto go_final_c;
- w_.more_ = result->second;
- w_.v_ = ch1_t{
- boost::in_place_init,
- detail::chunk_header{
- buffer_size(result->first)},
- [&]()
- {
- auto sv = w_.d_(result->first);
- return boost::asio::const_buffers_1{
- sv.data(), sv.size()};
-
- }(),
- result->first,
- detail::chunk_crlf()};
- // [[fallthrough]]
- }
-
- case do_body_c + 1:
- w_.s_ = do_body_c + 2;
- return s_.async_write_some(
- buffer_prefix(w_.limit_,
- boost::get(w_.v_)),
- std::move(*this));
-
- case do_body_c + 2:
- boost::get(w_.v_).consume(
- bytes_transferred);
- if(buffer_size(
- boost::get(w_.v_)) > 0)
- {
- w_.s_ = do_body_c + 1;
- break;
- }
- w_.v_ = boost::blank{};
- if(w_.more_)
- w_.s_ = do_body_c;
- else
- w_.s_ = do_final_c;
- break;
-
- go_final_c:
- case do_final_c:
- w_.v_ = ch2_t{
- boost::in_place_init,
- detail::chunk_final(),
- [&]()
- {
- auto sv = w_.d_(
- boost::asio::null_buffers{});
- return boost::asio::const_buffers_1{
- sv.data(), sv.size()};
-
- }(),
- detail::chunk_crlf()};
- // [[fallthrough]]
-
- case do_final_c + 1:
- w_.s_ = do_final_c + 2;
- return s_.async_write_some(
- buffer_prefix(w_.limit_,
- boost::get(w_.v_)),
- std::move(*this));
-
- case do_final_c + 2:
- boost::get(w_.v_).consume(
- bytes_transferred);
- if(buffer_size(boost::get(w_.v_)) > 0)
- {
- w_.s_ = do_final_c + 1;
- break;
- }
- w_.v_ = boost::blank{};
- goto go_complete;
-
- //----------------------------------------------------------------------
-
- default:
- BOOST_ASSERT(false);
-
- go_complete:
- w_.s_ = do_complete;
- case do_complete:
- if(w_.close_)
- // VFALCO TODO Decide on an error code
- ec = boost::asio::error::eof;
- break;
- }
-
-upcall:
+template<
+ class Stream, class Serializer, class Handler>
+void
+write_some_op::
+operator()(error_code ec, std::size_t bytes_transferred)
+{
+ if(! ec)
+ sr_.consume(bytes_transferred);
h_(ec);
}
//------------------------------------------------------------------------------
-template
-serializer::
-serializer(message const& m,
- Decorator const& d,
- Allocator const& alloc)
- : m_(m)
- , d_(d)
- , b_(1024, alloc)
- , chunked_(token_list{
- m.fields["Transfer-Encoding"]}.exists("chunked"))
- , close_(token_list{
- m.fields["Connection"]}.exists("close") ||
- (m.version < 11 && ! m.fields.exists(
- "Content-Length")))
-{
- s_ = chunked_ ? do_init_c : do_init;
- auto os = ostream(b_);
- detail::write_start_line(os, m_);
- detail::write_fields(os, m_.fields);
- os << "\r\n";
-}
-
-template
-template
-void
-serializer::
-write_some(SyncWriteStream& stream)
-{
- static_assert(
- is_sync_write_stream::value,
- "SyncWriteStream requirements not met");
- static_assert(is_body::value,
- "Body requirements not met");
- static_assert(is_body_reader::value,
- "BodyReader requirements not met");
- error_code ec;
- write_some(stream, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
-}
-
-template
-template
-void
-serializer::
-write_some(SyncWriteStream& stream, error_code &ec)
-{
- static_assert(
- is_sync_write_stream::value,
- "SyncWriteStream requirements not met");
- static_assert(is_body::value,
- "Body requirements not met");
- static_assert(is_body_reader::value,
- "BodyReader requirements not met");
-
- using boost::asio::buffer_size;
- switch(s_)
- {
- case do_init:
- {
- if(split_)
- goto go_header_only;
- rd_.emplace(m_);
- rd_->init(ec);
- if(ec)
- return;
- auto result = rd_->get(ec);
- if(ec)
- {
- // Can't use need_more when ! is_deferred
- BOOST_ASSERT(ec != error::need_more);
- return;
- }
- if(! result)
- goto go_header_only;
- more_ = result->second;
- v_ = cb0_t{
- boost::in_place_init,
- b_.data(),
- result->first};
- s_ = do_header;
- // [[fallthrough]]
- }
-
- case do_header:
- {
- auto bytes_transferred =
- stream.write_some(
- buffer_prefix(limit_,
- boost::get(v_)), ec);
- if(ec)
- return;
- boost::get(v_).consume(
- bytes_transferred);
- if(buffer_size(boost::get(v_)) > 0)
- break;
- header_done_ = true;
- v_ = boost::blank{};
- b_.consume(b_.size()); // VFALCO delete b_?
- if(! more_)
- goto go_complete;
- s_ = do_body;
- break;
- }
-
- go_header_only:
- s_ = do_header_only;
- case do_header_only:
- {
- auto bytes_transferred =
- stream.write_some(buffer_prefix(
- limit_, b_.data()), ec);
- if(ec)
- return;
- b_.consume(bytes_transferred);
- if(buffer_size(b_.data()) > 0)
- break;
- // VFALCO delete b_?
- header_done_ = true;
- if(! is_deferred::value)
- goto go_complete;
- BOOST_ASSERT(! rd_);
- rd_.emplace(m_);
- rd_->init(ec);
- if(ec)
- return;
- s_ = do_body;
- break;
- }
-
- case do_body:
- {
- auto result = rd_->get(ec);
- if(ec)
- return;
- if(! result)
- goto go_complete;
- more_ = result->second;
- v_ = cb1_t{result->first};
- s_ = do_body + 1;
- // [[fallthrough]]
- }
-
- case do_body + 1:
- {
- auto bytes_transferred =
- stream.write_some(buffer_prefix(
- limit_, boost::get(v_)), ec);
- if(ec)
- return;
- boost::get(v_).consume(
- bytes_transferred);
- if(buffer_size(boost::get(v_)) > 0)
- break;
- v_ = boost::blank{};
- if(! more_)
- goto go_complete;
- s_ = do_body;
- break;
- }
-
- //----------------------------------------------------------------------
-
- case do_init_c:
- {
- if(split_)
- goto go_header_only_c;
- rd_.emplace(m_);
- rd_->init(ec);
- if(ec)
- return;
- auto result = rd_->get(ec);
- if(ec)
- {
- // Can't use need_more when ! is_deferred
- BOOST_ASSERT(ec != error::need_more);
- return;
- }
- if(! result)
- goto go_header_only_c;
- more_ = result->second;
- v_ = ch0_t{
- boost::in_place_init,
- b_.data(),
- detail::chunk_header{
- buffer_size(result->first)},
- [&]()
- {
- auto sv = d_(result->first);
- return boost::asio::const_buffers_1{
- sv.data(), sv.size()};
-
- }(),
- result->first,
- detail::chunk_crlf()};
- s_ = do_header_c;
- // [[fallthrough]]
- }
-
- case do_header_c:
- {
- auto bytes_transferred =
- stream.write_some(buffer_prefix(
- limit_, boost::get(v_)), ec);
- if(ec)
- return;
- boost::get(v_).consume(
- bytes_transferred);
- if(buffer_size(boost::get(v_)) > 0)
- break;
- header_done_ = true;
- v_ = boost::blank{};
- b_.consume(b_.size()); // VFALCO delete b_?
- if(more_)
- s_ = do_body_c;
- else
- s_ = do_final_c;
- break;
- }
-
- go_header_only_c:
- s_ = do_header_only_c;
- case do_header_only_c:
- {
- auto bytes_transferred =
- stream.write_some(buffer_prefix(
- limit_, b_.data()), ec);
- if(ec)
- return;
- b_.consume(bytes_transferred);
- if(buffer_size(b_.data()) > 0)
- break;
- // VFALCO delete b_?
- header_done_ = true;
- if(! is_deferred::value)
- {
- s_ = do_final_c;
- break;
- }
- BOOST_ASSERT(! rd_);
- rd_.emplace(m_);
- rd_->init(ec);
- if(ec)
- return;
- s_ = do_body_c;
- break;
- }
-
- case do_body_c:
- {
- auto result = rd_->get(ec);
- if(ec)
- return;
- if(! result)
- goto go_final_c;
- more_ = result->second;
- v_ = ch1_t{
- boost::in_place_init,
- detail::chunk_header{
- buffer_size(result->first)},
- [&]()
- {
- auto sv = d_(result->first);
- return boost::asio::const_buffers_1{
- sv.data(), sv.size()};
-
- }(),
- result->first,
- detail::chunk_crlf()};
- s_ = do_body_c + 1;
- // [[fallthrough]]
- }
-
- case do_body_c + 1:
- {
- auto bytes_transferred =
- stream.write_some(buffer_prefix(
- limit_, boost::get(v_)), ec);
- if(ec)
- return;
- boost::get(v_).consume(
- bytes_transferred);
- if(buffer_size(boost::get(v_)) > 0)
- break;
- v_ = boost::blank{};
- if(more_)
- s_ = do_body_c;
- else
- s_ = do_final_c;
- break;
- }
-
- go_final_c:
- case do_final_c:
- v_ = ch2_t{
- boost::in_place_init,
- detail::chunk_final(),
- [&]()
- {
- auto sv = d_(
- boost::asio::null_buffers{});
- return boost::asio::const_buffers_1{
- sv.data(), sv.size()};
-
- }(),
- detail::chunk_crlf()};
- s_ = do_final_c + 1;
- // [[fallthrough]]
-
- case do_final_c + 1:
- {
- auto bytes_transferred =
- stream.write_some(buffer_prefix(
- limit_, boost::get(v_)), ec);
- if(ec)
- return;
- boost::get(v_).consume(
- bytes_transferred);
- if(buffer_size(boost::get(v_)) > 0)
- break;
- v_ = boost::blank{};
- goto go_complete;
- }
-
- default:
- case do_complete:
- BOOST_ASSERT(false);
-
- //----------------------------------------------------------------------
-
- go_complete:
- s_ = do_complete;
- if(close_)
- {
- // VFALCO TODO Decide on an error code
- ec = boost::asio::error::eof;
- return;
- }
- break;
- }
-}
-
-template
-template
-async_return_type
-serializer::
-async_write_some(AsyncWriteStream& stream,
- WriteHandler&& handler)
-{
- static_assert(is_async_write_stream::value,
- "AsyncWriteStream requirements not met");
- static_assert(is_body::value,
- "Body requirements not met");
- static_assert(is_body_reader::value,
- "BodyReader requirements not met");
- async_completion init{handler};
- async_op>{
- init.completion_handler, stream, *this}(
- error_code{}, 0, false);
-
- return init.result.get();
-}
-
-//------------------------------------------------------------------------------
-
-namespace detail {
-
template
class write_op
@@ -879,12 +151,12 @@ class write_op
int state = 0;
Stream& s;
serializer> ws;
+ empty_decorator, handler_alloc> sr;
data(Handler& h, Stream& s_, message<
isRequest, Body, Fields> const& m_)
: s(s_)
- , ws(m_, empty_decorator{},
+ , sr(m_, empty_decorator{},
handler_alloc{h})
{
}
@@ -956,20 +228,120 @@ operator()(error_code ec)
{
case 0:
d.state = 1;
- return d.ws.async_write_some(d.s, std::move(*this));
+ write_some_op{std::move(*this), d.s, d.sr}();
+ return;
case 1:
d.state = 2;
+ // [[fallthrough]]
case 2:
- if(d.ws.is_done())
+ if(d.sr.is_done())
+ {
+ if(d.sr.needs_close())
+ // VFALCO Choose an error code
+ ec = boost::asio::error::eof;
break;
- return d.ws.async_write_some(d.s, std::move(*this));
+ }
+ write_some_op{std::move(*this), d.s, d.sr}();
+ return;
}
upcall:
d_.invoke(ec);
}
+//------------------------------------------------------------------------------
+
+template
+class write_some_lambda
+{
+ Stream& stream_;
+
+public:
+ boost::optional bytes_transferred;
+
+ explicit
+ write_some_lambda(Stream& stream)
+ : stream_(stream)
+ {
+ }
+
+ template
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffer)
+ {
+ bytes_transferred =
+ stream_.write_some(buffer, ec);
+ }
+};
+
} // detail
+//------------------------------------------------------------------------------
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class Decorator, class Allocator>
+void
+write_some(SyncWriteStream& stream, serializer<
+ isRequest, Body, Fields, Decorator, Allocator>& sr)
+{
+ static_assert(is_sync_write_stream::value,
+ "SyncWriteStream requirements not met");
+ error_code ec;
+ write_some(stream, sr, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+}
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class Decorator, class Allocator>
+void
+write_some(SyncWriteStream& stream, serializer<
+ isRequest, Body, Fields, Decorator, Allocator>& sr,
+ error_code& ec)
+{
+ static_assert(is_sync_write_stream::value,
+ "SyncWriteStream requirements not met");
+ detail::write_some_lambda f{stream};
+ sr.get(ec, f);
+ if(! ec)
+ {
+ if(f.bytes_transferred)
+ sr.consume(*f.bytes_transferred);
+ if(sr.is_done() && sr.needs_close())
+ // VFALCO decide on an error code
+ ec = boost::asio::error::eof;
+ }
+}
+
+template
+async_return_type
+async_write_some(AsyncWriteStream& stream, serializer<
+ isRequest, Body, Fields, Decorator, Allocator>& sr,
+ WriteHandler&& handler)
+{
+ static_assert(is_async_write_stream<
+ AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body::value,
+ "Body requirements not met");
+ static_assert(is_body_reader::value,
+ "BodyReader requirements not met");
+ async_completion init{handler};
+ detail::write_some_op>{
+ init.completion_handler, stream, sr}();
+ return init.result.get();
+}
+
template
void
@@ -1001,13 +373,17 @@ write(SyncWriteStream& stream,
"Body requirements not met");
static_assert(is_body_reader::value,
"BodyReader requirements not met");
- auto ws = make_serializer(msg);
+ auto sr = make_serializer(msg);
for(;;)
{
- ws.write_some(stream, ec);
+#if 0
+ sr.write_some(stream, ec);
+#else
+ write_some(stream, sr, ec);
+#endif
if(ec)
return;
- if(ws.is_done())
+ if(sr.is_done())
break;
}
}
diff --git a/include/beast/http/serializer.hpp b/include/beast/http/serializer.hpp
new file mode 100644
index 00000000..ec6a4550
--- /dev/null
+++ b/include/beast/http/serializer.hpp
@@ -0,0 +1,350 @@
+//
+// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef BEAST_HTTP_SERIALIZER_HPP
+#define BEAST_HTTP_SERIALIZER_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace beast {
+namespace http {
+
+/** A chunk decorator which does nothing.
+
+ When selected as a chunk decorator, objects of this type
+ affect the output of messages specifying chunked
+ transfer encodings as follows:
+
+ @li chunk headers will have empty chunk extensions, and
+
+ @li final chunks will have an empty set of trailers.
+
+ @see @ref serializer
+*/
+struct empty_decorator
+{
+ template
+ string_view
+ operator()(ConstBufferSequence const&) const
+ {
+ return {"\r\n"};
+ }
+
+ string_view
+ operator()(boost::asio::null_buffers) const
+ {
+ return {};
+ }
+};
+
+/** Provides buffer oriented HTTP message serialization functionality.
+
+ An object of this type is used to serialize a complete
+ HTTP message into a seriest of octets. To use this class,
+ construct an instance with the message to be serialized.
+ To make it easier to declare the type, the helper function
+ @ref make_serializer is provided.
+
+ The implementation will automatically perform chunk encoding
+ if the contents of the message indicate that chunk encoding
+ is required. If the semantics of the message indicate that
+ the connection should be closed after the message is sent, the
+ function @ref needs_close will return `true`.
+
+ Upon construction, an optional chunk decorator may be
+ specified. This decorator is a function object called with
+ each buffer sequence of the body when the chunked transfer
+ encoding is indicate in the message header. The decorator
+ will be called with an empty buffer sequence (actually
+ the type `boost::asio::null_buffers`) to indicate the
+ final chunk. The decorator may return a string which forms
+ the chunk extension for chunks, and the field trailers
+ for the final chunk.
+
+ In C++11 the decorator must be declared as a class or
+ struct with a templated operator() thusly:
+
+ @code
+ // The implementation guarantees that operator()
+ // will be called only after the view returned by
+ // any previous calls to operator() are no longer
+ // needed. The decorator instance is intended to
+ // manage the lifetime of the storage for all returned
+ // views.
+ //
+ struct decorator
+ {
+ // Returns the chunk-extension for each chunk.
+ // The buffer returned must include a trailing "\r\n",
+ // and the leading semicolon (";") if one or more
+ // chunk extensions are specified.
+ //
+ template
+ string_view
+ operator()(ConstBufferSequence const&) const;
+
+ // Returns a set of field trailers for the final chunk.
+ // Each field should be formatted according to rfc7230
+ // including the trailing "\r\n" for each field. If
+ // no trailers are indicated, an empty string is returned.
+ //
+ string_view
+ operator()(boost::asio::null_buffers) const;
+ };
+ @endcode
+
+ @tparam isRequest `true` if the message is a request.
+
+ @tparam Body The body type of the message.
+
+ @tparam Fields The type of fields in the message.
+
+ @tparam Decorator The type of chunk decorator to use.
+
+ @tparam Allocator The type of allocator to use.
+
+ @see @ref make_serializer
+*/
+template<
+ bool isRequest, class Body, class Fields,
+ class Decorator = empty_decorator,
+ class Allocator = std::allocator
+>
+class serializer
+{
+ static_assert(is_body::value,
+ "Body requirements not met");
+ static_assert(is_body_reader::value,
+ "BodyReader requirements not met");
+
+ enum
+ {
+ do_init = 0,
+ do_header_only = 10,
+ do_header = 20,
+ do_body = 40,
+
+ do_init_c = 50,
+ do_header_only_c = 60,
+ do_header_c = 70,
+ do_body_c = 90,
+ do_final_c = 100,
+
+ do_complete = 110
+ };
+
+ void split(bool, std::true_type) {}
+ void split(bool v, std::false_type) { split_ = v; }
+
+ using buffer_type =
+ basic_multi_buffer;
+
+ using reader = typename Body::reader;
+
+ using is_deferred =
+ typename reader::is_deferred;
+
+ using cb0_t = consuming_buffers>; // body
+
+ using cb1_t = consuming_buffers<
+ typename reader::const_buffers_type>; // body
+
+ using ch0_t = consuming_buffers>; // crlf
+
+ using ch1_t = consuming_buffers>; // crlf
+
+ using ch2_t = consuming_buffers>; // crlf
+
+ message const& m_;
+ Decorator d_;
+ boost::optional rd_;
+ buffer_type b_;
+ boost::variant v_;
+ int s_;
+ bool split_ = is_deferred::value;
+ bool header_done_ = false;
+ bool chunked_;
+ bool close_;
+ bool more_;
+
+public:
+ /** Constructor
+
+ @param msg The message to serialize. The message object
+ must remain valid for the lifetime of the write stream.
+
+ @param decorator An optional decorator to use.
+
+ @param alloc An optional allocator to use.
+ */
+ explicit
+ serializer(message const& msg,
+ Decorator const& decorator = Decorator{},
+ Allocator const& alloc = Allocator{});
+
+ /** Returns `true` if we will pause after writing the header.
+ */
+ bool
+ split() const
+ {
+ return split_;
+ }
+
+ /** Set whether the header and body are written separately.
+
+ When the split feature is enabled, the implementation will
+ write only the octets corresponding to the serialized header
+ first. If the header has already been written, this function
+ will have no effect on output. This function should be called
+ before any writes take place, otherwise the behavior is
+ undefined.
+ */
+ void
+ split(bool v)
+ {
+ split(v, is_deferred{});
+ }
+
+ /** Return `true` if serialization of the header is complete.
+
+ This function indicates whether or not all octets
+ corresponding to the serialized representation of the
+ header have been successfully delivered to the stream.
+ */
+ bool
+ is_header_done() const
+ {
+ return header_done_;
+ }
+
+ /** Return `true` if serialization is complete
+
+ The operation is complete when all octets corresponding
+ to the serialized representation of the message have been
+ successfully delivered to the stream.
+ */
+ bool
+ is_done() const
+ {
+ return s_ == do_complete;
+ }
+
+ /** Return `true` if Connection: close semantic is indicated.
+
+ After serialization is complete, if there is an
+ underlying network connection then it should be closed if
+ this function returns `true`.
+ */
+ bool
+ needs_close() const
+ {
+ return close_;
+ }
+
+ /** Returns the next set of buffers in the serialization.
+
+ This function will attempt to call the `visit` function
+ object with a @b ConstBufferSequence of unspecified type
+ representing the next set of buffers in the serialization
+ of the message represented by this object.
+
+ If there are no more buffers in the serialization, the
+ visit function will not be called. In this case, no error
+ will be indicated, and the function @ref is_done will
+ return `true`.
+
+ @param ec Set to the error, if any occurred.
+
+ @param visit The function to call. The equivalent function
+ signature of this object must be:
+ @code template
+ void visit(error_code&, ConstBufferSequence const&);
+ @endcode
+ The function is not copied, if no error occurs it will be
+ invoked before the call to @ref get returns.
+
+ */
+ template
+ void
+ get(error_code& ec, Visit&& visit);
+
+ /** Consume buffer octets in the serialization.
+
+ This function should be called after one or more octets
+ contained in the buffers provided in the prior call
+ to @ref get have been used.
+
+ After a call to @ref consume, callers should check the
+ return value of @ref is_done to determine if the entire
+ message has been serialized.
+
+ @param n The number of octets to consume. This number must
+ be greater than zero and no greater than the number of
+ octets in the buffers provided in the prior call to @ref get.
+ */
+ void
+ consume(std::size_t n);
+};
+
+/** Return a stateful object to serialize an HTTP message.
+
+ This convenience function makes it easier to declare
+ the variable for a given message.
+
+ @see @ref serializer
+*/
+template<
+ bool isRequest, class Body, class Fields,
+ class Decorator = empty_decorator,
+ class Allocator = std::allocator>
+inline
+serializer::type,
+ typename std::decay::type>
+make_serializer(message const& m,
+ Decorator const& decorator = Decorator{},
+ Allocator const& allocator = Allocator{})
+{
+ return serializer::type,
+ typename std::decay::type>{
+ m, decorator, allocator};
+}
+
+} // http
+} // beast
+
+#include
+
+#endif
diff --git a/include/beast/http/write.hpp b/include/beast/http/write.hpp
index 9e58290c..969d090e 100644
--- a/include/beast/http/write.hpp
+++ b/include/beast/http/write.hpp
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -27,410 +28,109 @@
namespace beast {
namespace http {
-/** A chunk decorator which does nothing.
+/** Write some serialized message data to a stream.
- When selected as a chunk decorator, objects of this type
- affect the output of messages specifying chunked
- transfer encodings as follows:
+ This function is used to write serialized message data to the
+ stream. The function call will block until one of the following
+ conditions is true:
+
+ @li One or more bytes have been transferred.
- @li chunk headers will have empty chunk extensions, and
+ @li An error occurs on the stream.
- @li final chunks will have an empty set of trailers.
+ In order to completely serialize a message, this function
+ should be called until `sr.is_done()` returns `true`.
+
+ @param stream The stream to write to. This type must
+ satisfy the requirements of @b SyncWriteStream.
- @see @ref serializer
+ @param sr The serializer to use.
+
+ @throws system_error Thrown on failure.
+
+ @see @ref async_write_some, @ref serializer
*/
-struct empty_decorator
-{
- template
- string_view
- operator()(ConstBufferSequence const&) const
- {
- return {"\r\n"};
- }
-
- string_view
- operator()(boost::asio::null_buffers) const
- {
- return {};
- }
-};
-
-/** Provides stream-oriented HTTP message serialization functionality.
-
- Objects of this type may be used to perform synchronous or
- asynchronous serialization of an HTTP message on a stream.
- Unlike functions such as @ref write or @ref async_write,
- the stream operations provided here guarantee that bounded
- work will be performed. This is accomplished by making one
- or more calls to the underlying stream's `write_some` or
- `async_write_some` member functions. In order to fully
- serialize the message, multiple calls are required.
-
- The ability to incrementally serialize a message, peforming
- bounded work at each iteration is useful in many scenarios,
- such as:
-
- @li Setting consistent, per-call timeouts
-
- @li Efficiently relaying body content from another stream
-
- @li Performing application-layer flow control
-
- To use this class, construct an instance with the message
- to be sent. To make it easier to declare the type, the
- helper function @ref make_serializer is provided:
-
- The implementation will automatically perform chunk encoding
- if the contents of the message indicate that chunk encoding
- is required. If the semantics of the message indicate that
- the connection should be closed after the message is sent,
- the error delivered from stream operations will be
- `boost::asio::error::eof`.
-
- @code
- template
- void send(Stream& stream, request const& msg)
- {
- serializer w{msg};
- do
- {
- w.write_some();
- }
- while(! w.is_done());
- }
- @endcode
-
- Upon construction, an optional chunk decorator may be
- specified. This decorator is a function object called with
- each buffer sequence of the body when the chunked transfer
- encoding is indicate in the message header. The decorator
- will be called with an empty buffer sequence (actually
- the type `boost::asio::null_buffers`) to indicate the
- final chunk. The decorator may return a string which forms
- the chunk extension for chunks, and the field trailers
- for the final chunk.
-
- In C++11 the decorator must be declared as a class or
- struct with a templated operator() thusly:
-
- @code
- // The implementation guarantees that operator()
- // will be called only after the view returned by
- // any previous calls to operator() are no longer
- // needed. The decorator instance is intended to
- // manage the lifetime of the storage for all returned
- // views.
- //
- struct decorator
- {
- // Returns the chunk-extension for each chunk.
- // The buffer returned must include a trailing "\r\n",
- // and the leading semicolon (";") if one or more
- // chunk extensions are specified.
- //
- template
- string_view
- operator()(ConstBufferSequence const&) const;
-
- // Returns a set of field trailers for the final chunk.
- // Each field should be formatted according to rfc7230
- // including the trailing "\r\n" for each field. If
- // no trailers are indicated, an empty string is returned.
- //
- string_view
- operator()(boost::asio::null_buffers) const;
- };
- @endcode
-
- @par Thread Safety
- @e Distinct @e objects: Safe.@n
- @e Shared @e objects: Unsafe.
-
- @tparam isRequest `true` if the message is a request.
-
- @tparam Body The body type of the message.
-
- @tparam Fields The type of fields in the message.
-
- @tparam Decorator The type of chunk decorator to use.
-
- @tparam Allocator The type of allocator to use.
-
- @see @ref make_serializer
-*/
-template<
+template
->
-class serializer
-{
- template
- class async_op;
+ class Decorator, class Allocator>
+void
+write_some(SyncWriteStream& stream, serializer<
+ isRequest, Body, Fields, Decorator, Allocator>& sr);
- enum
- {
- do_init = 0,
- do_header_only = 10,
- do_header = 20,
- do_body = 40,
+
+/** Write some serialized message data to a stream.
+
+ This function is used to write serialized message data to the
+ stream. The function call will block until one of the following
+ conditions is true:
- do_init_c = 50,
- do_header_only_c = 60,
- do_header_c = 70,
- do_body_c = 90,
- do_final_c = 100,
+ @li One or more bytes have been transferred.
- do_complete = 110
- };
+ @li An error occurs on the stream.
- void split(bool, std::true_type) {}
- void split(bool v, std::false_type) { split_ = v; }
-
- using buffer_type =
- basic_multi_buffer;
-
- using reader = typename Body::reader;
-
- using is_deferred =
- typename reader::is_deferred;
-
- using cb0_t = consuming_buffers>; // body
-
- using cb1_t = consuming_buffers<
- typename reader::const_buffers_type>; // body
-
- using ch0_t = consuming_buffers>; // crlf
+ In order to completely serialize a message, this function
+ should be called until `sr.is_done()` returns `true`.
- using ch1_t = consuming_buffers>; // crlf
+ @param stream The stream to write to. This type must
+ satisfy the requirements of @b SyncWriteStream.
- using ch2_t = consuming_buffers>; // crlf
+ @param sr The serializer to use.
- message const& m_;
- Decorator d_;
- std::size_t limit_ =
- (std::numeric_limits::max)();
- boost::optional rd_;
- buffer_type b_;
- boost::variant v_;
- int s_;
- bool split_ = is_deferred::value;
- bool header_done_ = false;
- bool chunked_;
- bool close_;
- bool more_;
+ @param ec Set to indicate what error occurred, if any.
-public:
- /** Constructor
+ @see @ref async_write_some, @ref serializer
+*/
+template
+void
+write_some(SyncWriteStream& stream, serializer<
+ isRequest, Body, Fields, Decorator, Allocator>& sr,
+ error_code& ec);
- @param msg The message to serialize. The message object
- must remain valid for the lifetime of the write stream.
+/** Start an asynchronous write of some serialized message data to a stream.
- @param decorator An optional decorator to use.
+ This function is used to asynchronously write serialized
+ message data to the stream. The function call always returns
+ immediately. The asynchronous operation will continue until
+ one of the following conditions is true:
- @param alloc An optional allocator to use.
- */
- explicit
- serializer(message const& msg,
- Decorator const& decorator = Decorator{},
- Allocator const& alloc = Allocator{});
+ @li One or more bytes have been transferred.
- /// Returns the maximum number of bytes that will be written in each operation
- std::size_t
- limit() const
- {
- return limit_;
- }
+ @li An error occurs on the stream.
- /** Returns `true` if we will pause after writing the header.
- */
- bool
- split() const
- {
- return split_;
- }
+ In order to completely serialize a message, this function
+ should be called until `sr.is_done()` returns `true`.
- /** Set whether the header and body are written separately.
+ @param stream The stream to write to. This type must
+ satisfy the requirements of @b SyncWriteStream.
- When the split feature is enabled, the implementation will
- write only the octets corresponding to the serialized header
- first. If the header has already been written, this function
- will have no effect on output. This function should be called
- before any writes take place, otherwise the behavior is
- undefined.
- */
- void
- split(bool v)
- {
- split(v, is_deferred{});
- }
+ @param sr The serializer to use for writing.
- /** Set the maximum number of bytes that will be written in each operation.
+ @param handler The handler to be called when the request
+ completes. Copies will be made of the handler as required. The
+ equivalent function signature of the handler must be:
+ @code void handler(
+ error_code const& ec // Result of operation
+ ); @endcode
+ Regardless of whether the asynchronous operation completes
+ immediately or not, the handler will not be invoked from within
+ this function. Invocation of the handler will be performed in a
+ manner equivalent to using `boost::asio::io_service::post`.
- By default, there is no limit on the size of writes.
-
- @param n The byte limit. This must be greater than zero.
- */
- void
- limit(std::size_t n)
- {
- limit_ = n;
- }
-
- /** Return `true` if serialization of the header is complete.
-
- This function indicates whether or not all octets
- corresponding to the serialized representation of the
- header have been successfully delivered to the stream.
- */
- bool
- is_header_done() const
- {
- return header_done_;
- }
-
- /** Return `true` if serialization is complete
-
- The operation is complete when all octets corresponding
- to the serialized representation of the message have been
- successfully delivered to the stream.
- */
- bool
- is_done() const
- {
- return s_ == do_complete;
- }
-
- /** Write some serialized message data to the stream.
-
- This function is used to write serialized message data to the
- stream. The function call will block until one of the following
- conditions is true:
-
- @li One or more bytes have been transferred.
-
- @li An error occurs on the stream.
-
- In order to completely serialize a message, this function
- should be called until @ref is_done returns `true`. If the
- semantics of the message indicate that the connection should
- be closed after the message is sent, the error delivered from
- this call will be `boost::asio::error::eof`.
-
- @param stream The stream to write to. This type must
- satisfy the requirements of @b SyncWriteStream.
-
- @throws system_error Thrown on failure.
- */
- template
- void
- write_some(SyncWriteStream& stream);
-
- /** Write some serialized message data to the stream.
-
- This function is used to write serialized message data to the
- stream. The function call will block until one of the following
- conditions is true:
-
- @li One or more bytes have been transferred.
-
- @li An error occurs on the stream.
-
- In order to completely serialize a message, this function
- should be called until @ref is_done returns `true`. If the
- semantics of the message indicate that the connection should
- be closed after the message is sent, the error delivered from
- this call will be `boost::asio::error::eof`.
-
- @param stream The stream to write to. This type must
- satisfy the requirements of @b SyncWriteStream.
-
- @param ec Set to indicate what error occurred, if any.
- */
- template
- void
- write_some(SyncWriteStream& stream, error_code &ec);
-
- /** Start an asynchronous write of some serialized message data.
-
- This function is used to asynchronously write serialized
- message data to the stream. The function call always returns
- immediately. The asynchronous operation will continue until
- one of the following conditions is true:
-
- @li One or more bytes have been transferred.
-
- @li An error occurs on the stream.
-
- In order to completely serialize a message, this function
- should be called until @ref is_done returns `true`. If the
- semantics of the message indicate that the connection should
- be closed after the message is sent, the error delivered from
- this call will be `boost::asio::error::eof`.
-
- @param stream The stream to write to. This type must
- satisfy the requirements of @b SyncWriteStream.
-
- @param handler The handler to be called when the request
- completes. Copies will be made of the handler as required. The
- equivalent function signature of the handler must be:
- @code void handler(
- error_code const& ec // Result of operation
- ); @endcode
- Regardless of whether the asynchronous operation completes
- immediately or not, the handler will not be invoked from within
- this function. Invocation of the handler will be performed in a
- manner equivalent to using `boost::asio::io_service::post`.
- */
- template
+ @see @ref write_some, @ref serializer
+*/
+template
#if BEAST_DOXYGEN
void_or_deduced
#else
- async_return_type
+async_return_type
#endif
- async_write_some(AsyncWriteStream& stream,
+async_write_some(AsyncWriteStream& stream, serializer<
+ isRequest, Body, Fields, Decorator, Allocator>& sr,
WriteHandler&& handler);
-};
-
-/** Return a stateful object to serialize an HTTP message.
-
- This convenience function makes it easier to declare
- the variable for a given message.
-*/
-template<
- bool isRequest, class Body, class Fields,
- class Decorator = empty_decorator,
- class Allocator = std::allocator>
-inline
-serializer::type,
- typename std::decay::type>
-make_serializer(message const& m,
- Decorator const& decorator = Decorator{},
- Allocator const& allocator = Allocator{})
-{
- return serializer::type,
- typename std::decay::type>{
- m, decorator, allocator};
-}
-
-//------------------------------------------------------------------------------
/** Write a HTTP/1 message to a stream.
diff --git a/test/Jamfile b/test/Jamfile
index 38995912..2bb0b928 100644
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -52,6 +52,7 @@ unit-test http-tests :
http/message_parser.cpp
http/read.cpp
http/rfc7230.cpp
+ http/serializer.cpp
http/string_body.cpp
http/type_traits.cpp
http/write.cpp
diff --git a/test/http/CMakeLists.txt b/test/http/CMakeLists.txt
index ea3255ec..b95f3b69 100644
--- a/test/http/CMakeLists.txt
+++ b/test/http/CMakeLists.txt
@@ -22,6 +22,7 @@ add_executable (http-tests
message_parser.cpp
read.cpp
rfc7230.cpp
+ serializer.cpp
string_body.cpp
type_traits.cpp
write.cpp
diff --git a/test/http/design.cpp b/test/http/design.cpp
index 7c07fcff..29145fd7 100644
--- a/test/http/design.cpp
+++ b/test/http/design.cpp
@@ -77,11 +77,11 @@ public:
req.body = "Hello, world!";
// send header
- auto ws = make_serializer(req);
+ auto sr = make_serializer(req);
for(;;)
{
- ws.async_write_some(stream, yield);
- if(ws.is_header_done())
+ async_write_some(stream, sr, yield);
+ if(sr.is_header_done())
break;
}
@@ -92,8 +92,8 @@ public:
}
// send body
- while(! ws.is_done())
- ws.async_write_some(stream, yield);
+ while(! sr.is_done())
+ async_write_some(stream, sr, yield);
}
void
@@ -162,7 +162,7 @@ public:
m.target("/");
m.fields.insert("User-Agent", "test");
m.fields.insert("Content-Length", s.size());
- auto ws = make_serializer(m);
+ auto sr = make_serializer(m);
error_code ec;
for(;;)
{
@@ -171,7 +171,7 @@ public:
m.body.second = s.size() > 3;
for(;;)
{
- ws.write_some(p.client, ec);
+ write_some(p.client, sr, ec);
if(ec == error::need_more)
{
ec = {};
@@ -179,12 +179,12 @@ public:
}
if(! BEAST_EXPECTS(! ec, ec.message()))
return;
- if(ws.is_done())
+ if(sr.is_done())
break;
}
s.erase(s.begin(), s.begin() +
boost::asio::buffer_size(*m.body.first));
- if(ws.is_done())
+ if(sr.is_done())
break;
}
BEAST_EXPECT(p.server.str() ==
diff --git a/test/http/serializer.cpp b/test/http/serializer.cpp
new file mode 100644
index 00000000..3fb95423
--- /dev/null
+++ b/test/http/serializer.cpp
@@ -0,0 +1,9 @@
+//
+// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+// Test that header file is self-contained.
+#include
diff --git a/test/http/write.cpp b/test/http/write.cpp
index b0ca6fac..e46045e6 100644
--- a/test/http/write.cpp
+++ b/test/http/write.cpp
@@ -765,15 +765,15 @@ public:
isRequest, Body, Fields> const& m, error_code& ec,
Decorator const& decorator = Decorator{})
{
- auto ws = make_serializer(m, decorator);
+ auto sr = make_serializer(m, decorator);
for(;;)
{
stream.nwrite = 0;
- ws.write_some(stream, ec);
+ write_some(stream, sr, ec);
if(ec)
return;
BEAST_EXPECT(stream.nwrite <= 1);
- if(ws.is_done())
+ if(sr.is_done())
break;
}
}
@@ -787,15 +787,15 @@ public:
error_code& ec, yield_context yield,
Decorator const& decorator = Decorator{})
{
- auto ws = make_serializer(m);
+ auto sr = make_serializer(m);
for(;;)
{
stream.nwrite = 0;
- ws.async_write_some(stream, yield[ec]);
+ async_write_some(stream, sr, yield[ec]);
if(ec)
return;
BEAST_EXPECT(stream.nwrite <= 1);
- if(ws.is_done())
+ if(sr.is_done())
break;
}
}
@@ -857,12 +857,12 @@ public:
{
auto m = m0;
error_code ec;
- serializer w{m};
- w.split(true);
+ serializer sr{m};
+ sr.split(true);
for(;;)
{
- w.write_some(p.client);
- if(w.is_header_done())
+ write_some(p.client, sr);
+ if(sr.is_header_done())
break;
}
BEAST_EXPECT(! m.body.read);
@@ -871,12 +871,12 @@ public:
{
auto m = m0;
error_code ec;
- serializer w{m};
- w.split(true);
+ serializer sr{m};
+ sr.split(true);
for(;;)
{
- w.async_write_some(p.client, yield);
- if(w.is_header_done())
+ async_write_some(p.client, sr, yield);
+ if(sr.is_header_done())
break;
}
BEAST_EXPECT(! m.body.read);
@@ -921,12 +921,12 @@ public:
auto m = m0;
error_code ec;
test::string_ostream so{get_io_service(), 3};
- serializer w{m};
- w.split(true);
+ serializer sr{m};
+ sr.split(true);
for(;;)
{
- w.write_some(p.client);
- if(w.is_header_done())
+ write_some(p.client, sr);
+ if(sr.is_header_done())
break;
}
BEAST_EXPECT(! m.body.read);
@@ -935,12 +935,12 @@ public:
{
auto m = m0;
error_code ec;
- serializer w{m};
- w.split(true);
+ serializer sr{m};
+ sr.split(true);
for(;;)
{
- w.async_write_some(p.client, yield);
- if(w.is_header_done())
+ async_write_some(p.client, sr, yield);
+ if(sr.is_header_done())
break;
}
BEAST_EXPECT(! m.body.read);
@@ -969,13 +969,13 @@ public:
m.body.first = boost::none;
m.body.second = true;
- auto w = make_serializer(m);
+ auto sr = make_serializer(m);
// send the header first, so the
// other end gets it right away
for(;;)
{
- w.write_some(output, ec);
+ write_some(output, sr, ec);
if(ec == error::need_more)
{
ec = {};
@@ -1008,7 +1008,7 @@ public:
// write to output
for(;;)
{
- w.write_some(output, ec);
+ write_some(output, sr, ec);
if(ec == error::need_more)
{
ec = {};
@@ -1016,7 +1016,7 @@ public:
}
if(ec)
return;
- if(w.is_done())
+ if(sr.is_done())
goto is_done;
}
b.consume(b.size());