forked from boostorg/beast
Boost prep
This commit is contained in:
1 parent
3777cb8b9c
commit
7139dd639a
375 files changed
+5253
-3884
No files matched your search
@@ -0,0 +1,939 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
|
||||
|
||||
#include <boost/beast/core/static_string.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
basic_parser<isRequest, Derived>::
|
||||
~basic_parser()
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
basic_parser<isRequest, Derived>::
|
||||
basic_parser()
|
||||
: body_limit_(
|
||||
default_body_limit(is_request{}))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class OtherDerived>
|
||||
basic_parser<isRequest, Derived>::
|
||||
basic_parser(basic_parser<
|
||||
isRequest, OtherDerived>&& other)
|
||||
: body_limit_(other.body_limit_)
|
||||
, len_(other.len_)
|
||||
, buf_(std::move(other.buf_))
|
||||
, buf_len_(other.buf_len_)
|
||||
, skip_(other.skip_)
|
||||
, state_(other.state_)
|
||||
, f_(other.f_)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
basic_parser<isRequest, Derived>::
|
||||
is_keep_alive() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(f_ & flagHTTP11)
|
||||
{
|
||||
if(f_ & flagConnectionClose)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(! (f_ & flagConnectionKeepAlive))
|
||||
return false;
|
||||
}
|
||||
return (f_ & flagNeedEOF) == 0;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest, Derived>::
|
||||
content_length() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(! (f_ & flagContentLength))
|
||||
return boost::none;
|
||||
return len_;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
skip(bool v)
|
||||
{
|
||||
BOOST_ASSERT(! got_some());
|
||||
if(v)
|
||||
f_ |= flagSkipBody;
|
||||
else
|
||||
f_ &= ~flagSkipBody;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const p = buffers.begin();
|
||||
auto const last = buffers.end();
|
||||
if(p == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return 0;
|
||||
}
|
||||
if(std::next(p) == last)
|
||||
{
|
||||
// single buffer
|
||||
auto const b = *p;
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buffer_cast<char const*>(b),
|
||||
buffer_size(b)}, ec);
|
||||
}
|
||||
auto const size = buffer_size(buffers);
|
||||
if(size <= max_stack_buffer)
|
||||
return put_from_stack(size, buffers, ec);
|
||||
if(size > buf_len_)
|
||||
{
|
||||
// reallocate
|
||||
buf_ = boost::make_unique_noinit<char[]>(size);
|
||||
buf_len_ = size;
|
||||
}
|
||||
// flatten
|
||||
buffer_copy(boost::asio::buffer(
|
||||
buf_.get(), buf_len_), buffers);
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buf_.get(), buf_len_}, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(state_ != state::complete);
|
||||
using boost::asio::buffer_size;
|
||||
auto p = boost::asio::buffer_cast<
|
||||
char const*>(*buffer.begin());
|
||||
auto n = buffer_size(*buffer.begin());
|
||||
auto const p0 = p;
|
||||
auto const p1 = p0 + n;
|
||||
ec.assign(0, ec.category());
|
||||
loop:
|
||||
switch(state_)
|
||||
{
|
||||
case state::nothing_yet:
|
||||
if(n == 0)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return 0;
|
||||
}
|
||||
state_ = state::start_line;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case state::start_line:
|
||||
{
|
||||
maybe_need_more(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
parse_start_line(p, p + std::min<std::size_t>(
|
||||
header_limit_, n), ec, is_request{});
|
||||
if(ec)
|
||||
{
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
if(n >= header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
goto done;
|
||||
}
|
||||
if(p + 3 <= p1)
|
||||
skip_ = static_cast<
|
||||
std::size_t>(p1 - p - 3);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
BOOST_ASSERT(! is_done());
|
||||
n = static_cast<std::size_t>(p1 - p);
|
||||
if(p >= p1)
|
||||
{
|
||||
ec = error::need_more;
|
||||
goto done;
|
||||
}
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case state::fields:
|
||||
maybe_need_more(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
parse_fields(p, p + std::min<std::size_t>(
|
||||
header_limit_, n), ec);
|
||||
if(ec)
|
||||
{
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
if(n >= header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
goto done;
|
||||
}
|
||||
if(p + 3 <= p1)
|
||||
skip_ = static_cast<
|
||||
std::size_t>(p1 - p - 3);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
finish_header(ec, is_request{});
|
||||
break;
|
||||
|
||||
case state::body0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
impl().on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case state::body:
|
||||
BOOST_ASSERT(! skip_);
|
||||
parse_body(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::body_to_eof0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
impl().on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body_to_eof;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case state::body_to_eof:
|
||||
BOOST_ASSERT(! skip_);
|
||||
parse_body_to_eof(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::chunk_header0:
|
||||
impl().on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::chunk_header;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case state::chunk_header:
|
||||
parse_chunk_header(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::chunk_body:
|
||||
parse_chunk_body(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::complete:
|
||||
ec.assign(0, ec.category());
|
||||
goto done;
|
||||
}
|
||||
if(p < p1 && ! is_done() && eager())
|
||||
{
|
||||
n = static_cast<std::size_t>(p1 - p);
|
||||
goto loop;
|
||||
}
|
||||
done:
|
||||
return static_cast<std::size_t>(p - p0);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
put_eof(error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(got_some());
|
||||
if( state_ == state::start_line ||
|
||||
state_ == state::fields)
|
||||
{
|
||||
ec = error::partial_message;
|
||||
return;
|
||||
}
|
||||
if(f_ & (flagContentLength | flagChunked))
|
||||
{
|
||||
if(state_ != state::complete)
|
||||
{
|
||||
ec = error::partial_message;
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put_from_stack(std::size_t size,
|
||||
ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
char buf[max_stack_buffer];
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
buffer_copy(buffer(buf, sizeof(buf)), buffers);
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buf, size}, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
maybe_need_more(
|
||||
char const* p, std::size_t n,
|
||||
error_code& ec)
|
||||
{
|
||||
if(skip_ == 0)
|
||||
return;
|
||||
if( n > header_limit_)
|
||||
n = header_limit_;
|
||||
if(n < skip_ + 4)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
auto const term =
|
||||
find_eom(p + skip_, p + n);
|
||||
if(! term)
|
||||
{
|
||||
skip_ = n - 3;
|
||||
if(skip_ + 4 > header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
return;
|
||||
}
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
skip_ = 0;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_start_line(
|
||||
char const*& in, char const* last,
|
||||
error_code& ec, std::true_type)
|
||||
{
|
||||
/*
|
||||
request-line = method SP request-target SP HTTP-version CRLF
|
||||
method = token
|
||||
*/
|
||||
auto p = in;
|
||||
|
||||
string_view method;
|
||||
parse_method(p, last, method, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
string_view target;
|
||||
parse_target(p, last, target, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
int version = 0;
|
||||
parse_version(p, last, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(version < 10 || version > 11)
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
if(p + 2 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(p[0] != '\r' || p[1] != '\n')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
impl().on_request_impl(string_to_verb(method),
|
||||
method, target, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_start_line(
|
||||
char const*& in, char const* last,
|
||||
error_code& ec, std::false_type)
|
||||
{
|
||||
/*
|
||||
status-line = HTTP-version SP status-code SP reason-phrase CRLF
|
||||
status-code = 3*DIGIT
|
||||
reason-phrase = *( HTAB / SP / VCHAR / obs-text )
|
||||
*/
|
||||
auto p = in;
|
||||
|
||||
int version = 0;
|
||||
parse_version(p, last, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(version < 10 || version > 11)
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
// SP
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*p++ != ' ')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
parse_status(p, last, status_, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
// parse reason CRLF
|
||||
string_view reason;
|
||||
parse_reason(p, last, reason, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
impl().on_response_impl(
|
||||
status_, reason, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_fields(char const*& in,
|
||||
char const* last, error_code& ec)
|
||||
{
|
||||
string_view name;
|
||||
string_view value;
|
||||
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
||||
static_string<max_obs_fold> buf;
|
||||
auto p = in;
|
||||
for(;;)
|
||||
{
|
||||
if(p + 2 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(p[0] == '\r')
|
||||
{
|
||||
if(p[1] != '\n')
|
||||
ec = error::bad_line_ending;
|
||||
in = p + 2;
|
||||
return;
|
||||
}
|
||||
parse_field(p, last, name, value, buf, ec);
|
||||
if(ec)
|
||||
return;
|
||||
auto const f = string_to_field(name);
|
||||
do_field(f, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
impl().on_field_impl(f, name, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
in = p;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
finish_header(error_code& ec, std::true_type)
|
||||
{
|
||||
// RFC 7230 section 3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
if(f_ & flagSkipBody)
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
else if(f_ & flagContentLength)
|
||||
{
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
len_ = 0;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
impl().on_header_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
finish_header(error_code& ec, std::false_type)
|
||||
{
|
||||
// RFC 7230 section 3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
|
||||
status_ / 100 == 1 || // 1xx e.g. Continue
|
||||
status_ == 204 || // No Content
|
||||
status_ == 304) // Not Modified
|
||||
{
|
||||
state_ = state::complete;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
f_ |= flagNeedEOF;
|
||||
state_ = state::body_to_eof0;
|
||||
}
|
||||
|
||||
impl().on_header_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
n = impl().on_body_impl(string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(ec)
|
||||
return;
|
||||
if(len_ > 0)
|
||||
return;
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
if(n > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
body_limit_ = body_limit_ - n;
|
||||
n = impl().on_body_impl(string_view{p, n}, ec);
|
||||
p += n;
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_chunk_header(char const*& p0,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunked-body = *chunk last-chunk trailer-part CRLF
|
||||
|
||||
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
|
||||
last-chunk = 1*("0") [ chunk-ext ] CRLF
|
||||
trailer-part = *( header-field CRLF )
|
||||
|
||||
chunk-size = 1*HEXDIG
|
||||
chunk-data = 1*OCTET ; a sequence of chunk-size octets
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
*/
|
||||
|
||||
auto p = p0;
|
||||
auto const pend = p + n;
|
||||
char const* eol;
|
||||
|
||||
if(! (f_ & flagFinalChunk))
|
||||
{
|
||||
if(n < skip_ + 2)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(f_ & flagExpectCRLF)
|
||||
{
|
||||
// Treat the last CRLF in a chunk as
|
||||
// part of the next chunk, so p can
|
||||
// be parsed in one call instead of two.
|
||||
if(! parse_crlf(p))
|
||||
{
|
||||
ec = error::bad_chunk;
|
||||
return;
|
||||
}
|
||||
}
|
||||
eol = find_eol(p0 + skip_, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! eol)
|
||||
{
|
||||
ec = error::need_more;
|
||||
skip_ = n - 1;
|
||||
return;
|
||||
}
|
||||
skip_ = static_cast<
|
||||
std::size_t>(eol - 2 - p0);
|
||||
|
||||
std::uint64_t size;
|
||||
if(! parse_hex(p, size))
|
||||
{
|
||||
ec = error::bad_chunk;
|
||||
return;
|
||||
}
|
||||
if(size != 0)
|
||||
{
|
||||
if(size > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
body_limit_ -= size;
|
||||
auto const start = p;
|
||||
parse_chunk_extensions(p, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(p != eol -2 )
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
auto const ext = make_string(start, p);
|
||||
impl().on_chunk_header_impl(size, ext, ec);
|
||||
if(ec)
|
||||
return;
|
||||
len_ = size;
|
||||
skip_ = 2;
|
||||
p0 = eol;
|
||||
f_ |= flagExpectCRLF;
|
||||
state_ = state::chunk_body;
|
||||
return;
|
||||
}
|
||||
|
||||
f_ |= flagFinalChunk;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(n >= 5);
|
||||
if(f_ & flagExpectCRLF)
|
||||
BOOST_VERIFY(parse_crlf(p));
|
||||
std::uint64_t size;
|
||||
BOOST_VERIFY(parse_hex(p, size));
|
||||
eol = find_eol(p, pend, ec);
|
||||
BOOST_ASSERT(! ec);
|
||||
}
|
||||
|
||||
auto eom = find_eom(p0 + skip_, pend);
|
||||
if(! eom)
|
||||
{
|
||||
BOOST_ASSERT(n >= 3);
|
||||
skip_ = n - 3;
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
|
||||
auto const start = p;
|
||||
parse_chunk_extensions(p, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(p != eol - 2)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
auto const ext = make_string(start, p);
|
||||
impl().on_chunk_header_impl(0, ext, ec);
|
||||
if(ec)
|
||||
return;
|
||||
p = eol;
|
||||
parse_fields(p, eom, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p == eom);
|
||||
p0 = eom;
|
||||
|
||||
impl().on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
n = impl().on_chunk_body_impl(
|
||||
len_, string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(len_ == 0)
|
||||
state_ = state::chunk_header;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
do_field(field f,
|
||||
string_view value, error_code& ec)
|
||||
{
|
||||
// Connection
|
||||
if(f == field::connection ||
|
||||
f == field::proxy_connection)
|
||||
{
|
||||
auto const list = opt_token_list{value};
|
||||
if(! validate_list(list))
|
||||
{
|
||||
// VFALCO Should this be a field specific error?
|
||||
ec = error::bad_value;
|
||||
return;
|
||||
}
|
||||
for(auto const& s : list)
|
||||
{
|
||||
if(iequals({"close", 5}, s))
|
||||
{
|
||||
f_ |= flagConnectionClose;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(iequals({"keep-alive", 10}, s))
|
||||
{
|
||||
f_ |= flagConnectionKeepAlive;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(iequals({"upgrade", 7}, s))
|
||||
{
|
||||
f_ |= flagConnectionUpgrade;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
|
||||
// Content-Length
|
||||
if(f == field::content_length)
|
||||
{
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
// duplicate
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagChunked)
|
||||
{
|
||||
// conflicting field
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
std::uint64_t v;
|
||||
if(! parse_dec(
|
||||
value.begin(), value.end(), v))
|
||||
{
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
if(v > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
len_ = v;
|
||||
f_ |= flagContentLength;
|
||||
return;
|
||||
}
|
||||
|
||||
// Transfer-Encoding
|
||||
if(f == field::transfer_encoding)
|
||||
{
|
||||
if(f_ & flagChunked)
|
||||
{
|
||||
// duplicate
|
||||
ec = error::bad_transfer_encoding;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
// conflicting field
|
||||
ec = error::bad_transfer_encoding;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
auto const v = token_list{value};
|
||||
auto const p = std::find_if(v.begin(), v.end(),
|
||||
[&](typename token_list::value_type const& s)
|
||||
{
|
||||
return iequals({"chunked", 7}, s);
|
||||
});
|
||||
if(p == v.end())
|
||||
return;
|
||||
if(std::next(p) != v.end())
|
||||
return;
|
||||
len_ = 0;
|
||||
f_ |= flagChunked;
|
||||
return;
|
||||
}
|
||||
|
||||
// Upgrade
|
||||
if(f == field::upgrade)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
f_ |= flagUpgrade;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,707 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP
|
||||
|
||||
#include <boost/beast/core/detail/varint.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
inline
|
||||
chunk_header::
|
||||
chunk_header(std::size_t size)
|
||||
: view_(
|
||||
size,
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{})
|
||||
{
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
inline
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
string_view extensions)
|
||||
: view_(
|
||||
size,
|
||||
boost::asio::const_buffers_1{
|
||||
extensions.data(), extensions.size()},
|
||||
chunk_crlf{})
|
||||
{
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
template<class ChunkExtensions, class>
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions)
|
||||
: exts_(std::make_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
size,
|
||||
exts_->str(),
|
||||
chunk_crlf{})
|
||||
{
|
||||
static_assert(
|
||||
detail::is_chunk_extensions<ChunkExtensions>::value,
|
||||
"ChunkExtensions requirements not met");
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
template<class ChunkExtensions, class Allocator, class>
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator)
|
||||
: exts_(std::allocate_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(allocator,
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
size,
|
||||
exts_->str(),
|
||||
chunk_crlf{})
|
||||
{
|
||||
static_assert(
|
||||
detail::is_chunk_extensions<ChunkExtensions>::value,
|
||||
"ChunkExtensions requirements not met");
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(ConstBufferSequence const& buffers)
|
||||
: view_(
|
||||
boost::asio::buffer_size(buffers),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
string_view extensions)
|
||||
: view_(
|
||||
boost::asio::buffer_size(buffers),
|
||||
boost::asio::const_buffers_1{
|
||||
extensions.data(), extensions.size()},
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
template<class ChunkExtensions, class>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions)
|
||||
: exts_(std::make_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
boost::asio::buffer_size(buffers),
|
||||
exts_->str(),
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
template<class ChunkExtensions, class Allocator, class>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator)
|
||||
: exts_(std::allocate_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(allocator,
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
boost::asio::buffer_size(buffers),
|
||||
exts_->str(),
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Trailer>
|
||||
template<class Allocator>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, Allocator const& allocator) ->
|
||||
buffers_type
|
||||
{
|
||||
auto sp = std::allocate_shared<typename
|
||||
Trailer::reader>(allocator, trailer);
|
||||
sp_ = sp;
|
||||
return sp->get();
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, std::true_type) ->
|
||||
buffers_type
|
||||
{
|
||||
auto sp = std::make_shared<
|
||||
typename Trailer::reader>(trailer);
|
||||
sp_ = sp;
|
||||
return sp->get();
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, std::false_type) ->
|
||||
buffers_type
|
||||
{
|
||||
return trailer;
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last()
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
Trailer{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last(Trailer const& trailer)
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
prepare(trailer, is_fields<Trailer>{}))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
template<class DeducedTrailer, class Allocator, class>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last(
|
||||
DeducedTrailer const& trailer, Allocator const& allocator)
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
prepare(trailer, allocator))
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
class basic_chunk_extensions<Allocator>::const_iterator
|
||||
{
|
||||
friend class basic_chunk_extensions;
|
||||
|
||||
using iter_type = char const*;
|
||||
|
||||
iter_type it_;
|
||||
typename basic_chunk_extensions::value_type value_;
|
||||
|
||||
explicit
|
||||
const_iterator(iter_type it)
|
||||
: it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
increment();
|
||||
|
||||
public:
|
||||
using value_type = typename
|
||||
basic_chunk_extensions::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*();
|
||||
|
||||
pointer
|
||||
operator->()
|
||||
{
|
||||
return &(**this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
increment();
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
const_iterator::
|
||||
increment()
|
||||
{
|
||||
using beast::detail::varint_read;
|
||||
auto n = varint_read(it_);
|
||||
it_ += n;
|
||||
n = varint_read(it_);
|
||||
it_ += n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
const_iterator::
|
||||
operator*() ->
|
||||
reference
|
||||
{
|
||||
using beast::detail::varint_read;
|
||||
auto it = it_;
|
||||
auto n = varint_read(it);
|
||||
value_.first = string_view{it, n};
|
||||
it += n;
|
||||
n = varint_read(it);
|
||||
value_.second = string_view{it, n};
|
||||
return value_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
template<class FwdIt>
|
||||
FwdIt
|
||||
basic_chunk_extensions<Allocator>::
|
||||
do_parse(FwdIt it, FwdIt last, error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
|
||||
BWS = *( SP / HTAB ) ; "Bad White Space"
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
token = 1*tchar
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
|
||||
https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
|
||||
*/
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
using CharT = char;
|
||||
using Traits = std::char_traits<CharT>;
|
||||
range_.reserve(static_cast<std::size_t>(
|
||||
std::distance(it, last) * 1.2));
|
||||
range_.resize(0);
|
||||
auto const emit_string =
|
||||
[this](FwdIt from, FwdIt to)
|
||||
{
|
||||
auto const len =
|
||||
std::distance(from, to);
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len);
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
Traits::copy(dest, from, len);
|
||||
};
|
||||
auto const emit_string_plus_empty =
|
||||
[this](FwdIt from, FwdIt to)
|
||||
{
|
||||
auto const len =
|
||||
std::distance(from, to);
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len +
|
||||
varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
Traits::copy(dest, from, len);
|
||||
dest += len;
|
||||
varint_write(dest, 0);
|
||||
};
|
||||
auto const emit_empty_string =
|
||||
[this]
|
||||
{
|
||||
auto const offset = range_.size();
|
||||
range_.resize(offset + varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, 0);
|
||||
};
|
||||
loop:
|
||||
if(it == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return it;
|
||||
}
|
||||
// BWS
|
||||
if(*it == ' ' || *it == '\t')
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ';'
|
||||
if(*it != ';')
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
semi:
|
||||
++it; // skip ';'
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-name
|
||||
{
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
auto const first = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
emit_string_plus_empty(first, it);
|
||||
return it;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
emit_string(first, it);
|
||||
}
|
||||
// BWS [ ";" / "=" ]
|
||||
for(;;)
|
||||
{
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
}
|
||||
if(*it == ';')
|
||||
{
|
||||
emit_empty_string();
|
||||
goto semi;
|
||||
}
|
||||
if(*it != '=')
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
++it; // skip '='
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-val
|
||||
if(*it != '"')
|
||||
{
|
||||
// token
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
auto const first = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
break;
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
emit_string(first, it);
|
||||
if(it == last)
|
||||
return it;
|
||||
}
|
||||
else
|
||||
{
|
||||
// quoted-string
|
||||
auto const first = ++it; // skip DQUOTE
|
||||
// first pass, count chars
|
||||
std::size_t len = 0;
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return it;
|
||||
}
|
||||
}
|
||||
++len;
|
||||
++it;
|
||||
}
|
||||
// now build the string
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len);
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
it = first;
|
||||
for(;;)
|
||||
{
|
||||
BOOST_ASSERT(it != last);
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
BOOST_ASSERT(it != last);
|
||||
}
|
||||
Traits::assign(*dest++, *it++);
|
||||
}
|
||||
++it; // skip DQUOTE
|
||||
}
|
||||
goto loop;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
do_insert(string_view name, string_view value)
|
||||
{
|
||||
/*
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
token = 1*tchar
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
*/
|
||||
if(value.empty())
|
||||
{
|
||||
s_.reserve(1 + name.size());
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_token = true;
|
||||
for(auto const c : value)
|
||||
{
|
||||
if(! detail::is_token_char(c))
|
||||
{
|
||||
is_token = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(is_token)
|
||||
{
|
||||
// token
|
||||
s_.reserve(1 + name.size() + 1 + value.size());
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
s_.push_back('=');
|
||||
s_.append(value.data(), value.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// quoted-string
|
||||
s_.reserve(
|
||||
1 + name.size() + 1 +
|
||||
1 + value.size() + 20 + 1);
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
s_.append("=\"", 2);
|
||||
for(auto const c : value)
|
||||
{
|
||||
if(c == '\\')
|
||||
s_.append(R"(\\)", 2);
|
||||
else if(c == '\"')
|
||||
s_.append(R"(\")", 2);
|
||||
else
|
||||
s_.push_back(c);
|
||||
}
|
||||
s_.push_back('"');
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
parse(string_view s, error_code& ec)
|
||||
{
|
||||
do_parse(s.data(), s.data() + s.size(), ec);
|
||||
if(! ec)
|
||||
{
|
||||
s_.clear();
|
||||
for(auto const& v : *this)
|
||||
do_insert(v.first, v.second);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
insert(string_view name)
|
||||
{
|
||||
do_insert(name, {});
|
||||
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(name.size()) +
|
||||
name.size() +
|
||||
varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, name.size());
|
||||
std::memcpy(dest, name.data(), name.size());
|
||||
dest += name.size();
|
||||
varint_write(dest, 0);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
insert(string_view name, string_view value)
|
||||
{
|
||||
do_insert(name, value);
|
||||
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(name.size()) +
|
||||
name.size() +
|
||||
varint_size(value.size()) +
|
||||
value.size());
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, name.size());
|
||||
std::memcpy(dest, name.data(), name.size());
|
||||
dest += name.size();
|
||||
varint_write(dest, value.size());
|
||||
std::memcpy(dest, value.data(), value.size());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{range_.data()};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{
|
||||
range_.data() + range_.size()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_ERROR_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_ERROR_IPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::http::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class http_error_category : public error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "beast.http";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
case error::end_of_stream: return "end of stream";
|
||||
case error::partial_message: return "partial message";
|
||||
case error::need_more: return "need more";
|
||||
case error::unexpected_body: return "unexpected body";
|
||||
case error::need_buffer: return "need buffer";
|
||||
case error::end_of_chunk: return "end of chunk";
|
||||
case error::buffer_overflow: return "buffer overflow";
|
||||
case error::header_limit: return "header limit exceeded";
|
||||
case error::body_limit: return "body limit exceeded";
|
||||
case error::bad_alloc: return "bad alloc";
|
||||
case error::bad_line_ending: return "bad line ending";
|
||||
case error::bad_method: return "bad method";
|
||||
case error::bad_target: return "bad target";
|
||||
case error::bad_version: return "bad version";
|
||||
case error::bad_status: return "bad status";
|
||||
case error::bad_reason: return "bad reason";
|
||||
case error::bad_field: return "bad field";
|
||||
case error::bad_value: return "bad value";
|
||||
case error::bad_content_length: return "bad Content-Length";
|
||||
case error::bad_transfer_encoding: return "bad Transfer-Encoding";
|
||||
case error::bad_chunk: return "bad chunk";
|
||||
case error::bad_chunk_extension: return "bad chunk extension";
|
||||
case error::bad_obs_fold: return "bad obs-fold";
|
||||
|
||||
default:
|
||||
return "beast.http error";
|
||||
}
|
||||
}
|
||||
|
||||
error_condition
|
||||
default_error_condition(
|
||||
int ev) const noexcept override
|
||||
{
|
||||
return error_condition{ev, *this};
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
error_condition const& condition
|
||||
) const noexcept override
|
||||
{
|
||||
return condition.value() == ev &&
|
||||
&condition.category() == this;
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(error_code const& error,
|
||||
int ev) const noexcept override
|
||||
{
|
||||
return error.value() == ev &&
|
||||
&error.category() == this;
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
error_category const&
|
||||
get_http_error_category()
|
||||
{
|
||||
static http_error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_error_code(error ev)
|
||||
{
|
||||
return error_code{
|
||||
static_cast<std::underlying_type<error>::type>(ev),
|
||||
detail::get_http_error_category()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,561 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_FIELD_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FIELD_IPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct field_table
|
||||
{
|
||||
using array_type =
|
||||
std::array<string_view, 352>;
|
||||
|
||||
struct hash
|
||||
{
|
||||
std::size_t
|
||||
operator()(string_view s) const
|
||||
{
|
||||
auto const n = s.size();
|
||||
return
|
||||
beast::detail::ascii_tolower(s[0]) *
|
||||
beast::detail::ascii_tolower(s[n/2]) ^
|
||||
beast::detail::ascii_tolower(s[n-1]); // hist[] = 331, 10, max_load_factor = 0.15f
|
||||
}
|
||||
};
|
||||
|
||||
struct iequal
|
||||
{
|
||||
// assumes inputs have equal length
|
||||
bool
|
||||
operator()(
|
||||
string_view lhs,
|
||||
string_view rhs) const
|
||||
{
|
||||
auto p1 = lhs.data();
|
||||
auto p2 = rhs.data();
|
||||
auto pend = lhs.end();
|
||||
char a, b;
|
||||
while(p1 < pend)
|
||||
{
|
||||
a = *p1++;
|
||||
b = *p2++;
|
||||
if(a != b)
|
||||
goto slow;
|
||||
}
|
||||
return true;
|
||||
|
||||
while(p1 < pend)
|
||||
{
|
||||
slow:
|
||||
if( beast::detail::ascii_tolower(a) !=
|
||||
beast::detail::ascii_tolower(b))
|
||||
return false;
|
||||
a = *p1++;
|
||||
b = *p2++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using map_type = std::unordered_map<
|
||||
string_view, field, hash, iequal>;
|
||||
|
||||
array_type by_name_;
|
||||
std::vector<map_type> by_size_;
|
||||
/*
|
||||
From:
|
||||
|
||||
https://www.iana.org/assignments/message-headers/message-headers.xhtml
|
||||
*/
|
||||
field_table()
|
||||
: by_name_({{
|
||||
"<unknown-field>",
|
||||
"A-IM",
|
||||
"Accept",
|
||||
"Accept-Additions",
|
||||
"Accept-Charset",
|
||||
"Accept-Datetime",
|
||||
"Accept-Encoding",
|
||||
"Accept-Features",
|
||||
"Accept-Language",
|
||||
"Accept-Patch",
|
||||
"Accept-Post",
|
||||
"Accept-Ranges",
|
||||
"Access-Control",
|
||||
"Access-Control-Allow-Credentials",
|
||||
"Access-Control-Allow-Headers",
|
||||
"Access-Control-Allow-Methods",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Access-Control-Max-Age",
|
||||
"Access-Control-Request-Headers",
|
||||
"Access-Control-Request-Method",
|
||||
"Age",
|
||||
"Allow",
|
||||
"ALPN",
|
||||
"Also-Control",
|
||||
"Alt-Svc",
|
||||
"Alt-Used",
|
||||
"Alternate-Recipient",
|
||||
"Alternates",
|
||||
"Apparently-To",
|
||||
"Apply-To-Redirect-Ref",
|
||||
"Approved",
|
||||
"Archive",
|
||||
"Archived-At",
|
||||
"Article-Names",
|
||||
"Article-Updates",
|
||||
"Authentication-Control",
|
||||
"Authentication-Info",
|
||||
"Authentication-Results",
|
||||
"Authorization",
|
||||
"Auto-Submitted",
|
||||
"Autoforwarded",
|
||||
"Autosubmitted",
|
||||
"Base",
|
||||
"Bcc",
|
||||
"Body",
|
||||
"C-Ext",
|
||||
"C-Man",
|
||||
"C-Opt",
|
||||
"C-PEP",
|
||||
"C-PEP-Info",
|
||||
"Cache-Control",
|
||||
"CalDAV-Timezones",
|
||||
"Cancel-Key",
|
||||
"Cancel-Lock",
|
||||
"Cc",
|
||||
"Close",
|
||||
"Comments",
|
||||
"Compliance",
|
||||
"Connection",
|
||||
"Content-Alternative",
|
||||
"Content-Base",
|
||||
"Content-Description",
|
||||
"Content-Disposition",
|
||||
"Content-Duration",
|
||||
"Content-Encoding",
|
||||
"Content-features",
|
||||
"Content-ID",
|
||||
"Content-Identifier",
|
||||
"Content-Language",
|
||||
"Content-Length",
|
||||
"Content-Location",
|
||||
"Content-MD5",
|
||||
"Content-Range",
|
||||
"Content-Return",
|
||||
"Content-Script-Type",
|
||||
"Content-Style-Type",
|
||||
"Content-Transfer-Encoding",
|
||||
"Content-Type",
|
||||
"Content-Version",
|
||||
"Control",
|
||||
"Conversion",
|
||||
"Conversion-With-Loss",
|
||||
"Cookie",
|
||||
"Cookie2",
|
||||
"Cost",
|
||||
"DASL",
|
||||
"Date",
|
||||
"Date-Received",
|
||||
"DAV",
|
||||
"Default-Style",
|
||||
"Deferred-Delivery",
|
||||
"Delivery-Date",
|
||||
"Delta-Base",
|
||||
"Depth",
|
||||
"Derived-From",
|
||||
"Destination",
|
||||
"Differential-ID",
|
||||
"Digest",
|
||||
"Discarded-X400-IPMS-Extensions",
|
||||
"Discarded-X400-MTS-Extensions",
|
||||
"Disclose-Recipients",
|
||||
"Disposition-Notification-Options",
|
||||
"Disposition-Notification-To",
|
||||
"Distribution",
|
||||
"DKIM-Signature",
|
||||
"DL-Expansion-History",
|
||||
"Downgraded-Bcc",
|
||||
"Downgraded-Cc",
|
||||
"Downgraded-Disposition-Notification-To",
|
||||
"Downgraded-Final-Recipient",
|
||||
"Downgraded-From",
|
||||
"Downgraded-In-Reply-To",
|
||||
"Downgraded-Mail-From",
|
||||
"Downgraded-Message-Id",
|
||||
"Downgraded-Original-Recipient",
|
||||
"Downgraded-Rcpt-To",
|
||||
"Downgraded-References",
|
||||
"Downgraded-Reply-To",
|
||||
"Downgraded-Resent-Bcc",
|
||||
"Downgraded-Resent-Cc",
|
||||
"Downgraded-Resent-From",
|
||||
"Downgraded-Resent-Reply-To",
|
||||
"Downgraded-Resent-Sender",
|
||||
"Downgraded-Resent-To",
|
||||
"Downgraded-Return-Path",
|
||||
"Downgraded-Sender",
|
||||
"Downgraded-To",
|
||||
"EDIINT-Features",
|
||||
"Eesst-Version",
|
||||
"Encoding",
|
||||
"Encrypted",
|
||||
"Errors-To",
|
||||
"ETag",
|
||||
"Expect",
|
||||
"Expires",
|
||||
"Expiry-Date",
|
||||
"Ext",
|
||||
"Followup-To",
|
||||
"Forwarded",
|
||||
"From",
|
||||
"Generate-Delivery-Report",
|
||||
"GetProfile",
|
||||
"Hobareg",
|
||||
"Host",
|
||||
"HTTP2-Settings",
|
||||
"If",
|
||||
"If-Match",
|
||||
"If-Modified-Since",
|
||||
"If-None-Match",
|
||||
"If-Range",
|
||||
"If-Schedule-Tag-Match",
|
||||
"If-Unmodified-Since",
|
||||
"IM",
|
||||
"Importance",
|
||||
"In-Reply-To",
|
||||
"Incomplete-Copy",
|
||||
"Injection-Date",
|
||||
"Injection-Info",
|
||||
"Jabber-ID",
|
||||
"Keep-Alive",
|
||||
"Keywords",
|
||||
"Label",
|
||||
"Language",
|
||||
"Last-Modified",
|
||||
"Latest-Delivery-Time",
|
||||
"Lines",
|
||||
"Link",
|
||||
"List-Archive",
|
||||
"List-Help",
|
||||
"List-ID",
|
||||
"List-Owner",
|
||||
"List-Post",
|
||||
"List-Subscribe",
|
||||
"List-Unsubscribe",
|
||||
"List-Unsubscribe-Post",
|
||||
"Location",
|
||||
"Lock-Token",
|
||||
"Man",
|
||||
"Max-Forwards",
|
||||
"Memento-Datetime",
|
||||
"Message-Context",
|
||||
"Message-ID",
|
||||
"Message-Type",
|
||||
"Meter",
|
||||
"Method-Check",
|
||||
"Method-Check-Expires",
|
||||
"MIME-Version",
|
||||
"MMHS-Acp127-Message-Identifier",
|
||||
"MMHS-Authorizing-Users",
|
||||
"MMHS-Codress-Message-Indicator",
|
||||
"MMHS-Copy-Precedence",
|
||||
"MMHS-Exempted-Address",
|
||||
"MMHS-Extended-Authorisation-Info",
|
||||
"MMHS-Handling-Instructions",
|
||||
"MMHS-Message-Instructions",
|
||||
"MMHS-Message-Type",
|
||||
"MMHS-Originator-PLAD",
|
||||
"MMHS-Originator-Reference",
|
||||
"MMHS-Other-Recipients-Indicator-CC",
|
||||
"MMHS-Other-Recipients-Indicator-To",
|
||||
"MMHS-Primary-Precedence",
|
||||
"MMHS-Subject-Indicator-Codes",
|
||||
"MT-Priority",
|
||||
"Negotiate",
|
||||
"Newsgroups",
|
||||
"NNTP-Posting-Date",
|
||||
"NNTP-Posting-Host",
|
||||
"Non-Compliance",
|
||||
"Obsoletes",
|
||||
"Opt",
|
||||
"Optional",
|
||||
"Optional-WWW-Authenticate",
|
||||
"Ordering-Type",
|
||||
"Organization",
|
||||
"Origin",
|
||||
"Original-Encoded-Information-Types",
|
||||
"Original-From",
|
||||
"Original-Message-ID",
|
||||
"Original-Recipient",
|
||||
"Original-Sender",
|
||||
"Original-Subject",
|
||||
"Originator-Return-Address",
|
||||
"Overwrite",
|
||||
"P3P",
|
||||
"Path",
|
||||
"PEP",
|
||||
"Pep-Info",
|
||||
"PICS-Label",
|
||||
"Position",
|
||||
"Posting-Version",
|
||||
"Pragma",
|
||||
"Prefer",
|
||||
"Preference-Applied",
|
||||
"Prevent-NonDelivery-Report",
|
||||
"Priority",
|
||||
"Privicon",
|
||||
"ProfileObject",
|
||||
"Protocol",
|
||||
"Protocol-Info",
|
||||
"Protocol-Query",
|
||||
"Protocol-Request",
|
||||
"Proxy-Authenticate",
|
||||
"Proxy-Authentication-Info",
|
||||
"Proxy-Authorization",
|
||||
"Proxy-Connection",
|
||||
"Proxy-Features",
|
||||
"Proxy-Instruction",
|
||||
"Public",
|
||||
"Public-Key-Pins",
|
||||
"Public-Key-Pins-Report-Only",
|
||||
"Range",
|
||||
"Received",
|
||||
"Received-SPF",
|
||||
"Redirect-Ref",
|
||||
"References",
|
||||
"Referer",
|
||||
"Referer-Root",
|
||||
"Relay-Version",
|
||||
"Reply-By",
|
||||
"Reply-To",
|
||||
"Require-Recipient-Valid-Since",
|
||||
"Resent-Bcc",
|
||||
"Resent-Cc",
|
||||
"Resent-Date",
|
||||
"Resent-From",
|
||||
"Resent-Message-ID",
|
||||
"Resent-Reply-To",
|
||||
"Resent-Sender",
|
||||
"Resent-To",
|
||||
"Resolution-Hint",
|
||||
"Resolver-Location",
|
||||
"Retry-After",
|
||||
"Return-Path",
|
||||
"Safe",
|
||||
"Schedule-Reply",
|
||||
"Schedule-Tag",
|
||||
"Sec-WebSocket-Accept",
|
||||
"Sec-WebSocket-Extensions",
|
||||
"Sec-WebSocket-Key",
|
||||
"Sec-WebSocket-Protocol",
|
||||
"Sec-WebSocket-Version",
|
||||
"Security-Scheme",
|
||||
"See-Also",
|
||||
"Sender",
|
||||
"Sensitivity",
|
||||
"Server",
|
||||
"Set-Cookie",
|
||||
"Set-Cookie2",
|
||||
"SetProfile",
|
||||
"SIO-Label",
|
||||
"SIO-Label-History",
|
||||
"SLUG",
|
||||
"SoapAction",
|
||||
"Solicitation",
|
||||
"Status-URI",
|
||||
"Strict-Transport-Security",
|
||||
"Subject",
|
||||
"SubOK",
|
||||
"Subst",
|
||||
"Summary",
|
||||
"Supersedes",
|
||||
"Surrogate-Capability",
|
||||
"Surrogate-Control",
|
||||
"TCN",
|
||||
"TE",
|
||||
"Timeout",
|
||||
"Title",
|
||||
"To",
|
||||
"Topic",
|
||||
"Trailer",
|
||||
"Transfer-Encoding",
|
||||
"TTL",
|
||||
"UA-Color",
|
||||
"UA-Media",
|
||||
"UA-Pixels",
|
||||
"UA-Resolution",
|
||||
"UA-Windowpixels",
|
||||
"Upgrade",
|
||||
"Urgency",
|
||||
"URI",
|
||||
"User-Agent",
|
||||
"Variant-Vary",
|
||||
"Vary",
|
||||
"VBR-Info",
|
||||
"Version",
|
||||
"Via",
|
||||
"Want-Digest",
|
||||
"Warning",
|
||||
"WWW-Authenticate",
|
||||
"X-Archived-At",
|
||||
"X-Device-Accept",
|
||||
"X-Device-Accept-Charset",
|
||||
"X-Device-Accept-Encoding",
|
||||
"X-Device-Accept-Language",
|
||||
"X-Device-User-Agent",
|
||||
"X-Frame-Options",
|
||||
"X-Mittente",
|
||||
"X-PGP-Sig",
|
||||
"X-Ricevuta",
|
||||
"X-Riferimento-Message-ID",
|
||||
"X-TipoRicevuta",
|
||||
"X-Trasporto",
|
||||
"X-VerificaSicurezza",
|
||||
"X400-Content-Identifier",
|
||||
"X400-Content-Return",
|
||||
"X400-Content-Type",
|
||||
"X400-MTS-Identifier",
|
||||
"X400-Originator",
|
||||
"X400-Received",
|
||||
"X400-Recipients",
|
||||
"X400-Trace",
|
||||
"Xref"
|
||||
}})
|
||||
{
|
||||
// find the longest field length
|
||||
std::size_t high = 0;
|
||||
for(auto const& s : by_name_)
|
||||
if(high < s.size())
|
||||
high = s.size();
|
||||
// build by_size map
|
||||
// skip field::unknown
|
||||
by_size_.resize(high + 1);
|
||||
for(auto& map : by_size_)
|
||||
map.max_load_factor(.15f);
|
||||
for(std::size_t i = 1;
|
||||
i < by_name_.size(); ++i)
|
||||
{
|
||||
auto const& s = by_name_[i];
|
||||
by_size_[s.size()].emplace(
|
||||
s, static_cast<field>(i));
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This snippet calculates the performance
|
||||
// of the hash function and map settings
|
||||
{
|
||||
std::vector<std::size_t> hist;
|
||||
for(auto const& map : by_size_)
|
||||
{
|
||||
for(std::size_t i = 0; i < map.bucket_count(); ++i)
|
||||
{
|
||||
auto const n = map.bucket_size(i);
|
||||
if(n > 0)
|
||||
{
|
||||
if(hist.size() < n)
|
||||
hist.resize(n);
|
||||
++hist[n-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
field
|
||||
string_to_field(string_view s) const
|
||||
{
|
||||
if(s.size() >= by_size_.size())
|
||||
return field::unknown;
|
||||
auto const& map = by_size_[s.size()];
|
||||
if(map.empty())
|
||||
return field::unknown;
|
||||
auto it = map.find(s);
|
||||
if(it == map.end())
|
||||
return field::unknown;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//
|
||||
// Deprecated
|
||||
//
|
||||
|
||||
using const_iterator =
|
||||
array_type::const_iterator;
|
||||
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return by_name_.size();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return by_name_.begin();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return by_name_.end();
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
field_table const&
|
||||
get_field_table()
|
||||
{
|
||||
static field_table const tab;
|
||||
return tab;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
string_view
|
||||
to_string(field f)
|
||||
{
|
||||
auto const& v = get_field_table();
|
||||
BOOST_ASSERT(static_cast<unsigned>(f) < v.size());
|
||||
return v.begin()[static_cast<unsigned>(f)];
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
string_view
|
||||
to_string(field f)
|
||||
{
|
||||
return detail::to_string(f);
|
||||
}
|
||||
|
||||
inline
|
||||
field
|
||||
string_to_field(string_view s)
|
||||
{
|
||||
return detail::get_field_table().string_to_field(s);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,585 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
|
||||
|
||||
#if BOOST_BEAST_USE_WIN32_FILE
|
||||
|
||||
#include <boost/beast/core/async_result.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <boost/asio/basic_stream_socket.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/asio/windows/overlapped_ptr.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
#include <boost/smart_ptr/make_shared_array.hpp>
|
||||
#include <boost/detail/winapi/basic_types.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
template<class, class, bool, class>
|
||||
class write_some_win32_op;
|
||||
} // detail
|
||||
|
||||
template<>
|
||||
struct basic_file_body<file_win32>
|
||||
{
|
||||
using file_type = file_win32;
|
||||
|
||||
class reader;
|
||||
class writer;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class value_type
|
||||
{
|
||||
friend class reader;
|
||||
friend class writer;
|
||||
friend struct basic_file_body<file_win32>;
|
||||
|
||||
template<class, class, bool, class>
|
||||
friend class detail::write_some_win32_op;
|
||||
template<
|
||||
class Protocol, bool isRequest, class Fields>
|
||||
friend
|
||||
void
|
||||
write_some(
|
||||
boost::asio::basic_stream_socket<Protocol>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
file_win32 file_;
|
||||
std::uint64_t size_ = 0; // cached file size
|
||||
std::uint64_t first_; // starting offset of the range
|
||||
std::uint64_t last_; // ending offset of the range
|
||||
|
||||
public:
|
||||
~value_type() = default;
|
||||
value_type() = default;
|
||||
value_type(value_type&& other) = default;
|
||||
value_type& operator=(value_type&& other) = default;
|
||||
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
void
|
||||
close();
|
||||
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
void
|
||||
reset(file_win32&& file, error_code& ec);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class reader
|
||||
{
|
||||
template<class, class, bool, class>
|
||||
friend class detail::write_some_win32_op;
|
||||
template<
|
||||
class Protocol, bool isRequest, class Fields>
|
||||
friend
|
||||
void
|
||||
write_some(
|
||||
boost::asio::basic_stream_socket<Protocol>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
value_type& body_; // The body we are reading from
|
||||
std::uint64_t pos_; // The current position in the file
|
||||
char buf_[4096]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
reader(message<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&)
|
||||
{
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
pos_ = body_.first_;
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
std::size_t const n = (std::min)(sizeof(buf_),
|
||||
beast::detail::clamp(body_.last_ - pos_));
|
||||
if(n == 0)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
auto const nread = body_.file_.read(buf_, n, ec);
|
||||
if(ec)
|
||||
return boost::none;
|
||||
BOOST_ASSERT(nread != 0);
|
||||
pos_ += nread;
|
||||
ec.assign(0, ec.category());
|
||||
return {{
|
||||
{buf_, nread}, // buffer to return.
|
||||
pos_ < body_.last_}}; // `true` if there are more buffers.
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, basic_file_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
// VFALCO We could reserve space in the file
|
||||
boost::ignore_unused(content_length);
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
std::size_t nwritten = 0;
|
||||
for(boost::asio::const_buffer buffer : buffers)
|
||||
{
|
||||
nwritten += body_.file_.write(
|
||||
boost::asio::buffer_cast<void const*>(buffer),
|
||||
boost::asio::buffer_size(buffer),
|
||||
ec);
|
||||
if(ec)
|
||||
return nwritten;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body)
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
close()
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
file_.open(path, mode, ec);
|
||||
if(ec)
|
||||
return;
|
||||
size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
first_ = 0;
|
||||
last_ = size_;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
reset(file_win32&& file, error_code& ec)
|
||||
{
|
||||
if(file_.is_open())
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
file_ = std::move(file);
|
||||
if(file_.is_open())
|
||||
{
|
||||
size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
first_ = 0;
|
||||
last_ = size_;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Unsigned>
|
||||
inline
|
||||
boost::detail::winapi::DWORD_
|
||||
lowPart(Unsigned n)
|
||||
{
|
||||
return static_cast<
|
||||
boost::detail::winapi::DWORD_>(
|
||||
n & 0xffffffff);
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
inline
|
||||
boost::detail::winapi::DWORD_
|
||||
highPart(Unsigned n, std::true_type)
|
||||
{
|
||||
return static_cast<
|
||||
boost::detail::winapi::DWORD_>(
|
||||
(n>>32)&0xffffffff);
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
inline
|
||||
boost::detail::winapi::DWORD_
|
||||
highPart(Unsigned, std::false_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
inline
|
||||
boost::detail::winapi::DWORD_
|
||||
highPart(Unsigned n)
|
||||
{
|
||||
return highPart(n, std::integral_constant<
|
||||
bool, (sizeof(Unsigned)>4)>{});
|
||||
}
|
||||
|
||||
class null_lambda
|
||||
{
|
||||
public:
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code&,
|
||||
ConstBufferSequence const&) const
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
|
||||
|
||||
template<
|
||||
class Protocol, class Handler,
|
||||
bool isRequest, class Fields>
|
||||
class write_some_win32_op
|
||||
{
|
||||
boost::asio::basic_stream_socket<Protocol>& sock_;
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr_;
|
||||
bool header_ = false;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
write_some_win32_op(write_some_win32_op&&) = default;
|
||||
write_some_win32_op(write_some_win32_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_some_win32_op(
|
||||
DeducedHandler&& h,
|
||||
boost::asio::basic_stream_socket<Protocol>& s,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>,Fields>& sr)
|
||||
: sock_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()();
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_some_win32_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_some_win32_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_some_win32_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_some_win32_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class Protocol, class Handler,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
write_some_win32_op<
|
||||
Protocol, Handler, isRequest, Fields>::
|
||||
operator()()
|
||||
{
|
||||
if(! sr_.is_header_done())
|
||||
{
|
||||
header_ = true;
|
||||
sr_.split(true);
|
||||
return detail::async_write_some(
|
||||
sock_, sr_, std::move(*this));
|
||||
}
|
||||
if(sr_.chunked())
|
||||
{
|
||||
return detail::async_write_some(
|
||||
sock_, sr_, std::move(*this));
|
||||
}
|
||||
auto& r = sr_.reader_impl();
|
||||
boost::detail::winapi::DWORD_ const nNumberOfBytesToWrite =
|
||||
std::min<boost::detail::winapi::DWORD_>(
|
||||
beast::detail::clamp(std::min<std::uint64_t>(
|
||||
r.body_.last_ - r.pos_, sr_.limit())),
|
||||
2147483646);
|
||||
boost::asio::windows::overlapped_ptr overlapped{
|
||||
sock_.get_io_service(), *this};
|
||||
auto& ov = *overlapped.get();
|
||||
ov.Offset = lowPart(r.pos_);
|
||||
ov.OffsetHigh = highPart(r.pos_);
|
||||
auto const bSuccess = ::TransmitFile(
|
||||
sock_.native_handle(),
|
||||
sr_.get().body.file_.native_handle(),
|
||||
nNumberOfBytesToWrite,
|
||||
0,
|
||||
overlapped.get(),
|
||||
nullptr,
|
||||
0);
|
||||
auto const dwError = ::GetLastError();
|
||||
if(! bSuccess && dwError !=
|
||||
boost::detail::winapi::ERROR_IO_PENDING_)
|
||||
{
|
||||
// completed immediately
|
||||
overlapped.complete(error_code{static_cast<int>(
|
||||
boost::detail::winapi::GetLastError()),
|
||||
system_category()}, 0);
|
||||
return;
|
||||
}
|
||||
overlapped.release();
|
||||
}
|
||||
|
||||
template<
|
||||
class Protocol, class Handler,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
write_some_win32_op<
|
||||
Protocol, Handler, isRequest, Fields>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
{
|
||||
if(header_)
|
||||
{
|
||||
header_ = false;
|
||||
return (*this)();
|
||||
}
|
||||
auto& r = sr_.reader_impl();
|
||||
r.pos_ += bytes_transferred;
|
||||
BOOST_ASSERT(r.pos_ <= r.body_.last_);
|
||||
if(r.pos_ >= r.body_.last_)
|
||||
{
|
||||
sr_.next(ec, null_lambda{});
|
||||
BOOST_ASSERT(! ec);
|
||||
BOOST_ASSERT(sr_.is_done());
|
||||
if(! sr_.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
}
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Protocol, bool isRequest, class Fields>
|
||||
void
|
||||
write_some(
|
||||
boost::asio::basic_stream_socket<Protocol>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
if(! sr.is_header_done())
|
||||
{
|
||||
sr.split(true);
|
||||
detail::write_some(sock, sr, ec);
|
||||
if(ec)
|
||||
return;
|
||||
return;
|
||||
}
|
||||
if(sr.chunked())
|
||||
{
|
||||
detail::write_some(sock, sr, ec);
|
||||
if(ec)
|
||||
return;
|
||||
return;
|
||||
}
|
||||
auto& r = sr.reader_impl();
|
||||
r.body_.file_.seek(r.pos_, ec);
|
||||
if(ec)
|
||||
return;
|
||||
boost::detail::winapi::DWORD_ const nNumberOfBytesToWrite =
|
||||
std::min<boost::detail::winapi::DWORD_>(
|
||||
beast::detail::clamp(std::min<std::uint64_t>(
|
||||
r.body_.last_ - r.pos_, sr.limit())),
|
||||
2147483646);
|
||||
auto const bSuccess = ::TransmitFile(
|
||||
sock.native_handle(),
|
||||
r.body_.file_.native_handle(),
|
||||
nNumberOfBytesToWrite,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0);
|
||||
if(! bSuccess)
|
||||
{
|
||||
ec.assign(static_cast<int>(
|
||||
boost::detail::winapi::GetLastError()),
|
||||
system_category());
|
||||
return;
|
||||
}
|
||||
r.pos_ += nNumberOfBytesToWrite;
|
||||
BOOST_ASSERT(r.pos_ <= r.body_.last_);
|
||||
if(r.pos_ < r.body_.last_)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
else
|
||||
{
|
||||
sr.next(ec, detail::null_lambda{});
|
||||
BOOST_ASSERT(! ec);
|
||||
BOOST_ASSERT(sr.is_done());
|
||||
if(! sr.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
}
|
||||
|
||||
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
|
||||
|
||||
template<
|
||||
class Protocol,
|
||||
bool isRequest, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_some(
|
||||
boost::asio::basic_stream_socket<Protocol>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_some_win32_op<Protocol, handler_type<
|
||||
WriteHandler, void(error_code)>, isRequest, Fields>{
|
||||
init.completion_handler, sock, sr}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,419 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Fields>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
header<true, Fields>::
|
||||
header(Arg1&& arg1, ArgN&&... argn)
|
||||
: Fields(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
verb
|
||||
header<true, Fields>::
|
||||
method() const
|
||||
{
|
||||
return method_;
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<true, Fields>::
|
||||
method(verb v)
|
||||
{
|
||||
if(v == verb::unknown)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument{"unknown method"});
|
||||
method_ = v;
|
||||
this->set_method_impl({});
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
string_view
|
||||
header<true, Fields>::
|
||||
method_string() const
|
||||
{
|
||||
if(method_ != verb::unknown)
|
||||
return to_string(method_);
|
||||
return this->get_method_impl();
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<true, Fields>::
|
||||
method_string(string_view s)
|
||||
{
|
||||
method_ = string_to_verb(s);
|
||||
if(method_ != verb::unknown)
|
||||
this->set_method_impl({});
|
||||
else
|
||||
this->set_method_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
string_view
|
||||
header<true, Fields>::
|
||||
target() const
|
||||
{
|
||||
return this->get_target_impl();
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
void
|
||||
header<true, Fields>::
|
||||
target(string_view s)
|
||||
{
|
||||
this->set_target_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<true, Fields>& h1,
|
||||
header<true, Fields>& h2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<Fields&>(h1),
|
||||
static_cast<Fields&>(h2));
|
||||
swap(h1.version, h2.version);
|
||||
swap(h1.method_, h2.method_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Fields>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
header<false, Fields>::
|
||||
header(Arg1&& arg1, ArgN&&... argn)
|
||||
: Fields(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<class Fields>
|
||||
template<class... Args>
|
||||
header<false, Fields>::
|
||||
header(status result, unsigned version_, Args&&... args)
|
||||
: Fields(std::forward<Args>(args)...)
|
||||
, version(version_)
|
||||
, result_(result)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
status
|
||||
header<false, Fields>::
|
||||
result() const
|
||||
{
|
||||
return int_to_status(
|
||||
static_cast<int>(result_));
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
void
|
||||
header<false, Fields>::
|
||||
result(status v)
|
||||
{
|
||||
result_ = v;
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
void
|
||||
header<false, Fields>::
|
||||
result(unsigned v)
|
||||
{
|
||||
if(v > 999)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument{
|
||||
"invalid status-code"});
|
||||
result_ = static_cast<status>(v);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
unsigned
|
||||
header<false, Fields>::
|
||||
result_int() const
|
||||
{
|
||||
return static_cast<unsigned>(result_);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
string_view
|
||||
header<false, Fields>::
|
||||
reason() const
|
||||
{
|
||||
auto const s = this->get_reason_impl();
|
||||
if(! s.empty())
|
||||
return s;
|
||||
return obsolete_reason(result_);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
inline
|
||||
void
|
||||
header<false, Fields>::
|
||||
reason(string_view s)
|
||||
{
|
||||
this->set_reason_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<false, Fields>& h1,
|
||||
header<false, Fields>& h2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<Fields&>(h1),
|
||||
static_cast<Fields&>(h2));
|
||||
swap(h1.version, h2.version);
|
||||
swap(h1.result_, h2.result_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(header_type&& h, BodyArgs&&... body_args)
|
||||
: header_type(std::move(h))
|
||||
, body(std::forward<BodyArgs>(body_args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(header_type const& h, BodyArgs&&... body_args)
|
||||
: header_type(h)
|
||||
, body(std::forward<BodyArgs>(body_args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(verb method, string_view target, Version version)
|
||||
: header_type(method, target, version)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(verb method, string_view target,
|
||||
Version version, BodyArg&& body_arg)
|
||||
: header_type(method, target, version)
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class FieldsArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(
|
||||
verb method, string_view target, Version version,
|
||||
BodyArg&& body_arg,
|
||||
FieldsArg&& fields_arg)
|
||||
: header_type(method, target, version,
|
||||
std::forward<FieldsArg>(fields_arg))
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version)
|
||||
: header_type(result, version)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version,
|
||||
BodyArg&& body_arg)
|
||||
: header_type(result, version)
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class FieldsArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version,
|
||||
BodyArg&& body_arg, FieldsArg&& fields_arg)
|
||||
: header_type(result, version,
|
||||
std::forward<FieldsArg>(fields_arg))
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...> body_args)
|
||||
: message(std::piecewise_construct,
|
||||
body_args,
|
||||
beast::detail::make_index_sequence<
|
||||
sizeof...(BodyArgs)>{})
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs, class... FieldsArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...> body_args,
|
||||
std::tuple<FieldsArgs...> fields_args)
|
||||
: message(std::piecewise_construct,
|
||||
body_args,
|
||||
fields_args,
|
||||
beast::detail::make_index_sequence<
|
||||
sizeof...(BodyArgs)>{},
|
||||
beast::detail::make_index_sequence<
|
||||
sizeof...(FieldsArgs)>{})
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
chunked(bool value)
|
||||
{
|
||||
this->set_chunked_impl(value);
|
||||
this->set_content_length_impl(boost::none);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
content_length(
|
||||
boost::optional<std::uint64_t> const& value)
|
||||
{
|
||||
this->set_content_length_impl(value);
|
||||
this->set_chunked_impl(false);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
boost::optional<std::uint64_t>
|
||||
message<isRequest, Body, Fields>::
|
||||
payload_size() const
|
||||
{
|
||||
return payload_size(detail::is_body_sized<Body>{});
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_payload(std::true_type)
|
||||
{
|
||||
auto const n = payload_size();
|
||||
if(this->method() == verb::trace && (! n || *n > 0))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid request body"});
|
||||
if(n)
|
||||
{
|
||||
if(*n > 0 ||
|
||||
this->method() == verb::options ||
|
||||
this->method() == verb::put ||
|
||||
this->method() == verb::post)
|
||||
{
|
||||
this->content_length(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->chunked(false);
|
||||
}
|
||||
}
|
||||
else if(this->version >= 11)
|
||||
{
|
||||
this->chunked(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->chunked(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_payload(std::false_type)
|
||||
{
|
||||
auto const n = payload_size();
|
||||
if((status_class(this->result()) == status_class::informational ||
|
||||
this->result() == status::no_content ||
|
||||
this->result() == status::not_modified))
|
||||
{
|
||||
if(! n || *n > 0)
|
||||
// The response body MUST BE empty for this case
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid response body"});
|
||||
}
|
||||
if(n)
|
||||
this->content_length(n);
|
||||
else
|
||||
this->chunked(true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
swap(
|
||||
message<isRequest, Body, Fields>& m1,
|
||||
message<isRequest, Body, Fields>& m2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<header<isRequest, Fields>&>(m1),
|
||||
static_cast<header<isRequest, Fields>&>(m2));
|
||||
swap(m1.body, m2.body);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_PARSER_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_PARSER_IPP
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser()
|
||||
: wr_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser(Arg1&& arg1, ArgN&&... argn)
|
||||
: m_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
, wr_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
template<class OtherBody, class... Args, class>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser(parser<isRequest, OtherBody, Allocator>&& other,
|
||||
Args&&... args)
|
||||
: base_type(std::move(other))
|
||||
, m_(other.release(), std::forward<Args>(args)...)
|
||||
, wr_(m_)
|
||||
{
|
||||
if(other.wr_inited_)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"moved-from parser has a body"});
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,796 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/read_size.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
class read_some_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest, Derived>& p_;
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> mb_;
|
||||
std::size_t used_ = 0;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_some_op(read_some_op&&) = default;
|
||||
read_some_op(read_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
read_some_op(DeducedHandler&& h, Stream& s,
|
||||
DynamicBuffer& b, basic_parser<isRequest, Derived>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 2 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
void
|
||||
read_some_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
state_ = 1;
|
||||
if(b_.size() == 0)
|
||||
goto do_read;
|
||||
goto do_parse;
|
||||
|
||||
case 1:
|
||||
state_ = 2;
|
||||
case 2:
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(p_.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec.assign(0, ec.category());
|
||||
p_.put_eof(ec);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
BOOST_ASSERT(p_.is_done());
|
||||
goto upcall;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
goto upcall;
|
||||
}
|
||||
if(ec)
|
||||
goto upcall;
|
||||
b_.commit(bytes_transferred);
|
||||
|
||||
do_parse:
|
||||
{
|
||||
auto const used = p_.put(b_.data(), ec);
|
||||
used_ += used;
|
||||
b_.consume(used);
|
||||
if(! ec || ec != http::error::need_more)
|
||||
goto do_upcall;
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
do_read:
|
||||
try
|
||||
{
|
||||
mb_.emplace(b_.prepare(
|
||||
read_size_or_throw(b_, 65536)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
goto do_upcall;
|
||||
}
|
||||
return s_.async_read_some(*mb_, std::move(*this));
|
||||
|
||||
do_upcall:
|
||||
if(state_ >= 2)
|
||||
goto upcall;
|
||||
state_ = 3;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
upcall:
|
||||
h_(ec, used_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct parser_is_done
|
||||
{
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
operator()(basic_parser<
|
||||
isRequest, Derived> const& p) const
|
||||
{
|
||||
return p.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct parser_is_header_done
|
||||
{
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
operator()(basic_parser<
|
||||
isRequest, Derived> const& p) const
|
||||
{
|
||||
return p.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Condition,
|
||||
class Handler>
|
||||
class read_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest, Derived>& p_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
read_op(DeducedHandler&& h, Stream& s,
|
||||
DynamicBuffer& b, basic_parser<isRequest,
|
||||
Derived>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_used = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 3 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Condition,
|
||||
class Handler>
|
||||
void
|
||||
read_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Condition, Handler>::
|
||||
operator()(error_code ec, std::size_t)
|
||||
{
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
if(Condition{}(p_))
|
||||
{
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec));
|
||||
}
|
||||
state_ = 2;
|
||||
|
||||
do_read:
|
||||
return async_read_some(
|
||||
s_, b_, p_, std::move(*this));
|
||||
|
||||
case 1:
|
||||
goto upcall;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(Condition{}(p_))
|
||||
goto upcall;
|
||||
state_ = 3;
|
||||
goto do_read;
|
||||
}
|
||||
upcall:
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
class Handler>
|
||||
class read_msg_op
|
||||
{
|
||||
using parser_type =
|
||||
parser<isRequest, Body, Allocator>;
|
||||
|
||||
using message_type =
|
||||
typename parser_type::value_type;
|
||||
|
||||
struct data
|
||||
{
|
||||
int state = 0;
|
||||
Stream& s;
|
||||
DynamicBuffer& b;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
|
||||
data(Handler&, Stream& s_,
|
||||
DynamicBuffer& b_, message_type& m_)
|
||||
: s(s_)
|
||||
, b(b_)
|
||||
, m(m_)
|
||||
, p(std::move(m))
|
||||
{
|
||||
p.eager(true);
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_msg_op(read_msg_op&&) = default;
|
||||
read_msg_op(read_msg_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_used = 0);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->d_->state >= 2 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
class Handler>
|
||||
void
|
||||
read_msg_op<Stream, DynamicBuffer,
|
||||
isRequest, Body, Allocator, Handler>::
|
||||
operator()(error_code ec, std::size_t)
|
||||
{
|
||||
auto& d = *d_;
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
|
||||
do_read:
|
||||
return async_read_some(
|
||||
d.s, d.b, d.p, std::move(*this));
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(d.p.is_done())
|
||||
{
|
||||
d.m = d.p.release();
|
||||
goto upcall;
|
||||
}
|
||||
d.state = 2;
|
||||
goto do_read;
|
||||
}
|
||||
upcall:
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
error_code ec;
|
||||
auto const bytes_used = read_some(
|
||||
stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_used;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
std::size_t bytes_used = 0;
|
||||
if(buffer.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
// invoke parser
|
||||
{
|
||||
auto const used = parser.put(buffer.data(), ec);
|
||||
bytes_used += used;
|
||||
buffer.consume(used);
|
||||
if(! ec)
|
||||
break;
|
||||
if(ec != http::error::need_more)
|
||||
break;
|
||||
}
|
||||
do_read:
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> b;
|
||||
try
|
||||
{
|
||||
b.emplace(buffer.prepare(
|
||||
read_size_or_throw(buffer, 65536)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return bytes_used;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*b, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(parser.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
parser.put_eof(ec);
|
||||
if(ec)
|
||||
break;
|
||||
BOOST_ASSERT(parser.is_done());
|
||||
break;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
break;
|
||||
buffer.commit(bytes_transferred);
|
||||
}
|
||||
return bytes_used;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code, std::size_t)>
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
async_completion<ReadHandler, void(error_code)> init{handler};
|
||||
detail::read_some_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, Derived, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
error_code ec;
|
||||
read_header(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(false);
|
||||
if(parser.is_header_done())
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
read_some(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
while(! parser.is_header_done());
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(false);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Derived, detail::parser_is_header_done,
|
||||
handler_type<ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
error_code ec;
|
||||
read(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(true);
|
||||
if(parser.is_done())
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
read_some(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
while(! parser.is_done());
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(true);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Derived, detail::parser_is_done,
|
||||
handler_type<ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
error_code ec;
|
||||
read(stream, buffer, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
parser<isRequest, Body, Allocator> p{std::move(msg)};
|
||||
p.eager(true);
|
||||
read(stream, buffer, p.base(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
msg = p.release();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
class ReadHandler>
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_msg_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Body, Allocator, handler_type<
|
||||
ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, msg}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,572 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <iterator>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class param_list::const_iterator
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
std::string s_;
|
||||
detail::param_iter pi_;
|
||||
|
||||
public:
|
||||
using value_type = param_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.pi_.it == pi_.it &&
|
||||
other.pi_.last == pi_.last &&
|
||||
other.pi_.first == pi_.first;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return pi_.v;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class param_list;
|
||||
|
||||
const_iterator(iter_type first, iter_type last)
|
||||
{
|
||||
pi_.it = first;
|
||||
pi_.first = first;
|
||||
pi_.last = last;
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
static
|
||||
std::string
|
||||
unquote(string_view sr);
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class>
|
||||
std::string
|
||||
param_list::const_iterator::
|
||||
unquote(string_view sr)
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(sr.size());
|
||||
auto it = sr.begin() + 1;
|
||||
auto end = sr.end() - 1;
|
||||
while(it != end)
|
||||
{
|
||||
if(*it == '\\')
|
||||
++it;
|
||||
s.push_back(*it);
|
||||
++it;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
param_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
s_.clear();
|
||||
pi_.increment();
|
||||
if(pi_.empty())
|
||||
{
|
||||
pi_.it = pi_.last;
|
||||
pi_.first = pi_.last;
|
||||
}
|
||||
else if(! pi_.v.second.empty() &&
|
||||
pi_.v.second.front() == '"')
|
||||
{
|
||||
s_ = unquote(pi_.v.second);
|
||||
pi_.v.second = string_view{
|
||||
s_.data(), s_.size()};
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ext_list::const_iterator
|
||||
{
|
||||
ext_list::value_type v_;
|
||||
iter_type it_;
|
||||
iter_type first_;
|
||||
iter_type last_;
|
||||
|
||||
public:
|
||||
using value_type = ext_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.it_ == it_ &&
|
||||
other.first_ == first_ &&
|
||||
other.last_ == last_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ext_list;
|
||||
|
||||
const_iterator(iter_type begin, iter_type end)
|
||||
{
|
||||
it_ = begin;
|
||||
first_ = begin;
|
||||
last_ = end;
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
auto
|
||||
ext_list::
|
||||
find(T const& s) ->
|
||||
const_iterator
|
||||
{
|
||||
return std::find_if(begin(), end(),
|
||||
[&s](value_type const& v)
|
||||
{
|
||||
return iequals(s, v.first);
|
||||
});
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool
|
||||
ext_list::
|
||||
exists(T const& s)
|
||||
{
|
||||
return find(s) != end();
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
ext_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-list
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS "=" OWS ( token / quoted-string )
|
||||
|
||||
chunked;a=b;i=j,gzip;windowBits=12
|
||||
x,y
|
||||
,,,,,chameleon
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_.first = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_.first = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
detail::param_iter pi;
|
||||
pi.it = it_;
|
||||
pi.first = it_;
|
||||
pi.last = last_;
|
||||
for(;;)
|
||||
{
|
||||
pi.increment();
|
||||
if(pi.empty())
|
||||
break;
|
||||
}
|
||||
v_.second = param_list{string_view{&*it_,
|
||||
static_cast<std::size_t>(pi.it - it_)}};
|
||||
it_ = pi.it;
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class token_list::const_iterator
|
||||
{
|
||||
token_list::value_type v_;
|
||||
iter_type it_;
|
||||
iter_type first_;
|
||||
iter_type last_;
|
||||
|
||||
public:
|
||||
using value_type = token_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.it_ == it_ &&
|
||||
other.first_ == first_ &&
|
||||
other.last_ == last_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class token_list;
|
||||
|
||||
const_iterator(iter_type begin, iter_type end)
|
||||
{
|
||||
it_ = begin;
|
||||
first_ = begin;
|
||||
last_ = end;
|
||||
increment();
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
token_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_ = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_ = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool
|
||||
token_list::
|
||||
exists(T const& s)
|
||||
{
|
||||
return std::find_if(begin(), end(),
|
||||
[&s](value_type const& v)
|
||||
{
|
||||
return iequals(s, v);
|
||||
}
|
||||
) != end();
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
bool
|
||||
validate_list(detail::basic_parsed_list<
|
||||
Policy> const& list)
|
||||
{
|
||||
auto const last = list.end();
|
||||
auto it = list.begin();
|
||||
if(it.error())
|
||||
return false;
|
||||
while(it != last)
|
||||
{
|
||||
++it;
|
||||
if(it.error())
|
||||
return false;
|
||||
if(it == last)
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP
|
||||
|
||||
#include <boost/beast/core/detail/buffers_ref.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
frdinit(std::true_type)
|
||||
{
|
||||
frd_.emplace(m_, m_.version, m_.method());
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
frdinit(std::false_type)
|
||||
{
|
||||
frd_.emplace(m_, m_.version, m_.result_int());
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
template<class T1, class T2, class Visit>
|
||||
inline
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
do_visit(error_code& ec, Visit& visit)
|
||||
{
|
||||
// VFALCO work-around for missing variant::emplace
|
||||
pv_.~variant();
|
||||
new(&pv_) decltype(pv_){
|
||||
T1{limit_, boost::get<T2>(v_)}};
|
||||
visit(ec, beast::detail::make_buffers_ref(
|
||||
boost::get<T1>(pv_)));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
serializer<isRequest, Body, Fields>::
|
||||
serializer(value_type& m)
|
||||
: m_(m)
|
||||
, rd_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
template<class Visit>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
next(error_code& ec, Visit&& visit)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using beast::detail::make_buffers_ref;
|
||||
switch(s_)
|
||||
{
|
||||
case do_construct:
|
||||
{
|
||||
frdinit(std::integral_constant<bool,
|
||||
isRequest>{});
|
||||
keep_alive_ = m_.keep_alive();
|
||||
chunked_ = m_.chunked();
|
||||
if(chunked_)
|
||||
goto go_init_c;
|
||||
s_ = do_init;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_init:
|
||||
{
|
||||
rd_.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(split_)
|
||||
goto go_header_only;
|
||||
auto result = rd_.get(ec);
|
||||
if(ec == error::need_more)
|
||||
goto go_header_only;
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_header_only;
|
||||
more_ = result->second;
|
||||
v_ = cb2_t{
|
||||
boost::in_place_init,
|
||||
frd_->get(),
|
||||
result->first};
|
||||
s_ = do_header;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_header:
|
||||
do_visit<pcb2_t, cb2_t>(ec, visit);
|
||||
break;
|
||||
|
||||
go_header_only:
|
||||
v_ = cb1_t{frd_->get()};
|
||||
s_ = do_header_only;
|
||||
case do_header_only:
|
||||
do_visit<pcb1_t, cb1_t>(ec, visit);
|
||||
break;
|
||||
|
||||
case do_body:
|
||||
s_ = do_body + 1;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case do_body + 1:
|
||||
{
|
||||
auto result = rd_.get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_complete;
|
||||
more_ = result->second;
|
||||
v_ = cb3_t{result->first};
|
||||
s_ = do_body + 2;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_body + 2:
|
||||
do_visit<pcb3_t, cb3_t>(ec, visit);
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_init_c:
|
||||
s_ = do_init_c;
|
||||
case do_init_c:
|
||||
{
|
||||
rd_.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(split_)
|
||||
goto go_header_only_c;
|
||||
auto result = rd_.get(ec);
|
||||
if(ec == error::need_more)
|
||||
goto go_header_only_c;
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_header_only_c;
|
||||
more_ = result->second;
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
if(! more_)
|
||||
{
|
||||
// do it all in one buffer
|
||||
v_ = cb7_t{
|
||||
boost::in_place_init,
|
||||
frd_->get(),
|
||||
buffer_size(result->first),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{},
|
||||
detail::chunk_last(),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{}};
|
||||
goto go_all_c;
|
||||
}
|
||||
#endif
|
||||
v_ = cb4_t{
|
||||
boost::in_place_init,
|
||||
frd_->get(),
|
||||
buffer_size(result->first),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{}};
|
||||
s_ = do_header_c;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_header_c:
|
||||
do_visit<pcb4_t, cb4_t>(ec, visit);
|
||||
break;
|
||||
|
||||
go_header_only_c:
|
||||
v_ = cb1_t{frd_->get()};
|
||||
s_ = do_header_only_c;
|
||||
case do_header_only_c:
|
||||
do_visit<pcb1_t, cb1_t>(ec, visit);
|
||||
break;
|
||||
|
||||
case do_body_c:
|
||||
s_ = do_body_c + 1;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case do_body_c + 1:
|
||||
{
|
||||
auto result = rd_.get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_final_c;
|
||||
more_ = result->second;
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
if(! more_)
|
||||
{
|
||||
// do it all in one buffer
|
||||
v_ = cb6_t{
|
||||
boost::in_place_init,
|
||||
buffer_size(result->first),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{},
|
||||
detail::chunk_last(),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{}};
|
||||
goto go_body_final_c;
|
||||
}
|
||||
#endif
|
||||
v_ = cb5_t{
|
||||
boost::in_place_init,
|
||||
buffer_size(result->first),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{}};
|
||||
s_ = do_body_c + 2;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
do_visit<pcb5_t, cb5_t>(ec, visit);
|
||||
break;
|
||||
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
go_body_final_c:
|
||||
s_ = do_body_final_c;
|
||||
case do_body_final_c:
|
||||
do_visit<pcb6_t, cb6_t>(ec, visit);
|
||||
break;
|
||||
|
||||
go_all_c:
|
||||
s_ = do_all_c;
|
||||
case do_all_c:
|
||||
do_visit<pcb7_t, cb7_t>(ec, visit);
|
||||
break;
|
||||
#endif
|
||||
|
||||
go_final_c:
|
||||
case do_final_c:
|
||||
v_ = cb8_t{
|
||||
boost::in_place_init,
|
||||
detail::chunk_last(),
|
||||
boost::asio::const_buffers_1{nullptr, 0},
|
||||
chunk_crlf{}};
|
||||
s_ = do_final_c + 1;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case do_final_c + 1:
|
||||
do_visit<pcb8_t, cb8_t>(ec, visit);
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
case do_complete:
|
||||
BOOST_ASSERT(false);
|
||||
break;
|
||||
|
||||
go_complete:
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
switch(s_)
|
||||
{
|
||||
case do_header:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb2_t>(v_)));
|
||||
boost::get<cb2_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb2_t>(v_)) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_ = boost::blank{};
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
|
||||
case do_header_only:
|
||||
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;
|
||||
frd_ = boost::none;
|
||||
header_done_ = true;
|
||||
if(! split_)
|
||||
goto go_complete;
|
||||
s_ = do_body;
|
||||
break;
|
||||
|
||||
case do_body + 2:
|
||||
{
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb3_t>(v_)));
|
||||
boost::get<cb3_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb3_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<cb4_t>(v_)));
|
||||
boost::get<cb4_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb4_t>(v_)) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_ = boost::blank{};
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
case do_header_only_c:
|
||||
{
|
||||
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;
|
||||
frd_ = boost::none;
|
||||
header_done_ = true;
|
||||
if(! split_)
|
||||
{
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
}
|
||||
s_ = do_body_c;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
BOOST_ASSERT(n <= buffer_size(
|
||||
boost::get<cb5_t>(v_)));
|
||||
boost::get<cb5_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb5_t>(v_)) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
case do_body_final_c:
|
||||
{
|
||||
auto& b = boost::get<cb6_t>(v_);
|
||||
BOOST_ASSERT(n <= buffer_size(b));
|
||||
b.consume(n);
|
||||
if(buffer_size(b) > 0)
|
||||
break;
|
||||
v_ = boost::blank{};
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_all_c:
|
||||
{
|
||||
auto& b = boost::get<cb7_t>(v_);
|
||||
BOOST_ASSERT(n <= buffer_size(b));
|
||||
b.consume(n);
|
||||
if(buffer_size(b) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_ = boost::blank{};
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case do_final_c + 1:
|
||||
BOOST_ASSERT(buffer_size(
|
||||
boost::get<cb8_t>(v_)));
|
||||
boost::get<cb8_t>(v_).consume(n);
|
||||
if(buffer_size(boost::get<cb8_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
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,252 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_STATUS_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_STATUS_IPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
status
|
||||
int_to_status(unsigned v)
|
||||
{
|
||||
switch(static_cast<status>(v))
|
||||
{
|
||||
// 1xx
|
||||
case status::continue_:
|
||||
case status::switching_protocols:
|
||||
case status::processing:
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
// 2xx
|
||||
case status::ok:
|
||||
case status::created:
|
||||
case status::accepted:
|
||||
case status::non_authoritative_information:
|
||||
case status::no_content:
|
||||
case status::reset_content:
|
||||
case status::partial_content:
|
||||
case status::multi_status:
|
||||
case status::already_reported:
|
||||
case status::im_used:
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
// 3xx
|
||||
case status::multiple_choices:
|
||||
case status::moved_permanently:
|
||||
case status::found:
|
||||
case status::see_other:
|
||||
case status::not_modified:
|
||||
case status::use_proxy:
|
||||
case status::temporary_redirect:
|
||||
case status::permanent_redirect:
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
// 4xx
|
||||
case status::bad_request:
|
||||
case status::unauthorized:
|
||||
case status::payment_required:
|
||||
case status::forbidden:
|
||||
case status::not_found:
|
||||
case status::method_not_allowed:
|
||||
case status::not_acceptable:
|
||||
case status::proxy_authentication_required:
|
||||
case status::request_timeout:
|
||||
case status::conflict:
|
||||
case status::gone:
|
||||
case status::length_required:
|
||||
case status::precondition_failed:
|
||||
case status::payload_too_large:
|
||||
case status::uri_too_long:
|
||||
case status::unsupported_media_type:
|
||||
case status::range_not_satisfiable:
|
||||
case status::expectation_failed:
|
||||
case status::misdirected_request:
|
||||
case status::unprocessable_entity:
|
||||
case status::locked:
|
||||
case status::failed_dependency:
|
||||
case status::upgrade_required:
|
||||
case status::precondition_required:
|
||||
case status::too_many_requests:
|
||||
case status::request_header_fields_too_large:
|
||||
case status::connection_closed_without_response:
|
||||
case status::unavailable_for_legal_reasons:
|
||||
case status::client_closed_request:
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
// 5xx
|
||||
case status::internal_server_error:
|
||||
case status::not_implemented:
|
||||
case status::bad_gateway:
|
||||
case status::service_unavailable:
|
||||
case status::gateway_timeout:
|
||||
case status::http_version_not_supported:
|
||||
case status::variant_also_negotiates:
|
||||
case status::insufficient_storage:
|
||||
case status::loop_detected:
|
||||
case status::not_extended:
|
||||
case status::network_authentication_required:
|
||||
case status::network_connect_timeout_error:
|
||||
return static_cast<status>(v);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return status::unknown;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
string_view
|
||||
status_to_string(unsigned v)
|
||||
{
|
||||
switch(static_cast<status>(v))
|
||||
{
|
||||
// 1xx
|
||||
case status::continue_: return "Continue";
|
||||
case status::switching_protocols: return "Switching Protocols";
|
||||
case status::processing: return "Processing";
|
||||
|
||||
// 2xx
|
||||
case status::ok: return "OK";
|
||||
case status::created: return "Created";
|
||||
case status::accepted: return "Accepted";
|
||||
case status::non_authoritative_information: return "Non-Authoritative Information";
|
||||
case status::no_content: return "No Content";
|
||||
case status::reset_content: return "Reset Content";
|
||||
case status::partial_content: return "Partial Content";
|
||||
case status::multi_status: return "Multi-Status";
|
||||
case status::already_reported: return "Already Reported";
|
||||
case status::im_used: return "IM Used";
|
||||
|
||||
// 3xx
|
||||
case status::multiple_choices: return "Multiple Choices";
|
||||
case status::moved_permanently: return "Moved Permanently";
|
||||
case status::found: return "Found";
|
||||
case status::see_other: return "See Other";
|
||||
case status::not_modified: return "Not Modified";
|
||||
case status::use_proxy: return "Use Proxy";
|
||||
case status::temporary_redirect: return "Temporary Redirect";
|
||||
case status::permanent_redirect: return "Permanent Redirect";
|
||||
|
||||
// 4xx
|
||||
case status::bad_request: return "Bad Request";
|
||||
case status::unauthorized: return "Unauthorized";
|
||||
case status::payment_required: return "Payment Required";
|
||||
case status::forbidden: return "Forbidden";
|
||||
case status::not_found: return "Not Found";
|
||||
case status::method_not_allowed: return "Method Not Allowed";
|
||||
case status::not_acceptable: return "Not Acceptable";
|
||||
case status::proxy_authentication_required: return "Proxy Authentication Required";
|
||||
case status::request_timeout: return "Request Timeout";
|
||||
case status::conflict: return "Conflict";
|
||||
case status::gone: return "Gone";
|
||||
case status::length_required: return "Length Required";
|
||||
case status::precondition_failed: return "Precondition Failed";
|
||||
case status::payload_too_large: return "Payload Too Large";
|
||||
case status::uri_too_long: return "URI Too Long";
|
||||
case status::unsupported_media_type: return "Unsupported Media Type";
|
||||
case status::range_not_satisfiable: return "Range Not Satisfiable";
|
||||
case status::expectation_failed: return "Expectation Failed";
|
||||
case status::misdirected_request: return "Misdirected Request";
|
||||
case status::unprocessable_entity: return "Unprocessable Entity";
|
||||
case status::locked: return "Locked";
|
||||
case status::failed_dependency: return "Failed Dependency";
|
||||
case status::upgrade_required: return "Upgrade Required";
|
||||
case status::precondition_required: return "Precondition Required";
|
||||
case status::too_many_requests: return "Too Many Requests";
|
||||
case status::request_header_fields_too_large: return "Request Header Fields Too Large";
|
||||
case status::connection_closed_without_response: return "Connection Closed Without Response";
|
||||
case status::unavailable_for_legal_reasons: return "Unavailable For Legal Reasons";
|
||||
case status::client_closed_request: return "Client Closed Request";
|
||||
// 5xx
|
||||
case status::internal_server_error: return "Internal Server Error";
|
||||
case status::not_implemented: return "Not Implemented";
|
||||
case status::bad_gateway: return "Bad Gateway";
|
||||
case status::service_unavailable: return "Service Unavailable";
|
||||
case status::gateway_timeout: return "Gateway Timeout";
|
||||
case status::http_version_not_supported: return "HTTP Version Not Supported";
|
||||
case status::variant_also_negotiates: return "Variant Also Negotiates";
|
||||
case status::insufficient_storage: return "Insufficient Storage";
|
||||
case status::loop_detected: return "Loop Detected";
|
||||
case status::not_extended: return "Not Extended";
|
||||
case status::network_authentication_required: return "Network Authentication Required";
|
||||
case status::network_connect_timeout_error: return "Network Connect Timeout Error";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<unknown-status>";
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
status_class
|
||||
to_status_class(unsigned v)
|
||||
{
|
||||
switch(v / 100)
|
||||
{
|
||||
case 1: return status_class::informational;
|
||||
case 2: return status_class::successful;
|
||||
case 3: return status_class::redirection;
|
||||
case 4: return status_class::client_error;
|
||||
case 5: return status_class::server_error;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return status_class::unknown;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
status
|
||||
int_to_status(unsigned v)
|
||||
{
|
||||
return detail::int_to_status(v);
|
||||
}
|
||||
|
||||
inline
|
||||
status_class
|
||||
to_status_class(unsigned v)
|
||||
{
|
||||
return detail::to_status_class(v);
|
||||
}
|
||||
|
||||
inline
|
||||
status_class
|
||||
to_status_class(status v)
|
||||
{
|
||||
return to_status_class(static_cast<int>(v));
|
||||
}
|
||||
|
||||
inline
|
||||
string_view
|
||||
obsolete_reason(status v)
|
||||
{
|
||||
return detail::status_to_string(
|
||||
static_cast<unsigned>(v));
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, status v)
|
||||
{
|
||||
return os << obsolete_reason(v);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,337 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_VERB_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_VERB_IPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
inline
|
||||
string_view
|
||||
verb_to_string(verb v)
|
||||
{
|
||||
switch(v)
|
||||
{
|
||||
case verb::delete_: return "DELETE";
|
||||
case verb::get: return "GET";
|
||||
case verb::head: return "HEAD";
|
||||
case verb::post: return "POST";
|
||||
case verb::put: return "PUT";
|
||||
case verb::connect: return "CONNECT";
|
||||
case verb::options: return "OPTIONS";
|
||||
case verb::trace: return "TRACE";
|
||||
|
||||
case verb::copy: return "COPY";
|
||||
case verb::lock: return "LOCK";
|
||||
case verb::mkcol: return "MKCOL";
|
||||
case verb::move: return "MOVE";
|
||||
case verb::propfind: return "PROPFIND";
|
||||
case verb::proppatch: return "PROPPATCH";
|
||||
case verb::search: return "SEARCH";
|
||||
case verb::unlock: return "UNLOCK";
|
||||
case verb::bind: return "BIND";
|
||||
case verb::rebind: return "REBIND";
|
||||
case verb::unbind: return "UNBIND";
|
||||
case verb::acl: return "ACL";
|
||||
|
||||
case verb::report: return "REPORT";
|
||||
case verb::mkactivity: return "MKACTIVITY";
|
||||
case verb::checkout: return "CHECKOUT";
|
||||
case verb::merge: return "MERGE";
|
||||
|
||||
case verb::msearch: return "M-SEARCH";
|
||||
case verb::notify: return "NOTIFY";
|
||||
case verb::subscribe: return "SUBSCRIBE";
|
||||
case verb::unsubscribe: return "UNSUBSCRIBE";
|
||||
|
||||
case verb::patch: return "PATCH";
|
||||
case verb::purge: return "PURGE";
|
||||
|
||||
case verb::mkcalendar: return "MKCALENDAR";
|
||||
|
||||
case verb::link: return "LINK";
|
||||
case verb::unlink: return "UNLINK";
|
||||
|
||||
case verb::unknown:
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"});
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
verb
|
||||
string_to_verb(string_view v)
|
||||
{
|
||||
/*
|
||||
ACL
|
||||
BIND
|
||||
CHECKOUT
|
||||
CONNECT
|
||||
COPY
|
||||
DELETE
|
||||
GET
|
||||
HEAD
|
||||
LINK
|
||||
LOCK
|
||||
M-SEARCH
|
||||
MERGE
|
||||
MKACTIVITY
|
||||
MKCALENDAR
|
||||
MKCOL
|
||||
MOVE
|
||||
NOTIFY
|
||||
OPTIONS
|
||||
PATCH
|
||||
POST
|
||||
PROPFIND
|
||||
PROPPATCH
|
||||
PURGE
|
||||
PUT
|
||||
REBIND
|
||||
REPORT
|
||||
SEARCH
|
||||
SUBSCRIBE
|
||||
TRACE
|
||||
UNBIND
|
||||
UNLINK
|
||||
UNLOCK
|
||||
UNSUBSCRIBE
|
||||
*/
|
||||
if(v.size() < 3)
|
||||
return verb::unknown;
|
||||
// s must be null terminated
|
||||
auto const eq =
|
||||
[](string_view sv, char const* s)
|
||||
{
|
||||
auto p = sv.data();
|
||||
for(;;)
|
||||
{
|
||||
if(*s != *p)
|
||||
return false;
|
||||
++s;
|
||||
++p;
|
||||
if(! *s)
|
||||
return p == sv.end();
|
||||
}
|
||||
};
|
||||
auto c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'A':
|
||||
if(v == "CL")
|
||||
return verb::acl;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
if(v == "IND")
|
||||
return verb::bind;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'H':
|
||||
if(eq(v, "ECKOUT"))
|
||||
return verb::checkout;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(eq(v, "NNECT"))
|
||||
return verb::connect;
|
||||
if(eq(v, "PY"))
|
||||
return verb::copy;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
if(eq(v, "ELETE"))
|
||||
return verb::delete_;
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
if(eq(v, "ET"))
|
||||
return verb::get;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
if(eq(v, "EAD"))
|
||||
return verb::head;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
if(eq(v, "INK"))
|
||||
return verb::link;
|
||||
if(eq(v, "OCK"))
|
||||
return verb::lock;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case '-':
|
||||
if(eq(v, "SEARCH"))
|
||||
return verb::msearch;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
if(eq(v, "RGE"))
|
||||
return verb::merge;
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
if(eq(v, "ACTIVITY"))
|
||||
return verb::mkactivity;
|
||||
if(v[0] == 'C')
|
||||
{
|
||||
v.remove_prefix(1);
|
||||
if(eq(v, "ALENDAR"))
|
||||
return verb::mkcalendar;
|
||||
if(eq(v, "OL"))
|
||||
return verb::mkcol;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(eq(v, "VE"))
|
||||
return verb::move;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
if(eq(v, "OTIFY"))
|
||||
return verb::notify;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(eq(v, "PTIONS"))
|
||||
return verb::options;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'A':
|
||||
if(eq(v, "TCH"))
|
||||
return verb::patch;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(eq(v, "ST"))
|
||||
return verb::post;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if(eq(v, "OPFIND"))
|
||||
return verb::propfind;
|
||||
if(eq(v, "OPPATCH"))
|
||||
return verb::proppatch;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
if(eq(v, "RGE"))
|
||||
return verb::purge;
|
||||
if(eq(v, "T"))
|
||||
return verb::put;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if(v[0] != 'E')
|
||||
break;
|
||||
v.remove_prefix(1);
|
||||
if(eq(v, "BIND"))
|
||||
return verb::rebind;
|
||||
if(eq(v, "PORT"))
|
||||
return verb::report;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if(eq(v, "EARCH"))
|
||||
return verb::search;
|
||||
if(eq(v, "UBSCRIBE"))
|
||||
return verb::subscribe;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if(eq(v, "RACE"))
|
||||
return verb::trace;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
if(v[0] != 'N')
|
||||
break;
|
||||
v.remove_prefix(1);
|
||||
if(eq(v, "BIND"))
|
||||
return verb::unbind;
|
||||
if(eq(v, "LINK"))
|
||||
return verb::unlink;
|
||||
if(eq(v, "LOCK"))
|
||||
return verb::unlock;
|
||||
if(eq(v, "SUBSCRIBE"))
|
||||
return verb::unsubscribe;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return verb::unknown;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
string_view
|
||||
to_string(verb v)
|
||||
{
|
||||
return detail::verb_to_string(v);
|
||||
}
|
||||
|
||||
inline
|
||||
verb
|
||||
string_to_verb(string_view s)
|
||||
{
|
||||
return detail::string_to_verb(s);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,890 @@
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_WRITE_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_WRITE_IPP
|
||||
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/ostream.hpp>
|
||||
#include <boost/beast/core/handler_alloc.hpp>
|
||||
#include <boost/beast/core/handler_ptr.hpp>
|
||||
#include <boost/beast/core/type_traits.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_some_op
|
||||
{
|
||||
Stream& s_;
|
||||
serializer<isRequest,Body, Fields>& sr_;
|
||||
Handler h_;
|
||||
|
||||
class lambda
|
||||
{
|
||||
write_some_op& op_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
|
||||
explicit
|
||||
lambda(write_some_op& op)
|
||||
: op_(op)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
invoked = true;
|
||||
ec.assign(0, ec.category());
|
||||
return op_.s_.async_write_some(
|
||||
buffers, std::move(op_));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
write_some_op(write_some_op&&) = default;
|
||||
write_some_op(write_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_some_op(DeducedHandler&& h, Stream& s,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
: s_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()();
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some_op<
|
||||
Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()()
|
||||
{
|
||||
error_code ec;
|
||||
if(! sr_.is_done())
|
||||
{
|
||||
lambda f{*this};
|
||||
sr_.next(ec, f);
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
if(f.invoked)
|
||||
{
|
||||
// *this has been moved from,
|
||||
// cannot access members here.
|
||||
return;
|
||||
}
|
||||
// What else could it be?
|
||||
BOOST_ASSERT(sr_.is_done());
|
||||
}
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
template<
|
||||
class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some_op<
|
||||
Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
{
|
||||
sr_.consume(bytes_transferred);
|
||||
if(sr_.is_done())
|
||||
if(! sr_.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct serializer_is_header_done
|
||||
{
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool
|
||||
operator()(
|
||||
serializer<isRequest, Body, Fields>& sr) const
|
||||
{
|
||||
return sr.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct serializer_is_done
|
||||
{
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool
|
||||
operator()(
|
||||
serializer<isRequest, Body, Fields>& sr) const
|
||||
{
|
||||
return sr.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Stream, class Handler, class Predicate,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields>& sr_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_op(DeducedHandler&& h, Stream& s,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
: s_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 3 ||
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class Stream, class Handler, class Predicate,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_op<Stream, Handler, Predicate,
|
||||
isRequest, Body, Fields>::
|
||||
operator()(error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
goto upcall;
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if(Predicate{}(sr_))
|
||||
{
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec));
|
||||
}
|
||||
state_ = 2;
|
||||
return beast::http::async_write_some(
|
||||
s_, sr_, std::move(*this));
|
||||
}
|
||||
|
||||
case 1:
|
||||
goto upcall;
|
||||
|
||||
case 2:
|
||||
state_ = 3;
|
||||
BOOST_BEAST_FALLTHROUGH;
|
||||
|
||||
case 3:
|
||||
{
|
||||
if(Predicate{}(sr_))
|
||||
goto upcall;
|
||||
return beast::http::async_write_some(
|
||||
s_, sr_, std::move(*this));
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_msg_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
serializer<isRequest, Body, Fields> sr;
|
||||
|
||||
data(Handler&, Stream& s_, message<
|
||||
isRequest, Body, Fields>& m_)
|
||||
: s(s_)
|
||||
, sr(m_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_msg_op(write_msg_op&&) = default;
|
||||
write_msg_op(write_msg_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()();
|
||||
|
||||
void
|
||||
operator()(error_code ec);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_msg_op<
|
||||
Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
return async_write(d.s, d.sr, std::move(*this));
|
||||
}
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_msg_op<
|
||||
Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()(error_code ec)
|
||||
{
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream>
|
||||
class write_some_lambda
|
||||
{
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_some_lambda(Stream& stream)
|
||||
: stream_(stream)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred =
|
||||
stream_.write_some(buffers, ec);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream>
|
||||
class write_lambda
|
||||
{
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_lambda(Stream& stream)
|
||||
: stream_(stream)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred = boost::asio::write(
|
||||
stream_, buffers, ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
if(! sr.is_done())
|
||||
{
|
||||
write_some_lambda<SyncWriteStream> f{stream};
|
||||
sr.next(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
if(f.invoked)
|
||||
sr.consume(f.bytes_transferred);
|
||||
if(sr.is_done())
|
||||
if(! sr.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
return;
|
||||
}
|
||||
if(! sr.keep_alive())
|
||||
ec = error::end_of_stream;
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_some(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_some_op<AsyncWriteStream,
|
||||
handler_type<WriteHandler, void(error_code)>,
|
||||
isRequest, Body, Fields>{
|
||||
init.completion_handler, stream, sr}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader 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>
|
||||
void
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
detail::write_some(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_some(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
return detail::async_write_some(stream, sr,
|
||||
std::forward<WriteHandler>(handler));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_header(SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write_header(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_header(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(true);
|
||||
if(! sr.is_header_done())
|
||||
{
|
||||
detail::write_lambda<SyncWriteStream> f{stream};
|
||||
do
|
||||
{
|
||||
sr.next(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(f.invoked);
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
while(! sr.is_header_done());
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_header(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(true);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, handler_type<
|
||||
WriteHandler, void(error_code)>,
|
||||
detail::serializer_is_header_done,
|
||||
isRequest, Body, Fields>{
|
||||
init.completion_handler, stream, sr}(
|
||||
error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
error_code ec;
|
||||
write(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
sr.split(false);
|
||||
for(;;)
|
||||
{
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(false);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, handler_type<
|
||||
WriteHandler, void(error_code)>,
|
||||
detail::serializer_is_done, isRequest, Body, Fields>{
|
||||
init.completion_handler, stream, sr}(error_code{});
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
write(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
async_return_type<
|
||||
WriteHandler, void(error_code)>
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_msg_op<AsyncWriteStream, handler_type<
|
||||
WriteHandler, void(error_code)>, isRequest,
|
||||
Body, Fields>{init.completion_handler,
|
||||
stream, msg}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Serializer>
|
||||
class write_ostream_lambda
|
||||
{
|
||||
std::ostream& os_;
|
||||
Serializer& sr_;
|
||||
|
||||
public:
|
||||
write_ostream_lambda(std::ostream& os,
|
||||
Serializer& sr)
|
||||
: os_(os)
|
||||
, sr_(sr)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers) const
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
if(os_.fail())
|
||||
return;
|
||||
std::size_t bytes_transferred = 0;
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for(auto it = buffers.begin();
|
||||
it != buffers.end(); ++it)
|
||||
{
|
||||
boost::asio::const_buffer b = *it;
|
||||
auto const n = buffer_size(b);
|
||||
os_.write(buffer_cast<char const*>(b), n);
|
||||
if(os_.fail())
|
||||
return;
|
||||
bytes_transferred += n;
|
||||
}
|
||||
sr_.consume(bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<true, Fields> const& h)
|
||||
{
|
||||
typename Fields::reader fr{
|
||||
h, h.version, h.method()};
|
||||
return os << buffers(fr.get());
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<false, Fields> const& h)
|
||||
{
|
||||
typename Fields::reader fr{
|
||||
h, h.version, h.result_int()};
|
||||
return os << buffers(fr.get());
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
error_code ec;
|
||||
detail::write_ostream_lambda<decltype(sr)> f{os, sr};
|
||||
do
|
||||
{
|
||||
sr.next(ec, f);
|
||||
if(os.fail())
|
||||
break;
|
||||
if(ec == error::end_of_stream)
|
||||
ec.assign(0, ec.category());
|
||||
if(ec)
|
||||
{
|
||||
os.setstate(std::ios::failbit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(! sr.is_done());
|
||||
return os;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user