Refactor serialization algorithms:

serializer interface is changed to be buffer-only, no streams,
and placed in its own header file.

Operations on serializers are moved to free functions as part
of the HTTP write family of synchronous and asynchronous algorithms.
This commit is contained in:
Vinnie Falco committed 2017-05-28 19:35:20 -07:00
1 parent 407b0462d4
commit 34ea0b37c4
11 files changed
+1064 -1217

No files matched your search

+407
View File
@@ -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 <beast/http/error.hpp>
#include <boost/assert.hpp>
#include <ostream>
namespace beast {
namespace http {
namespace detail {
template<class Fields>
void
write_start_line(std::ostream& os,
header<true, Fields> 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<class Fields>
void
write_start_line(std::ostream& os,
header<false, Fields> 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<class FieldSequence>
void
write_fields(std::ostream& os,
FieldSequence const& fields)
{
//static_assert(is_FieldSequence<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<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;
// 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<bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
template<class Visit>
void
serializer<isRequest, Body, Fields,
Decorator, Allocator>::
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<cb0_t>(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<cb1_t>(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<ch0_t>(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<ch1_t>(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<ch2_t>(v_));
break;
//----------------------------------------------------------------------
default:
case do_complete:
BOOST_ASSERT(false);
break;
go_complete:
s_ = do_complete;
break;
}
}
template<bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
serializer<isRequest, Body, Fields,
Decorator, Allocator>::
consume(std::size_t n)
{
using boost::asio::buffer_size;
switch(s_)
{
case do_header:
BOOST_ASSERT(n <= buffer_size(
boost::get<cb0_t>(v_)));
boost::get<cb0_t>(v_).consume(n);
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 + 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<cb1_t>(v_)));
boost::get<cb1_t>(v_).consume(n);
if(buffer_size(boost::get<cb1_t>(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<ch0_t>(v_)));
boost::get<ch0_t>(v_).consume(n);
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 + 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<ch1_t>(v_)));
boost::get<ch1_t>(v_).consume(n);
if(buffer_size(boost::get<ch1_t>(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<ch2_t>(v_)));
boost::get<ch2_t>(v_).consume(n);
if(buffer_size(boost::get<ch2_t>(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
File diff suppressed because it is too large. Load diff