forked from boostorg/beast
408 lines
9.8 KiB
Plaintext
408 lines
9.8 KiB
Plaintext
|
|
//
|
||
|
|
// 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
|