2017-07-20 08:01:46 -07:00
|
|
|
//
|
2017-02-06 20:07:03 -05:00
|
|
|
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
2017-07-20 08:01:46 -07:00
|
|
|
//
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef BEAST_HTTP_IMPL_WRITE_IPP
|
|
|
|
#define BEAST_HTTP_IMPL_WRITE_IPP
|
|
|
|
|
2017-05-28 09:05:29 -07:00
|
|
|
#include <beast/http/type_traits.hpp>
|
2017-05-08 12:41:45 -07:00
|
|
|
#include <beast/http/error.hpp>
|
2016-05-07 14:57:15 -04:00
|
|
|
#include <beast/core/buffer_cat.hpp>
|
2017-05-08 12:41:45 -07:00
|
|
|
#include <beast/core/buffer_prefix.hpp>
|
2016-05-07 14:57:15 -04:00
|
|
|
#include <beast/core/bind_handler.hpp>
|
2017-05-03 15:29:23 -07:00
|
|
|
#include <beast/core/ostream.hpp>
|
2017-05-08 12:41:45 -07:00
|
|
|
#include <beast/core/handler_alloc.hpp>
|
2017-01-02 13:29:48 -05:00
|
|
|
#include <beast/core/handler_ptr.hpp>
|
2017-05-04 15:40:07 -07:00
|
|
|
#include <beast/core/multi_buffer.hpp>
|
2017-05-10 12:03:00 -07:00
|
|
|
#include <beast/core/type_traits.hpp>
|
2016-11-07 06:33:03 -05:00
|
|
|
#include <beast/core/detail/sync_ostream.hpp>
|
2017-05-14 09:25:43 -07:00
|
|
|
#include <boost/asio/handler_alloc_hook.hpp>
|
|
|
|
#include <boost/asio/handler_continuation_hook.hpp>
|
|
|
|
#include <boost/asio/handler_invoke_hook.hpp>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <boost/asio/write.hpp>
|
2017-05-22 15:30:12 -07:00
|
|
|
#include <boost/throw_exception.hpp>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <condition_variable>
|
|
|
|
#include <mutex>
|
2016-04-29 06:04:40 -04:00
|
|
|
#include <ostream>
|
|
|
|
#include <sstream>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
namespace beast {
|
|
|
|
namespace http {
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
2017-05-03 15:29:23 -07:00
|
|
|
template<class Fields>
|
2016-05-01 11:14:10 -04:00
|
|
|
void
|
2017-05-03 15:29:23 -07:00
|
|
|
write_start_line(std::ostream& os,
|
2016-11-10 05:34:49 -05:00
|
|
|
header<true, Fields> const& msg)
|
2016-05-01 11:14:10 -04:00
|
|
|
{
|
2016-11-07 13:51:10 -05:00
|
|
|
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
2017-05-03 15:29:23 -07:00
|
|
|
os << msg.method() << " " << msg.target();
|
2016-10-10 09:00:57 -04:00
|
|
|
switch(msg.version)
|
|
|
|
{
|
2017-05-03 15:29:23 -07:00
|
|
|
case 10: os << " HTTP/1.0\r\n"; break;
|
|
|
|
case 11: os << " HTTP/1.1\r\n"; break;
|
2016-10-10 09:00:57 -04:00
|
|
|
}
|
2016-05-01 11:14:10 -04:00
|
|
|
}
|
|
|
|
|
2017-05-03 15:29:23 -07:00
|
|
|
template<class Fields>
|
2016-05-01 11:14:10 -04:00
|
|
|
void
|
2017-05-03 15:29:23 -07:00
|
|
|
write_start_line(std::ostream& os,
|
2016-11-10 05:34:49 -05:00
|
|
|
header<false, Fields> const& msg)
|
2016-05-01 11:14:10 -04:00
|
|
|
{
|
2016-11-07 13:51:10 -05:00
|
|
|
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
2016-10-10 09:00:57 -04:00
|
|
|
switch(msg.version)
|
|
|
|
{
|
2017-05-03 15:29:23 -07:00
|
|
|
case 10: os << "HTTP/1.0 "; break;
|
|
|
|
case 11: os << "HTTP/1.1 "; break;
|
2016-10-10 09:00:57 -04:00
|
|
|
}
|
2017-05-03 15:29:23 -07:00
|
|
|
os << msg.status << " " << msg.reason() << "\r\n";
|
2016-05-01 11:14:10 -04:00
|
|
|
}
|
|
|
|
|
2017-05-03 15:29:23 -07:00
|
|
|
template<class FieldSequence>
|
2016-05-01 11:14:10 -04:00
|
|
|
void
|
2017-05-03 15:29:23 -07:00
|
|
|
write_fields(std::ostream& os,
|
|
|
|
FieldSequence const& fields)
|
2016-05-01 11:14:10 -04:00
|
|
|
{
|
|
|
|
//static_assert(is_FieldSequence<FieldSequence>::value,
|
|
|
|
// "FieldSequence requirements not met");
|
|
|
|
for(auto const& field : fields)
|
|
|
|
{
|
2017-05-02 15:49:22 -07:00
|
|
|
auto const name = field.name();
|
|
|
|
BOOST_ASSERT(! name.empty());
|
|
|
|
if(name[0] == ':')
|
|
|
|
continue;
|
2017-05-03 15:29:23 -07:00
|
|
|
os << field.name() << ": " << field.value() << "\r\n";
|
2016-05-01 11:14:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-07 13:51:10 -05:00
|
|
|
} // detail
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
template<bool isRequest, class Body, class Fields,
|
|
|
|
class Decorator, class Allocator>
|
2016-11-07 13:51:10 -05:00
|
|
|
template<class Stream, class Handler>
|
2017-05-08 12:41:45 -07:00
|
|
|
class serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>::async_op
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>& w_;
|
|
|
|
Stream& s_;
|
|
|
|
Handler h_;
|
|
|
|
bool cont_;
|
2016-11-07 13:51:10 -05:00
|
|
|
|
|
|
|
public:
|
2017-05-08 12:41:45 -07:00
|
|
|
async_op(async_op&&) = default;
|
|
|
|
async_op(async_op const&) = default;
|
|
|
|
|
|
|
|
async_op(Handler&& h, Stream& s,
|
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>& w)
|
|
|
|
: w_(w)
|
|
|
|
, s_(s)
|
|
|
|
, h_(std::move(h))
|
|
|
|
{
|
|
|
|
using boost::asio::asio_handler_is_continuation;
|
|
|
|
cont_ = asio_handler_is_continuation(
|
|
|
|
std::addressof(h_));
|
|
|
|
}
|
2016-11-07 13:51:10 -05:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
async_op(Handler const& h, Stream& s,
|
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>& w)
|
|
|
|
: w_(w)
|
|
|
|
, s_(s)
|
|
|
|
, h_(h)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
using boost::asio::asio_handler_is_continuation;
|
|
|
|
cont_ = asio_handler_is_continuation(
|
|
|
|
std::addressof(h_));
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-05-08 12:41:45 -07:00
|
|
|
operator()(error_code ec, std::size_t
|
|
|
|
bytes_transferred, bool again = true);
|
2016-11-07 13:51:10 -05:00
|
|
|
|
|
|
|
friend
|
|
|
|
void* asio_handler_allocate(
|
2017-05-08 12:41:45 -07:00
|
|
|
std::size_t size, async_op* op)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-14 09:25:43 -07:00
|
|
|
using boost::asio::asio_handler_allocate;
|
|
|
|
return asio_handler_allocate(
|
2017-05-08 12:41:45 -07:00
|
|
|
size, std::addressof(op->h_));
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
friend
|
|
|
|
void asio_handler_deallocate(
|
2017-05-08 12:41:45 -07:00
|
|
|
void* p, std::size_t size, async_op* op)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-14 09:25:43 -07:00
|
|
|
using boost::asio::asio_handler_deallocate;
|
|
|
|
asio_handler_deallocate(
|
2017-05-08 12:41:45 -07:00
|
|
|
p, size, std::addressof(op->h_));
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
friend
|
2017-05-08 12:41:45 -07:00
|
|
|
bool asio_handler_is_continuation(async_op* op)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
return op->cont_;
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class Function>
|
|
|
|
friend
|
2017-05-08 12:41:45 -07:00
|
|
|
void asio_handler_invoke(
|
|
|
|
Function&& f, async_op* op)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-14 09:25:43 -07:00
|
|
|
using boost::asio::asio_handler_invoke;
|
|
|
|
asio_handler_invoke(
|
2017-05-08 12:41:45 -07:00
|
|
|
f, std::addressof(op->h_));
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
template<bool isRequest, class Body, class Fields,
|
|
|
|
class Decorator, class Allocator>
|
2016-11-07 13:51:10 -05:00
|
|
|
template<class Stream, class Handler>
|
|
|
|
void
|
2017-05-08 12:41:45 -07:00
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>::
|
|
|
|
async_op<Stream, Handler>::
|
|
|
|
operator()(error_code ec,
|
|
|
|
std::size_t bytes_transferred, bool again)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
cont_ = again || cont_;
|
|
|
|
using boost::asio::buffer_size;
|
|
|
|
if(ec)
|
|
|
|
goto upcall;
|
|
|
|
switch(w_.s_)
|
|
|
|
{
|
|
|
|
case do_init:
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
if(w_.split_)
|
|
|
|
goto go_header_only;
|
2017-05-28 09:05:29 -07:00
|
|
|
w_.rd_.emplace(w_.m_);
|
|
|
|
w_.rd_->init(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
return s_.get_io_service().post(
|
|
|
|
bind_handler(std::move(*this), ec, 0));
|
2017-05-28 09:05:29 -07:00
|
|
|
auto result = w_.rd_->get(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
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]]
|
|
|
|
}
|
|
|
|
|
|
|
|
case do_header:
|
|
|
|
w_.s_ = do_header + 1;
|
|
|
|
return s_.async_write_some(
|
|
|
|
buffer_prefix(w_.limit_,
|
|
|
|
boost::get<cb0_t>(w_.v_)),
|
|
|
|
std::move(*this));
|
|
|
|
|
|
|
|
case do_header + 1:
|
|
|
|
boost::get<cb0_t>(w_.v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(
|
|
|
|
boost::get<cb0_t>(w_.v_)) > 0)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
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)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
w_.s_ = do_header_only;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// VFALCO delete b_?
|
|
|
|
w_.header_done_ = true;
|
|
|
|
if(! is_deferred::value)
|
|
|
|
goto go_complete;
|
2017-05-28 09:05:29 -07:00
|
|
|
BOOST_ASSERT(! w_.rd_);
|
|
|
|
w_.rd_.emplace(w_.m_);
|
|
|
|
w_.rd_->init(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
goto upcall;
|
|
|
|
w_.s_ = do_body;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case do_body:
|
|
|
|
{
|
2017-05-28 09:05:29 -07:00
|
|
|
auto result = w_.rd_->get(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
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));
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
2017-05-08 12:41:45 -07:00
|
|
|
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<cb1_t>(w_.v_)),
|
|
|
|
std::move(*this));
|
|
|
|
|
|
|
|
case do_body + 2:
|
|
|
|
boost::get<cb1_t>(w_.v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(
|
|
|
|
boost::get<cb1_t>(w_.v_)) > 0)
|
|
|
|
{
|
|
|
|
w_.s_ = do_body + 1;
|
|
|
|
break;
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
2017-05-08 12:41:45 -07:00
|
|
|
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;
|
2017-05-28 09:05:29 -07:00
|
|
|
w_.rd_.emplace(w_.m_);
|
|
|
|
w_.rd_->init(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
return s_.get_io_service().post(
|
|
|
|
bind_handler(std::move(*this), ec, 0));
|
2017-05-28 09:05:29 -07:00
|
|
|
auto result = w_.rd_->get(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
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]]
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
2017-05-08 12:41:45 -07:00
|
|
|
|
|
|
|
case do_header_c:
|
|
|
|
w_.s_ = do_header_c + 1;
|
|
|
|
return s_.async_write_some(
|
|
|
|
buffer_prefix(w_.limit_,
|
|
|
|
boost::get<ch0_t>(w_.v_)),
|
|
|
|
std::move(*this));
|
|
|
|
|
|
|
|
case do_header_c + 1:
|
|
|
|
boost::get<ch0_t>(w_.v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(
|
|
|
|
boost::get<ch0_t>(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;
|
|
|
|
}
|
2017-05-28 09:05:29 -07:00
|
|
|
BOOST_ASSERT(! w_.rd_);
|
|
|
|
w_.rd_.emplace(w_.m_);
|
|
|
|
w_.rd_->init(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
goto upcall;
|
|
|
|
w_.s_ = do_body_c;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case do_body_c:
|
|
|
|
{
|
2017-05-28 09:05:29 -07:00
|
|
|
auto result = w_.rd_->get(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
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<ch1_t>(w_.v_)),
|
|
|
|
std::move(*this));
|
|
|
|
|
|
|
|
case do_body_c + 2:
|
|
|
|
boost::get<ch1_t>(w_.v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(
|
|
|
|
boost::get<ch1_t>(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<ch2_t>(w_.v_)),
|
|
|
|
std::move(*this));
|
|
|
|
|
|
|
|
case do_final_c + 2:
|
|
|
|
boost::get<ch2_t>(w_.v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(boost::get<ch2_t>(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:
|
|
|
|
h_(ec);
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
//------------------------------------------------------------------------------
|
2016-11-07 13:51:10 -05:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
template<bool isRequest, class Body, class Fields,
|
|
|
|
class Decorator, class Allocator>
|
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>::
|
|
|
|
serializer(message<isRequest, Body, Fields> 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<bool isRequest, class Body, class Fields,
|
|
|
|
class Decorator, class Allocator>
|
|
|
|
template<class SyncWriteStream>
|
2016-11-07 13:51:10 -05:00
|
|
|
void
|
2017-05-08 12:41:45 -07:00
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>::
|
|
|
|
write_some(SyncWriteStream& stream)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
static_assert(
|
|
|
|
is_sync_write_stream<SyncWriteStream>::value,
|
2016-11-07 13:51:10 -05:00
|
|
|
"SyncWriteStream requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body<Body>::value,
|
2017-05-08 12:41:45 -07:00
|
|
|
"Body requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body_reader<Body>::value,
|
|
|
|
"BodyReader requirements not met");
|
2016-11-07 13:51:10 -05:00
|
|
|
error_code ec;
|
2017-05-08 12:41:45 -07:00
|
|
|
write_some(stream, ec);
|
2016-11-07 13:51:10 -05:00
|
|
|
if(ec)
|
2017-05-22 15:30:12 -07:00
|
|
|
BOOST_THROW_EXCEPTION(system_error{ec});
|
2016-11-07 13:51:10 -05:00
|
|
|
}
|
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
template<bool isRequest, class Body, class Fields,
|
|
|
|
class Decorator, class Allocator>
|
|
|
|
template<class SyncWriteStream>
|
2016-11-07 13:51:10 -05:00
|
|
|
void
|
2017-05-08 12:41:45 -07:00
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>::
|
|
|
|
write_some(SyncWriteStream& stream, error_code &ec)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
static_assert(
|
|
|
|
is_sync_write_stream<SyncWriteStream>::value,
|
2016-11-07 13:51:10 -05:00
|
|
|
"SyncWriteStream requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body<Body>::value,
|
2017-05-08 12:41:45 -07:00
|
|
|
"Body requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body_reader<Body>::value,
|
|
|
|
"BodyReader requirements not met");
|
2017-05-08 12:41:45 -07:00
|
|
|
|
|
|
|
using boost::asio::buffer_size;
|
|
|
|
switch(s_)
|
|
|
|
{
|
|
|
|
case do_init:
|
2017-05-03 15:29:23 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
if(split_)
|
|
|
|
goto go_header_only;
|
2017-05-28 09:05:29 -07:00
|
|
|
rd_.emplace(m_);
|
|
|
|
rd_->init(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
return;
|
2017-05-28 09:05:29 -07:00
|
|
|
auto result = rd_->get(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
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]]
|
2017-05-03 15:29:23 -07:00
|
|
|
}
|
2016-11-07 13:51:10 -05:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
case do_header:
|
2017-05-03 15:29:23 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
auto bytes_transferred =
|
|
|
|
stream.write_some(
|
|
|
|
buffer_prefix(limit_,
|
|
|
|
boost::get<cb0_t>(v_)), ec);
|
|
|
|
if(ec)
|
|
|
|
return;
|
|
|
|
boost::get<cb0_t>(v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(boost::get<cb0_t>(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;
|
2017-05-03 15:29:23 -07:00
|
|
|
}
|
2016-11-07 13:51:10 -05:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
go_header_only:
|
|
|
|
s_ = do_header_only;
|
|
|
|
case do_header_only:
|
2016-05-01 11:14:10 -04:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
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;
|
2017-05-28 09:05:29 -07:00
|
|
|
BOOST_ASSERT(! rd_);
|
|
|
|
rd_.emplace(m_);
|
|
|
|
rd_->init(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
return;
|
|
|
|
s_ = do_body;
|
|
|
|
break;
|
2016-05-01 11:14:10 -04:00
|
|
|
}
|
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
case do_body:
|
2016-05-01 11:14:10 -04:00
|
|
|
{
|
2017-05-28 09:05:29 -07:00
|
|
|
auto result = rd_->get(ec);
|
2016-05-01 11:14:10 -04:00
|
|
|
if(ec)
|
|
|
|
return;
|
2017-05-08 12:41:45 -07:00
|
|
|
if(! result)
|
|
|
|
goto go_complete;
|
|
|
|
more_ = result->second;
|
|
|
|
v_ = cb1_t{result->first};
|
|
|
|
s_ = do_body + 1;
|
|
|
|
// [[fallthrough]]
|
2016-05-01 11:14:10 -04:00
|
|
|
}
|
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
case do_body + 1:
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
auto bytes_transferred =
|
|
|
|
stream.write_some(buffer_prefix(
|
|
|
|
limit_, boost::get<cb1_t>(v_)), ec);
|
|
|
|
if(ec)
|
|
|
|
return;
|
|
|
|
boost::get<cb1_t>(v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(boost::get<cb1_t>(v_)) > 0)
|
|
|
|
break;
|
|
|
|
v_ = boost::blank{};
|
|
|
|
if(! more_)
|
|
|
|
goto go_complete;
|
|
|
|
s_ = do_body;
|
|
|
|
break;
|
|
|
|
}
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
case do_init_c:
|
|
|
|
{
|
|
|
|
if(split_)
|
|
|
|
goto go_header_only_c;
|
2017-05-28 09:05:29 -07:00
|
|
|
rd_.emplace(m_);
|
|
|
|
rd_->init(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
return;
|
2017-05-28 09:05:29 -07:00
|
|
|
auto result = rd_->get(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
// Can't use need_more when ! is_deferred
|
|
|
|
BOOST_ASSERT(ec != error::need_more);
|
|
|
|
return;
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
2017-05-08 12:41:45 -07:00
|
|
|
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]]
|
|
|
|
}
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
case do_header_c:
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
auto bytes_transferred =
|
|
|
|
stream.write_some(buffer_prefix(
|
|
|
|
limit_, boost::get<ch0_t>(v_)), ec);
|
|
|
|
if(ec)
|
|
|
|
return;
|
|
|
|
boost::get<ch0_t>(v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(boost::get<ch0_t>(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;
|
|
|
|
}
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
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)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
s_ = do_final_c;
|
|
|
|
break;
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
2017-05-28 09:05:29 -07:00
|
|
|
BOOST_ASSERT(! rd_);
|
|
|
|
rd_.emplace(m_);
|
|
|
|
rd_->init(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
return;
|
|
|
|
s_ = do_body_c;
|
|
|
|
break;
|
|
|
|
}
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
case do_body_c:
|
|
|
|
{
|
2017-05-28 09:05:29 -07:00
|
|
|
auto result = rd_->get(ec);
|
2017-05-08 12:41:45 -07:00
|
|
|
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]]
|
|
|
|
}
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
case do_body_c + 1:
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
auto bytes_transferred =
|
|
|
|
stream.write_some(buffer_prefix(
|
|
|
|
limit_, boost::get<ch1_t>(v_)), ec);
|
|
|
|
if(ec)
|
|
|
|
return;
|
|
|
|
boost::get<ch1_t>(v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(boost::get<ch1_t>(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<ch2_t>(v_)), ec);
|
|
|
|
if(ec)
|
|
|
|
return;
|
|
|
|
boost::get<ch2_t>(v_).consume(
|
|
|
|
bytes_transferred);
|
|
|
|
if(buffer_size(boost::get<ch2_t>(v_)) > 0)
|
|
|
|
break;
|
|
|
|
v_ = boost::blank{};
|
|
|
|
goto go_complete;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
case do_complete:
|
|
|
|
BOOST_ASSERT(false);
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
go_complete:
|
|
|
|
s_ = do_complete;
|
|
|
|
if(close_)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
// VFALCO TODO Decide on an error code
|
|
|
|
ec = boost::asio::error::eof;
|
|
|
|
return;
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
2017-05-08 12:41:45 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<bool isRequest, class Body, class Fields,
|
|
|
|
class Decorator, class Allocator>
|
|
|
|
template<class AsyncWriteStream, class WriteHandler>
|
|
|
|
async_return_type<WriteHandler, void(error_code)>
|
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
Decorator, Allocator>::
|
|
|
|
async_write_some(AsyncWriteStream& stream,
|
|
|
|
WriteHandler&& handler)
|
|
|
|
{
|
|
|
|
static_assert(is_async_write_stream<AsyncWriteStream>::value,
|
|
|
|
"AsyncWriteStream requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body<Body>::value,
|
2017-05-08 12:41:45 -07:00
|
|
|
"Body requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body_reader<Body>::value,
|
|
|
|
"BodyReader requirements not met");
|
2017-05-08 12:41:45 -07:00
|
|
|
async_completion<WriteHandler,
|
|
|
|
void(error_code)> init{handler};
|
|
|
|
async_op<AsyncWriteStream, handler_type<
|
|
|
|
WriteHandler, void(error_code)>>{
|
|
|
|
init.completion_handler, stream, *this}(
|
|
|
|
error_code{}, 0, false);
|
|
|
|
|
|
|
|
return init.result.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-05-08 12:41:45 -07:00
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
template<class Stream, class Handler,
|
|
|
|
bool isRequest, class Body, class Fields>
|
|
|
|
class write_op
|
|
|
|
{
|
|
|
|
struct data
|
|
|
|
{
|
|
|
|
int state = 0;
|
|
|
|
Stream& s;
|
|
|
|
serializer<isRequest, Body, Fields,
|
|
|
|
empty_decorator, handler_alloc<char, Handler>> ws;
|
|
|
|
|
|
|
|
data(Handler& h, Stream& s_, message<
|
|
|
|
isRequest, Body, Fields> const& m_)
|
|
|
|
: s(s_)
|
|
|
|
, ws(m_, empty_decorator{},
|
|
|
|
handler_alloc<char, Handler>{h})
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-01-02 13:29:48 -05:00
|
|
|
handler_ptr<data, Handler> d_;
|
2017-07-20 08:01:46 -07:00
|
|
|
|
|
|
|
public:
|
|
|
|
write_op(write_op&&) = default;
|
|
|
|
write_op(write_op const&) = default;
|
|
|
|
|
|
|
|
template<class DeducedHandler, class... Args>
|
|
|
|
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
2017-01-29 19:46:17 -05:00
|
|
|
: d_(std::forward<DeducedHandler>(h),
|
|
|
|
s, std::forward<Args>(args)...)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-05-08 12:41:45 -07:00
|
|
|
operator()(error_code ec);
|
2017-07-20 08:01:46 -07:00
|
|
|
|
|
|
|
friend
|
|
|
|
void* asio_handler_allocate(
|
|
|
|
std::size_t size, write_op* op)
|
|
|
|
{
|
2017-05-14 09:25:43 -07:00
|
|
|
using boost::asio::asio_handler_allocate;
|
|
|
|
return asio_handler_allocate(
|
|
|
|
size, std::addressof(op->d_.handler()));
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
friend
|
|
|
|
void asio_handler_deallocate(
|
|
|
|
void* p, std::size_t size, write_op* op)
|
|
|
|
{
|
2017-05-14 09:25:43 -07:00
|
|
|
using boost::asio::asio_handler_deallocate;
|
|
|
|
asio_handler_deallocate(
|
|
|
|
p, size, std::addressof(op->d_.handler()));
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
friend
|
|
|
|
bool asio_handler_is_continuation(write_op* op)
|
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
using boost::asio::asio_handler_is_continuation;
|
|
|
|
return op->d_->state == 2 ||
|
|
|
|
asio_handler_is_continuation(
|
|
|
|
std::addressof(op->d_.handler()));
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
2016-08-26 08:01:44 -04:00
|
|
|
template<class Function>
|
2017-07-20 08:01:46 -07:00
|
|
|
friend
|
|
|
|
void asio_handler_invoke(Function&& f, write_op* op)
|
|
|
|
{
|
2017-05-14 09:25:43 -07:00
|
|
|
using boost::asio::asio_handler_invoke;
|
|
|
|
asio_handler_invoke(
|
|
|
|
f, std::addressof(op->d_.handler()));
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<class Stream, class Handler,
|
2016-11-10 05:34:49 -05:00
|
|
|
bool isRequest, class Body, class Fields>
|
2017-07-20 08:01:46 -07:00
|
|
|
void
|
2016-11-10 05:34:49 -05:00
|
|
|
write_op<Stream, Handler, isRequest, Body, Fields>::
|
2017-05-08 12:41:45 -07:00
|
|
|
operator()(error_code ec)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
auto& d = *d_;
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ec)
|
|
|
|
goto upcall;
|
|
|
|
switch(d.state)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
case 0:
|
|
|
|
d.state = 1;
|
|
|
|
return d.ws.async_write_some(d.s, std::move(*this));
|
|
|
|
case 1:
|
|
|
|
d.state = 2;
|
|
|
|
case 2:
|
|
|
|
if(d.ws.is_done())
|
2017-07-20 08:01:46 -07:00
|
|
|
break;
|
2017-05-08 12:41:45 -07:00
|
|
|
return d.ws.async_write_some(d.s, std::move(*this));
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
2017-05-08 12:41:45 -07:00
|
|
|
upcall:
|
2017-01-02 13:29:48 -05:00
|
|
|
d_.invoke(ec);
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
} // detail
|
|
|
|
|
|
|
|
template<class SyncWriteStream,
|
2016-11-10 05:34:49 -05:00
|
|
|
bool isRequest, class Body, class Fields>
|
2017-07-20 08:01:46 -07:00
|
|
|
void
|
|
|
|
write(SyncWriteStream& stream,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields> const& msg)
|
2016-05-01 11:14:10 -04:00
|
|
|
{
|
2017-05-10 12:03:00 -07:00
|
|
|
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
2016-05-01 11:14:10 -04:00
|
|
|
"SyncWriteStream requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body<Body>::value,
|
2016-10-15 13:40:17 -04:00
|
|
|
"Body requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body_reader<Body>::value,
|
|
|
|
"BodyReader requirements not met");
|
2016-05-01 11:14:10 -04:00
|
|
|
error_code ec;
|
|
|
|
write(stream, msg, ec);
|
|
|
|
if(ec)
|
2017-05-22 15:30:12 -07:00
|
|
|
BOOST_THROW_EXCEPTION(system_error{ec});
|
2016-05-01 11:14:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class SyncWriteStream,
|
2016-11-10 05:34:49 -05:00
|
|
|
bool isRequest, class Body, class Fields>
|
2016-05-01 11:14:10 -04:00
|
|
|
void
|
|
|
|
write(SyncWriteStream& stream,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields> const& msg,
|
2016-10-04 18:00:11 -04:00
|
|
|
error_code& ec)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-10 12:03:00 -07:00
|
|
|
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
2016-04-29 06:04:40 -04:00
|
|
|
"SyncWriteStream requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body<Body>::value,
|
2016-10-15 13:40:17 -04:00
|
|
|
"Body requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body_reader<Body>::value,
|
|
|
|
"BodyReader requirements not met");
|
2017-05-28 14:36:38 -07:00
|
|
|
auto ws = make_serializer(msg);
|
2017-05-08 12:41:45 -07:00
|
|
|
for(;;)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
ws.write_some(stream, ec);
|
2017-07-20 08:01:46 -07:00
|
|
|
if(ec)
|
|
|
|
return;
|
2017-05-08 12:41:45 -07:00
|
|
|
if(ws.is_done())
|
|
|
|
break;
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class AsyncWriteStream,
|
2016-11-10 05:34:49 -05:00
|
|
|
bool isRequest, class Body, class Fields,
|
2017-07-20 08:01:46 -07:00
|
|
|
class WriteHandler>
|
2017-05-12 17:13:03 -07:00
|
|
|
async_return_type<
|
|
|
|
WriteHandler, void(error_code)>
|
2017-07-20 08:01:46 -07:00
|
|
|
async_write(AsyncWriteStream& stream,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields> const& msg,
|
2017-07-20 08:01:46 -07:00
|
|
|
WriteHandler&& handler)
|
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
static_assert(
|
|
|
|
is_async_write_stream<AsyncWriteStream>::value,
|
2016-05-28 09:23:54 -04:00
|
|
|
"AsyncWriteStream requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body<Body>::value,
|
2016-10-15 13:40:17 -04:00
|
|
|
"Body requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body_reader<Body>::value,
|
|
|
|
"BodyReader requirements not met");
|
2017-05-06 12:36:40 -07:00
|
|
|
async_completion<WriteHandler,
|
|
|
|
void(error_code)> init{handler};
|
2017-05-12 17:13:03 -07:00
|
|
|
detail::write_op<AsyncWriteStream, handler_type<
|
2017-05-08 12:41:45 -07:00
|
|
|
WriteHandler, void(error_code)>,
|
|
|
|
isRequest, Body, Fields>{
|
|
|
|
init.completion_handler, stream, msg}(
|
|
|
|
error_code{});
|
2017-05-06 12:36:40 -07:00
|
|
|
return init.result.get();
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
2016-11-07 13:51:10 -05:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Fields>
|
2016-11-07 13:51:10 -05:00
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& os,
|
2016-11-10 05:34:49 -05:00
|
|
|
header<isRequest, Fields> const& msg)
|
2016-11-07 13:51:10 -05:00
|
|
|
{
|
2017-05-08 12:41:45 -07:00
|
|
|
detail::write_start_line(os, msg);
|
|
|
|
detail::write_fields(os, msg.fields);
|
|
|
|
os << "\r\n";
|
2016-11-07 13:51:10 -05:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Body, class Fields>
|
2016-04-29 06:04:40 -04:00
|
|
|
std::ostream&
|
|
|
|
operator<<(std::ostream& os,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields> const& msg)
|
2016-04-29 06:04:40 -04:00
|
|
|
{
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body<Body>::value,
|
2016-10-15 13:40:17 -04:00
|
|
|
"Body requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body_reader<Body>::value,
|
|
|
|
"BodyReader requirements not met");
|
2016-11-07 06:33:03 -05:00
|
|
|
beast::detail::sync_ostream oss{os};
|
2016-04-29 06:04:40 -04:00
|
|
|
error_code ec;
|
|
|
|
write(oss, msg, ec);
|
|
|
|
if(ec && ec != boost::asio::error::eof)
|
2017-05-22 15:30:12 -07:00
|
|
|
BOOST_THROW_EXCEPTION(system_error{ec});
|
2016-04-29 06:04:40 -04:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2017-07-20 08:01:46 -07:00
|
|
|
} // http
|
|
|
|
} // beast
|
|
|
|
|
|
|
|
#endif
|