mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-05 15:07:49 +00:00
Added thirdparty: boost library
This commit is contained in:
+88
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP
|
||||
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest>::
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(net::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence type requirements not met");
|
||||
auto const p = net::buffer_sequence_begin(buffers);
|
||||
auto const last = net::buffer_sequence_end(buffers);
|
||||
if(p == last)
|
||||
{
|
||||
ec = {};
|
||||
return 0;
|
||||
}
|
||||
if(std::next(p) == last)
|
||||
{
|
||||
// single buffer
|
||||
return put(net::const_buffer(*p), ec);
|
||||
}
|
||||
auto const size = buffer_bytes(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
|
||||
net::buffer_copy(net::buffer(
|
||||
buf_.get(), size), buffers);
|
||||
return put(net::const_buffer{
|
||||
buf_.get(), size}, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest>::
|
||||
content_length_unchecked() const
|
||||
{
|
||||
if(f_ & flagContentLength)
|
||||
return len0_;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest>::
|
||||
put_from_stack(std::size_t size,
|
||||
ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
char buf[max_stack_buffer];
|
||||
net::buffer_copy(net::mutable_buffer(
|
||||
buf, sizeof(buf)), buffers);
|
||||
return put(net::const_buffer{
|
||||
buf, size}, ec);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+913
@@ -0,0 +1,913 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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/http/basic_parser.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/detail/string.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest>
|
||||
bool
|
||||
basic_parser<isRequest>::
|
||||
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>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest>::
|
||||
content_length() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
return content_length_unchecked();
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest>::
|
||||
content_length_remaining() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(! (f_ & flagContentLength))
|
||||
return boost::none;
|
||||
return len_;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
skip(bool v)
|
||||
{
|
||||
BOOST_ASSERT(! got_some());
|
||||
if(v)
|
||||
f_ |= flagSkipBody;
|
||||
else
|
||||
f_ &= ~flagSkipBody;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
std::size_t
|
||||
basic_parser<isRequest>::
|
||||
put(net::const_buffer buffer,
|
||||
error_code& ec)
|
||||
{
|
||||
// If this goes off you have tried to parse more data after the parser
|
||||
// has completed. A common cause of this is re-using a parser, which is
|
||||
// not supported. If you need to re-use a parser, consider storing it
|
||||
// in an optional. Then reset() and emplace() prior to parsing each new
|
||||
// message.
|
||||
BOOST_ASSERT(!is_done());
|
||||
if (is_done())
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::stale_parser);
|
||||
return 0;
|
||||
}
|
||||
auto p = static_cast<char const*>(buffer.data());
|
||||
auto n = buffer.size();
|
||||
auto const p0 = p;
|
||||
auto const p1 = p0 + n;
|
||||
ec = {};
|
||||
loop:
|
||||
switch(state_)
|
||||
{
|
||||
case state::nothing_yet:
|
||||
if(n == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return 0;
|
||||
}
|
||||
state_ = state::start_line;
|
||||
BOOST_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_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
goto done;
|
||||
}
|
||||
BOOST_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_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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{});
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::body0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
this->on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body;
|
||||
BOOST_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_);
|
||||
this->on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body_to_eof;
|
||||
BOOST_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:
|
||||
this->on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::chunk_header;
|
||||
BOOST_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 = {};
|
||||
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>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
put_eof(error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(got_some());
|
||||
if( state_ == state::start_line ||
|
||||
state_ == state::fields)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
|
||||
return;
|
||||
}
|
||||
if(f_ & (flagContentLength | flagChunked))
|
||||
{
|
||||
if(state_ != state::complete)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
|
||||
return;
|
||||
}
|
||||
ec = {};
|
||||
return;
|
||||
}
|
||||
ec = {};
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
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)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
auto const term =
|
||||
find_eom(p + skip_, p + n);
|
||||
if(! term)
|
||||
{
|
||||
skip_ = n - 3;
|
||||
if(skip_ + 4 > header_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
|
||||
return;
|
||||
}
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
skip_ = 0;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
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)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
|
||||
if(p + 2 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(p[0] != '\r' || p[1] != '\n')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
this->on_request_impl(string_to_verb(method),
|
||||
method, target, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
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)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
|
||||
// SP
|
||||
if(p + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(*p++ != ' ')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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;
|
||||
|
||||
this->on_response_impl(
|
||||
status_, reason, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
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
|
||||
beast::detail::char_buffer<max_obs_fold> buf;
|
||||
auto p = in;
|
||||
for(;;)
|
||||
{
|
||||
if(p + 2 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(p[0] == '\r')
|
||||
{
|
||||
if(p[1] != '\n')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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;
|
||||
this->on_field_impl(f, name, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
in = p;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
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(body_limit_.has_value() &&
|
||||
len_ > body_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
len0_ = 0;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
ec = {};
|
||||
this->on_header_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
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
|
||||
{
|
||||
// VFALCO Content-Length may be present, but we
|
||||
// treat the message as not having a body.
|
||||
// https://github.com/boostorg/beast/issues/692
|
||||
state_ = state::complete;
|
||||
}
|
||||
else if(f_ & flagContentLength)
|
||||
{
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
|
||||
if(body_limit_.has_value() &&
|
||||
len_ > body_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
f_ |= flagNeedEOF;
|
||||
state_ = state::body_to_eof0;
|
||||
}
|
||||
|
||||
ec = {};
|
||||
this->on_header_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
n = this->on_body_impl(string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(ec)
|
||||
return;
|
||||
if(len_ > 0)
|
||||
return;
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
if(body_limit_.has_value())
|
||||
{
|
||||
if (n > *body_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
|
||||
return;
|
||||
}
|
||||
*body_limit_ -= n;
|
||||
}
|
||||
ec = {};
|
||||
n = this->on_body_impl(string_view{p, n}, ec);
|
||||
p += n;
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
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)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
eol = find_eol(p0 + skip_, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! eol)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
|
||||
return;
|
||||
}
|
||||
if(size != 0)
|
||||
{
|
||||
if (body_limit_.has_value())
|
||||
{
|
||||
if (size > *body_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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 )
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return;
|
||||
}
|
||||
auto const ext = make_string(start, p);
|
||||
this->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;
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
|
||||
auto const start = p;
|
||||
parse_chunk_extensions(p, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(p != eol - 2)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return;
|
||||
}
|
||||
auto const ext = make_string(start, p);
|
||||
this->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;
|
||||
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
n = this->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>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
do_field(field f,
|
||||
string_view value, error_code& ec)
|
||||
{
|
||||
using namespace beast::detail::string_literals;
|
||||
// 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?
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
|
||||
return;
|
||||
}
|
||||
for(auto const& s : list)
|
||||
{
|
||||
if(beast::iequals("close"_sv, s))
|
||||
{
|
||||
f_ |= flagConnectionClose;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(beast::iequals("keep-alive"_sv, s))
|
||||
{
|
||||
f_ |= flagConnectionKeepAlive;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(beast::iequals("upgrade"_sv, s))
|
||||
{
|
||||
f_ |= flagConnectionUpgrade;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ec = {};
|
||||
return;
|
||||
}
|
||||
|
||||
// Content-Length
|
||||
if(f == field::content_length)
|
||||
{
|
||||
auto bad_content_length = [&ec]
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_content_length);
|
||||
};
|
||||
|
||||
auto multiple_content_length = [&ec]
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::multiple_content_length);
|
||||
};
|
||||
|
||||
// conflicting field
|
||||
if(f_ & flagChunked)
|
||||
return bad_content_length();
|
||||
|
||||
// Content-length may be a comma-separated list of integers
|
||||
auto tokens_unprocessed = 1 +
|
||||
std::count(value.begin(), value.end(), ',');
|
||||
auto tokens = opt_token_list(value);
|
||||
if (tokens.begin() == tokens.end() ||
|
||||
!validate_list(tokens))
|
||||
return bad_content_length();
|
||||
|
||||
auto existing = this->content_length_unchecked();
|
||||
for (auto tok : tokens)
|
||||
{
|
||||
std::uint64_t v;
|
||||
if (!parse_dec(tok, v))
|
||||
return bad_content_length();
|
||||
--tokens_unprocessed;
|
||||
if (existing.has_value())
|
||||
{
|
||||
if (v != *existing)
|
||||
return multiple_content_length();
|
||||
}
|
||||
else
|
||||
{
|
||||
existing = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens_unprocessed)
|
||||
return bad_content_length();
|
||||
|
||||
BOOST_ASSERT(existing.has_value());
|
||||
ec = {};
|
||||
len_ = *existing;
|
||||
len0_ = *existing;
|
||||
f_ |= flagContentLength;
|
||||
return;
|
||||
}
|
||||
|
||||
// Transfer-Encoding
|
||||
if(f == field::transfer_encoding)
|
||||
{
|
||||
if(f_ & flagChunked)
|
||||
{
|
||||
// duplicate
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
// conflicting field
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
|
||||
return;
|
||||
}
|
||||
|
||||
ec = {};
|
||||
auto const v = token_list{value};
|
||||
auto const p = std::find_if(v.begin(), v.end(),
|
||||
[&](string_view const& s)
|
||||
{
|
||||
return beast::iequals("chunked"_sv, s);
|
||||
});
|
||||
if(p == v.end())
|
||||
return;
|
||||
if(std::next(p) != v.end())
|
||||
return;
|
||||
len_ = 0;
|
||||
f_ |= flagChunked;
|
||||
return;
|
||||
}
|
||||
|
||||
// Upgrade
|
||||
if(f == field::upgrade)
|
||||
{
|
||||
ec = {};
|
||||
f_ |= flagUpgrade;
|
||||
return;
|
||||
}
|
||||
|
||||
ec = {};
|
||||
}
|
||||
|
||||
#ifdef BOOST_BEAST_SOURCE
|
||||
template class http::basic_parser<true>;
|
||||
template class http::basic_parser<false>;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+706
@@ -0,0 +1,706 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#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,
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{})
|
||||
{
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
inline
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
string_view extensions)
|
||||
: view_(
|
||||
size,
|
||||
net::const_buffer{
|
||||
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_(
|
||||
buffer_bytes(buffers),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
string_view extensions)
|
||||
: view_(
|
||||
buffer_bytes(buffers),
|
||||
net::const_buffer{
|
||||
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_(
|
||||
buffer_bytes(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_(
|
||||
buffer_bytes(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::writer>(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::writer>(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 = {};
|
||||
return it;
|
||||
}
|
||||
// BWS
|
||||
if(*it == ' ' || *it == '\t')
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ';'
|
||||
if(*it != ';')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
semi:
|
||||
++it; // skip ';'
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-name
|
||||
{
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
}
|
||||
if(*it == ';')
|
||||
{
|
||||
emit_empty_string();
|
||||
goto semi;
|
||||
}
|
||||
if(*it != '=')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
++it; // skip '='
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(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>
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{range_.data()};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{
|
||||
range_.data() + range_.size()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_ERROR_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<::boost::beast::http::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
error_code
|
||||
make_error_code(error ev);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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 <boost/beast/http/error.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
http_error_category() : error_category(0x964627da815bf210u) {}
|
||||
|
||||
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";
|
||||
case error::multiple_content_length: return "multiple Content-Length";
|
||||
case error::stale_parser: return "stale parser";
|
||||
case error::short_read: return "unexpected eof in body";
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
error_code
|
||||
make_error_code(error ev)
|
||||
{
|
||||
static detail::http_error_category const cat{};
|
||||
return error_code{static_cast<
|
||||
std::underlying_type<error>::type>(ev), cat};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+586
@@ -0,0 +1,586 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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/http/field.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct field_table
|
||||
{
|
||||
static
|
||||
std::uint32_t
|
||||
get_chars(
|
||||
unsigned char const* p) noexcept
|
||||
{
|
||||
// VFALCO memcpy is endian-dependent
|
||||
//std::memcpy(&v, p, 4);
|
||||
// Compiler should be smart enough to
|
||||
// optimize this down to one instruction.
|
||||
return
|
||||
p[0] |
|
||||
(p[1] << 8) |
|
||||
(p[2] << 16) |
|
||||
(p[3] << 24);
|
||||
}
|
||||
|
||||
using array_type =
|
||||
std::array<string_view, 357>;
|
||||
|
||||
// Strings are converted to lowercase
|
||||
static
|
||||
std::uint32_t
|
||||
digest(string_view s)
|
||||
{
|
||||
std::uint32_t r = 0;
|
||||
std::size_t n = s.size();
|
||||
auto p = reinterpret_cast<
|
||||
unsigned char const*>(s.data());
|
||||
// consume N characters at a time
|
||||
// VFALCO Can we do 8 on 64-bit systems?
|
||||
while(n >= 4)
|
||||
{
|
||||
auto const v = get_chars(p);
|
||||
r = (r * 5 + (
|
||||
v | 0x20202020 )); // convert to lower
|
||||
p += 4;
|
||||
n -= 4;
|
||||
}
|
||||
// handle remaining characters
|
||||
while( n > 0 )
|
||||
{
|
||||
r = r * 5 + ( *p | 0x20 );
|
||||
++p;
|
||||
--n;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// This comparison is case-insensitive, and the
|
||||
// strings must contain only valid http field characters.
|
||||
static
|
||||
bool
|
||||
equals(string_view lhs, string_view rhs)
|
||||
{
|
||||
using Int = std::uint32_t; // VFALCO std::size_t?
|
||||
auto n = lhs.size();
|
||||
if(n != rhs.size())
|
||||
return false;
|
||||
auto p1 = reinterpret_cast<
|
||||
unsigned char const*>(lhs.data());
|
||||
auto p2 = reinterpret_cast<
|
||||
unsigned char const*>(rhs.data());
|
||||
auto constexpr S = sizeof(Int);
|
||||
auto constexpr Mask = static_cast<Int>(
|
||||
0xDFDFDFDFDFDFDFDF & ~Int{0});
|
||||
for(; n >= S; p1 += S, p2 += S, n -= S)
|
||||
{
|
||||
Int const v1 = get_chars(p1);
|
||||
Int const v2 = get_chars(p2);
|
||||
if((v1 ^ v2) & Mask)
|
||||
return false;
|
||||
}
|
||||
for(; n; ++p1, ++p2, --n)
|
||||
if(( *p1 ^ *p2) & 0xDF)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
array_type by_name_;
|
||||
|
||||
enum { N = 5155 };
|
||||
unsigned char map_[ N ][ 2 ] = {};
|
||||
|
||||
/*
|
||||
From:
|
||||
|
||||
https://www.iana.org/assignments/message-headers/message-headers.xhtml
|
||||
*/
|
||||
field_table()
|
||||
: by_name_({{
|
||||
// string constants
|
||||
"<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-Expose-Headers",
|
||||
"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-Fetch-Dest",
|
||||
"Sec-Fetch-Mode",
|
||||
"Sec-Fetch-Site",
|
||||
"Sec-Fetch-User",
|
||||
"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"
|
||||
}})
|
||||
{
|
||||
for(std::size_t i = 1, n = 256; i < n; ++i)
|
||||
{
|
||||
auto sv = by_name_[ i ];
|
||||
auto h = digest(sv);
|
||||
auto j = h % N;
|
||||
BOOST_ASSERT(map_[j][0] == 0);
|
||||
map_[j][0] = static_cast<unsigned char>(i);
|
||||
}
|
||||
|
||||
for(std::size_t i = 256, n = by_name_.size(); i < n; ++i)
|
||||
{
|
||||
auto sv = by_name_[i];
|
||||
auto h = digest(sv);
|
||||
auto j = h % N;
|
||||
BOOST_ASSERT(map_[j][1] == 0);
|
||||
map_[j][1] = static_cast<unsigned char>(i - 255);
|
||||
}
|
||||
}
|
||||
|
||||
field
|
||||
string_to_field(string_view s) const
|
||||
{
|
||||
auto h = digest(s);
|
||||
auto j = h % N;
|
||||
int i = map_[j][0];
|
||||
string_view s2 = by_name_[i];
|
||||
if(i != 0 && equals(s, s2))
|
||||
return static_cast<field>(i);
|
||||
i = map_[j][1];
|
||||
if(i == 0)
|
||||
return field::unknown;
|
||||
i += 255;
|
||||
s2 = by_name_[i];
|
||||
|
||||
if(equals(s, s2))
|
||||
return static_cast<field>(i);
|
||||
return field::unknown;
|
||||
}
|
||||
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
field_table const&
|
||||
get_field_table()
|
||||
{
|
||||
static field_table const tab;
|
||||
return tab;
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
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
|
||||
|
||||
string_view
|
||||
to_string(field f)
|
||||
{
|
||||
return detail::to_string(f);
|
||||
}
|
||||
|
||||
field
|
||||
string_to_field(string_view s)
|
||||
{
|
||||
return detail::get_field_table().string_to_field(s);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, field f)
|
||||
{
|
||||
return os << to_string(f);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+1215
File diff suppressed because it is too large
Load Diff
+138
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_FIELDS_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
|
||||
|
||||
#include <boost/beast/http/fields.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
// `basic_fields` assumes that `std::size_t` is larger than `uint16_t`, so we
|
||||
// verify it explicitly here, so that users that use split compilation don't
|
||||
// need to pay the (fairly small) price for this sanity check
|
||||
BOOST_STATIC_ASSERT((std::numeric_limits<std::size_t>::max)() >=
|
||||
(std::numeric_limits<std::uint32_t>::max)());
|
||||
|
||||
// Filter a token list
|
||||
//
|
||||
inline
|
||||
void
|
||||
filter_token_list(
|
||||
beast::detail::temporary_buffer& s,
|
||||
string_view value,
|
||||
iequals_predicate const& pred)
|
||||
{
|
||||
token_list te{value};
|
||||
auto it = te.begin();
|
||||
auto last = te.end();
|
||||
if(it == last)
|
||||
return;
|
||||
while(pred(*it))
|
||||
if(++it == last)
|
||||
return;
|
||||
s.append(*it);
|
||||
while(++it != last)
|
||||
{
|
||||
if(! pred(*it))
|
||||
{
|
||||
s.append(", ", *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
filter_token_list_last(
|
||||
beast::detail::temporary_buffer& s,
|
||||
string_view value,
|
||||
iequals_predicate const& pred)
|
||||
{
|
||||
token_list te{value};
|
||||
if(te.begin() != te.end())
|
||||
{
|
||||
auto it = te.begin();
|
||||
auto next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! pred(*it))
|
||||
s.append(*it);
|
||||
return;
|
||||
}
|
||||
s.append(*it);
|
||||
for(;;)
|
||||
{
|
||||
it = next;
|
||||
next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! pred(*it))
|
||||
{
|
||||
s.append(", ", *it);
|
||||
}
|
||||
return;
|
||||
}
|
||||
s.append(", ", *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
keep_alive_impl(
|
||||
beast::detail::temporary_buffer& s, string_view value,
|
||||
unsigned version, bool keep_alive)
|
||||
{
|
||||
if(version < 11)
|
||||
{
|
||||
if(keep_alive)
|
||||
{
|
||||
// remove close
|
||||
filter_token_list(s, value, iequals_predicate{"close", {}});
|
||||
// add keep-alive
|
||||
if(s.empty())
|
||||
s.append("keep-alive");
|
||||
else if(! token_list{value}.exists("keep-alive"))
|
||||
s.append(", keep-alive");
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove close and keep-alive
|
||||
filter_token_list(s, value,
|
||||
iequals_predicate{"close", "keep-alive"});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(keep_alive)
|
||||
{
|
||||
// remove close and keep-alive
|
||||
filter_token_list(s, value,
|
||||
iequals_predicate{"close", "keep-alive"});
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove keep-alive
|
||||
filter_token_list(s, value, iequals_predicate{"keep-alive", {}});
|
||||
// add close
|
||||
if(s.empty())
|
||||
s.append("close");
|
||||
else if(! token_list{value}.exists("close"))
|
||||
s.append(", close");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
|
||||
+647
@@ -0,0 +1,647 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP
|
||||
|
||||
#if BOOST_BEAST_USE_WIN32_FILE
|
||||
|
||||
#include <boost/beast/core/async_base.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/buffers_range.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/core/detail/is_invocable.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/basic_stream_socket.hpp>
|
||||
#include <boost/asio/windows/overlapped_ptr.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
#include <boost/smart_ptr/make_shared_array.hpp>
|
||||
#include <boost/winapi/basic_types.hpp>
|
||||
#include <boost/winapi/error_codes.hpp>
|
||||
#include <boost/winapi/get_last_error.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
template<class, class, bool, class, class>
|
||||
class write_some_win32_op;
|
||||
} // detail
|
||||
|
||||
template<>
|
||||
struct basic_file_body<file_win32>
|
||||
{
|
||||
using file_type = file_win32;
|
||||
|
||||
class writer;
|
||||
class reader;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class value_type
|
||||
{
|
||||
friend class writer;
|
||||
friend class reader;
|
||||
friend struct basic_file_body<file_win32>;
|
||||
|
||||
template<class, class, bool, class, class>
|
||||
friend class detail::write_some_win32_op;
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields>
|
||||
friend
|
||||
std::size_t
|
||||
write_some(
|
||||
net::basic_stream_socket<Protocol, Executor>& 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;
|
||||
|
||||
file_win32& file()
|
||||
{
|
||||
return file_;
|
||||
}
|
||||
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
return last_ - first_;
|
||||
}
|
||||
|
||||
void
|
||||
close();
|
||||
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
void
|
||||
reset(file_win32&& file, error_code& ec);
|
||||
|
||||
void
|
||||
seek(std::uint64_t offset, error_code& ec);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class writer
|
||||
{
|
||||
template<class, class, bool, class, class>
|
||||
friend class detail::write_some_win32_op;
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields>
|
||||
friend
|
||||
std::size_t
|
||||
write_some(
|
||||
net::basic_stream_socket<Protocol, Executor>& 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_[BOOST_BEAST_FILE_BUFFER_SIZE]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
net::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
writer(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
, pos_(body_.first_)
|
||||
{
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
ec.clear();
|
||||
}
|
||||
|
||||
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 = {};
|
||||
return boost::none;
|
||||
}
|
||||
auto const nread = body_.file_.read(buf_, n, ec);
|
||||
if(ec)
|
||||
return boost::none;
|
||||
if (nread == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::short_read);
|
||||
return boost::none;
|
||||
}
|
||||
BOOST_ASSERT(nread != 0);
|
||||
pos_ += nread;
|
||||
ec = {};
|
||||
return {{
|
||||
{buf_, nread}, // buffer to return.
|
||||
pos_ < body_.last_}}; // `true` if there are more buffers.
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
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 = {};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
std::size_t nwritten = 0;
|
||||
for(auto buffer : beast::buffers_range_ref(buffers))
|
||||
{
|
||||
nwritten += body_.file_.write(
|
||||
buffer.data(), buffer.size(), ec);
|
||||
if(ec)
|
||||
return nwritten;
|
||||
}
|
||||
ec = {};
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
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_ = file_.pos(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
last_ = size_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
seek(std::uint64_t offset, error_code& ec)
|
||||
{
|
||||
first_ = offset;
|
||||
file_.seek(offset, ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Unsigned>
|
||||
boost::winapi::DWORD_
|
||||
lowPart(Unsigned n)
|
||||
{
|
||||
return static_cast<
|
||||
boost::winapi::DWORD_>(
|
||||
n & 0xffffffff);
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
boost::winapi::DWORD_
|
||||
highPart(Unsigned n, std::true_type)
|
||||
{
|
||||
return static_cast<
|
||||
boost::winapi::DWORD_>(
|
||||
(n>>32)&0xffffffff);
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
boost::winapi::DWORD_
|
||||
highPart(Unsigned, std::false_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
boost::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);
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/boostorg/beast/issues/1815
|
||||
// developer commentary:
|
||||
// This function mimics the behaviour of ASIO.
|
||||
// Perhaps the correct fix is to insist on the use
|
||||
// of an appropriate error_condition to detect
|
||||
// connection_reset and connection_refused?
|
||||
inline
|
||||
error_code
|
||||
make_win32_error(
|
||||
boost::winapi::DWORD_ dwError) noexcept
|
||||
{
|
||||
// from
|
||||
// https://github.com/boostorg/asio/blob/6534af41b471288091ae39f9ab801594189b6fc9/include/boost/asio/detail/impl/socket_ops.ipp#L842
|
||||
switch(dwError)
|
||||
{
|
||||
case boost::winapi::ERROR_NETNAME_DELETED_:
|
||||
return net::error::connection_reset;
|
||||
case boost::winapi::ERROR_PORT_UNREACHABLE_:
|
||||
return net::error::connection_refused;
|
||||
case boost::winapi::WSAEMSGSIZE_:
|
||||
case boost::winapi::ERROR_MORE_DATA_:
|
||||
return {};
|
||||
}
|
||||
return error_code(
|
||||
static_cast<int>(dwError),
|
||||
system_category());
|
||||
}
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_win32_error(
|
||||
error_code ec) noexcept
|
||||
{
|
||||
if(ec.category() !=
|
||||
system_category())
|
||||
return ec;
|
||||
return make_win32_error(
|
||||
static_cast<boost::winapi::DWORD_>(
|
||||
ec.value()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields,
|
||||
class Handler>
|
||||
class write_some_win32_op
|
||||
: public beast::async_base<Handler, Executor>
|
||||
{
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>& sock_;
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr_;
|
||||
bool header_ = false;
|
||||
|
||||
public:
|
||||
template<class Handler_>
|
||||
write_some_win32_op(
|
||||
Handler_&& h,
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>& s,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>,Fields>& sr)
|
||||
: async_base<
|
||||
Handler, Executor>(
|
||||
std::forward<Handler_>(h),
|
||||
s.get_executor())
|
||||
, sock_(s)
|
||||
, sr_(sr)
|
||||
{
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
if(! sr_.is_header_done())
|
||||
{
|
||||
header_ = true;
|
||||
sr_.split(true);
|
||||
return detail::async_write_some_impl(
|
||||
sock_, sr_, std::move(*this));
|
||||
}
|
||||
if(sr_.get().chunked())
|
||||
{
|
||||
return detail::async_write_some_impl(
|
||||
sock_, sr_, std::move(*this));
|
||||
}
|
||||
auto& w = sr_.writer_impl();
|
||||
boost::winapi::DWORD_ const nNumberOfBytesToWrite =
|
||||
static_cast<boost::winapi::DWORD_>(
|
||||
(std::min<std::uint64_t>)(
|
||||
(std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr_.limit()),
|
||||
(std::numeric_limits<boost::winapi::INT_>::max)() - 1));
|
||||
net::windows::overlapped_ptr overlapped{
|
||||
sock_.get_executor(), std::move(*this)};
|
||||
// Note that we have moved *this, so we cannot access
|
||||
// the handler since it is now moved-from. We can still
|
||||
// access simple things like references and built-in types.
|
||||
auto& ov = *overlapped.get();
|
||||
ov.Offset = lowPart(w.pos_);
|
||||
ov.OffsetHigh = highPart(w.pos_);
|
||||
auto const bSuccess = ::TransmitFile(
|
||||
sock_.native_handle(),
|
||||
sr_.get().body().file_.native_handle(),
|
||||
nNumberOfBytesToWrite,
|
||||
0,
|
||||
overlapped.get(),
|
||||
nullptr,
|
||||
0);
|
||||
auto const dwError = boost::winapi::GetLastError();
|
||||
if(! bSuccess && dwError !=
|
||||
boost::winapi::ERROR_IO_PENDING_)
|
||||
{
|
||||
// VFALCO This needs review, is 0 the right number?
|
||||
// completed immediately (with error?)
|
||||
overlapped.complete(
|
||||
make_win32_error(dwError), 0);
|
||||
return;
|
||||
}
|
||||
overlapped.release();
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec,
|
||||
std::size_t bytes_transferred = 0)
|
||||
{
|
||||
if(ec)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, make_win32_error(ec));
|
||||
}
|
||||
else if(! ec && ! header_)
|
||||
{
|
||||
auto& w = sr_.writer_impl();
|
||||
w.pos_ += bytes_transferred;
|
||||
BOOST_ASSERT(w.pos_ <= w.body_.last_);
|
||||
if(w.pos_ >= w.body_.last_)
|
||||
{
|
||||
sr_.next(ec, null_lambda{});
|
||||
BOOST_ASSERT(! ec);
|
||||
BOOST_ASSERT(sr_.is_done());
|
||||
}
|
||||
}
|
||||
this->complete_now(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_write_some_win32_op
|
||||
{
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields,
|
||||
class WriteHandler>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>* s,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>* sr)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_some_win32_op<
|
||||
Protocol, Executor,
|
||||
isRequest, Fields,
|
||||
typename std::decay<WriteHandler>::type>(
|
||||
std::forward<WriteHandler>(h), *s, *sr);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields>
|
||||
std::size_t
|
||||
write_some(
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
if(! sr.is_header_done())
|
||||
{
|
||||
sr.split(true);
|
||||
auto const bytes_transferred =
|
||||
detail::write_some_impl(sock, sr, ec);
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
return bytes_transferred;
|
||||
}
|
||||
if(sr.get().chunked())
|
||||
{
|
||||
auto const bytes_transferred =
|
||||
detail::write_some_impl(sock, sr, ec);
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
return bytes_transferred;
|
||||
}
|
||||
auto& w = sr.writer_impl();
|
||||
w.body_.file_.seek(w.pos_, ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
boost::winapi::DWORD_ const nNumberOfBytesToWrite =
|
||||
static_cast<boost::winapi::DWORD_>(
|
||||
(std::min<std::uint64_t>)(
|
||||
(std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()),
|
||||
(std::numeric_limits<boost::winapi::INT_>::max)() - 1));
|
||||
auto const bSuccess = ::TransmitFile(
|
||||
sock.native_handle(),
|
||||
w.body_.file_.native_handle(),
|
||||
nNumberOfBytesToWrite,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0);
|
||||
if(! bSuccess)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, detail::make_win32_error(
|
||||
boost::winapi::GetLastError()));
|
||||
return 0;
|
||||
}
|
||||
w.pos_ += nNumberOfBytesToWrite;
|
||||
BOOST_ASSERT(w.pos_ <= w.body_.last_);
|
||||
if(w.pos_ < w.body_.last_)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
sr.next(ec, detail::null_lambda{});
|
||||
BOOST_ASSERT(! ec);
|
||||
BOOST_ASSERT(sr.is_done());
|
||||
}
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_some(
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_some_win32_op{},
|
||||
handler,
|
||||
&sock,
|
||||
&sr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_MESSAGE_HPP
|
||||
|
||||
#include <boost/beast/core/error.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>
|
||||
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>
|
||||
string_view
|
||||
header<true, Fields>::
|
||||
target() const
|
||||
{
|
||||
return this->get_target_impl();
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
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)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
status
|
||||
header<false, Fields>::
|
||||
result() const
|
||||
{
|
||||
return int_to_status(
|
||||
static_cast<int>(result_));
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<false, Fields>::
|
||||
result(status v)
|
||||
{
|
||||
result_ = v;
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
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>
|
||||
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>
|
||||
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))
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
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)
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
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)
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
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))
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
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)
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
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))
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
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,
|
||||
mp11::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,
|
||||
mp11::make_index_sequence<
|
||||
sizeof...(BodyArgs)>{},
|
||||
mp11::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>
|
||||
bool
|
||||
message<isRequest, Body, Fields>::
|
||||
need_eof(std::false_type) const
|
||||
{
|
||||
// VFALCO Do we need a way to let the caller say "the body is intentionally skipped"?
|
||||
if( this->result() == status::no_content ||
|
||||
this->result() == status::not_modified ||
|
||||
to_status_class(this->result()) ==
|
||||
status_class::informational ||
|
||||
has_content_length() ||
|
||||
chunked())
|
||||
return ! keep_alive();
|
||||
return true;
|
||||
}
|
||||
|
||||
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( (! n || *n > 0) && (
|
||||
(status_class(this->result()) == status_class::informational ||
|
||||
this->result() == status::no_content ||
|
||||
this->result() == status::not_modified)))
|
||||
{
|
||||
// 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 if(this->version() == 11)
|
||||
this->chunked(true);
|
||||
else
|
||||
this->chunked(false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright (c) 2022 Seth Heeren (sgheeren 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_GENERATOR_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_MESSAGE_GENERATOR_HPP
|
||||
|
||||
#include <boost/beast/http/message_generator.hpp>
|
||||
#include <boost/smart_ptr/make_unique.hpp>
|
||||
#include <boost/beast/core/buffers_generator.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
message_generator::message_generator(
|
||||
http::message<isRequest, Body, Fields>&& m)
|
||||
: impl_(boost::make_unique<
|
||||
generator_impl<isRequest, Body, Fields>>(
|
||||
std::move(m)))
|
||||
{
|
||||
}
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
struct message_generator::generator_impl
|
||||
: message_generator::impl_base
|
||||
{
|
||||
explicit generator_impl(
|
||||
http::message<isRequest, Body, Fields>&& m)
|
||||
: m_(std::move(m))
|
||||
, sr_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
is_done() override
|
||||
{
|
||||
return sr_.is_done();
|
||||
}
|
||||
|
||||
const_buffers_type
|
||||
prepare(error_code& ec) override
|
||||
{
|
||||
sr_.next(ec, visit{*this});
|
||||
return current_;
|
||||
}
|
||||
|
||||
void
|
||||
consume(std::size_t n) override
|
||||
{
|
||||
sr_.consume((std::min)(n, beast::buffer_bytes(current_)));
|
||||
}
|
||||
|
||||
bool
|
||||
keep_alive() const noexcept override
|
||||
{
|
||||
return m_.keep_alive();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr unsigned max_fixed_bufs = 12;
|
||||
|
||||
http::message<isRequest, Body, Fields> m_;
|
||||
http::serializer<isRequest, Body, Fields> sr_;
|
||||
|
||||
std::array<net::const_buffer, max_fixed_bufs> bs_;
|
||||
const_buffers_type current_ = bs_; // subspan
|
||||
|
||||
struct visit
|
||||
{
|
||||
generator_impl& self_;
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code&, ConstBufferSequence const& buffers)
|
||||
{
|
||||
auto& s = self_.bs_;
|
||||
auto& cur = self_.current_;
|
||||
|
||||
auto it = net::buffer_sequence_begin(buffers);
|
||||
|
||||
std::size_t n =
|
||||
std::distance(it, net::buffer_sequence_end(buffers));
|
||||
|
||||
n = (std::min)(s.size(), n);
|
||||
|
||||
cur = { s.data(), n };
|
||||
std::copy_n(it, n, cur.begin());
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace beast
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_PARSER_HPP
|
||||
|
||||
#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()
|
||||
: rd_(m_.base(), m_.body())
|
||||
{
|
||||
}
|
||||
|
||||
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)...)
|
||||
, rd_(m_.base(), m_.body())
|
||||
{
|
||||
m_.clear();
|
||||
}
|
||||
|
||||
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)
|
||||
: basic_parser<isRequest>(std::move(other))
|
||||
, m_(other.release(), std::forward<Args>(args)...)
|
||||
, rd_(m_.base(), m_.body())
|
||||
{
|
||||
if(other.rd_inited_)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"moved-from parser has a body"});
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+722
@@ -0,0 +1,722 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
// Copyright (c) 2020 Richard Hodges (hodges.r@gmail.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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_READ_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/async_base.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <boost/beast/core/detail/buffer.hpp>
|
||||
#include <boost/beast/core/detail/read.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct parser_is_done
|
||||
{
|
||||
template<bool isRequest>
|
||||
bool
|
||||
operator()(basic_parser<isRequest> const& p) const
|
||||
{
|
||||
return p.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct parser_is_header_done
|
||||
{
|
||||
template<bool isRequest>
|
||||
bool
|
||||
operator()(basic_parser<isRequest> const& p) const
|
||||
{
|
||||
return p.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
class Handler>
|
||||
class read_msg_op
|
||||
: public beast::stable_async_base<
|
||||
Handler, beast::executor_type<Stream>>
|
||||
, public asio::coroutine
|
||||
{
|
||||
using parser_type =
|
||||
parser<isRequest, Body, Allocator>;
|
||||
|
||||
using message_type =
|
||||
typename parser_type::value_type;
|
||||
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
|
||||
data(
|
||||
Stream& s_,
|
||||
message_type& m_)
|
||||
: s(s_)
|
||||
, m(m_)
|
||||
, p(std::move(m))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
data& d_;
|
||||
|
||||
public:
|
||||
template<class Handler_>
|
||||
read_msg_op(
|
||||
Handler_&& h,
|
||||
Stream& s,
|
||||
DynamicBuffer& b,
|
||||
message_type& m)
|
||||
: stable_async_base<
|
||||
Handler, beast::executor_type<Stream>>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, d_(beast::allocate_stable<data>(
|
||||
*this, s, m))
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read(msg)"));
|
||||
|
||||
http::async_read(d_.s, b, d_.p, std::move(*this));
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
d_.m = d_.p.release();
|
||||
this->complete_now(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_read_msg_op
|
||||
{
|
||||
template<
|
||||
class ReadHandler,
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
void
|
||||
operator()(
|
||||
ReadHandler&& h,
|
||||
AsyncReadStream* s,
|
||||
DynamicBuffer* b,
|
||||
message<isRequest, Body,
|
||||
basic_fields<Allocator>>* m)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<ReadHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"ReadHandler type requirements not met");
|
||||
|
||||
read_msg_op<
|
||||
AsyncReadStream,
|
||||
DynamicBuffer,
|
||||
isRequest, Body, Allocator,
|
||||
typename std::decay<ReadHandler>::type>(
|
||||
std::forward<ReadHandler>(h), *s, *b, *m);
|
||||
}
|
||||
};
|
||||
|
||||
template<class AsyncReadStream, class DynamicBuffer, bool isRequest>
|
||||
class read_some_op : asio::coroutine
|
||||
{
|
||||
AsyncReadStream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest>& p_;
|
||||
std::size_t bytes_transferred_;
|
||||
bool cont_;
|
||||
|
||||
public:
|
||||
read_some_op(
|
||||
AsyncReadStream& s,
|
||||
DynamicBuffer& b,
|
||||
basic_parser<isRequest>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, bytes_transferred_(0)
|
||||
, cont_(false)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Self>
|
||||
void operator()(
|
||||
Self& self,
|
||||
error_code ec = {},
|
||||
std::size_t bytes_transferred = 0)
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
if(b_.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
// parse
|
||||
{
|
||||
auto const used = p_.put(b_.data(), ec);
|
||||
bytes_transferred_ += used;
|
||||
b_.consume(used);
|
||||
}
|
||||
if(ec != http::error::need_more)
|
||||
break;
|
||||
|
||||
do_read:
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
cont_ = true;
|
||||
// VFALCO This was read_size_or_throw
|
||||
auto const size = read_size(b_, 65536);
|
||||
if(size == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
goto upcall;
|
||||
}
|
||||
auto const mb =
|
||||
beast::detail::dynamic_buffer_prepare(
|
||||
b_, size, ec, error::buffer_overflow);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read_some"));
|
||||
|
||||
s_.async_read_some(*mb, std::move(self));
|
||||
}
|
||||
b_.commit(bytes_transferred);
|
||||
if(ec == net::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;
|
||||
}
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::end_of_stream);
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
|
||||
upcall:
|
||||
if(! cont_)
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read_some"));
|
||||
|
||||
|
||||
const auto ex =
|
||||
asio::get_associated_immediate_executor(
|
||||
self, s_.get_executor());
|
||||
|
||||
net::dispatch(
|
||||
ex,
|
||||
beast::bind_front_handler(std::move(self), ec));
|
||||
}
|
||||
}
|
||||
self.complete(ec, bytes_transferred_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer, bool isRequest, class Condition>
|
||||
class read_op
|
||||
: asio::coroutine
|
||||
{
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest>& p_;
|
||||
std::size_t bytes_transferred_;
|
||||
|
||||
public:
|
||||
read_op(Stream& s, DynamicBuffer& b, basic_parser<isRequest>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, bytes_transferred_(0)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Self>
|
||||
void operator()(Self& self, error_code ec = {}, std::size_t bytes_transferred = 0)
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
if (Condition{}(p_))
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read"));
|
||||
|
||||
const auto ex =
|
||||
asio::get_associated_immediate_executor(
|
||||
self, s_.get_executor());
|
||||
|
||||
net::dispatch(ex, std::move(self));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read"));
|
||||
|
||||
async_read_some(
|
||||
s_, b_, p_, std::move(self));
|
||||
}
|
||||
bytes_transferred_ += bytes_transferred;
|
||||
} while (!ec &&
|
||||
!Condition{}(p_));
|
||||
}
|
||||
self.complete(ec, bytes_transferred_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_some(SyncReadStream& s, DynamicBuffer& b, basic_parser<isRequest>& p, error_code& ec)
|
||||
{
|
||||
std::size_t total = 0;
|
||||
ec.clear();
|
||||
if(b.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
// parse
|
||||
{
|
||||
auto const used = p.put(b.data(), ec);
|
||||
total += used;
|
||||
b.consume(used);
|
||||
}
|
||||
if(ec != http::error::need_more)
|
||||
break;
|
||||
|
||||
do_read:
|
||||
// VFALCO This was read_size_or_throw
|
||||
auto const size = read_size(b, 65536);
|
||||
if(size == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return total;
|
||||
}
|
||||
auto const mb =
|
||||
beast::detail::dynamic_buffer_prepare(
|
||||
b, size, ec, error::buffer_overflow);
|
||||
if(ec)
|
||||
return total;
|
||||
std::size_t
|
||||
bytes_transferred =
|
||||
s.read_some(*mb, ec);
|
||||
b.commit(bytes_transferred);
|
||||
if(ec == net::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)
|
||||
return total;
|
||||
BOOST_ASSERT(p.is_done());
|
||||
return total;
|
||||
}
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::end_of_stream);
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
template<class Condition, class Stream, class DynamicBuffer, bool isRequest>
|
||||
std::size_t sync_read_op(Stream& s, DynamicBuffer& b, basic_parser<isRequest>& p, error_code& ec)
|
||||
{
|
||||
std::size_t total = 0;
|
||||
ec.clear();
|
||||
|
||||
if (!Condition{}(p))
|
||||
{
|
||||
do
|
||||
{
|
||||
total +=
|
||||
detail::read_some(s, b, p, ec);
|
||||
} while (!ec &&
|
||||
!Condition{}(p));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
http::read_some(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
return detail::read_some(stream, buffer, parser, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
return net::async_compose<ReadHandler,
|
||||
void(beast::error_code, std::size_t)>(
|
||||
detail::read_some_op<AsyncReadStream, DynamicBuffer, isRequest> {
|
||||
stream,
|
||||
buffer,
|
||||
parser
|
||||
},
|
||||
handler,
|
||||
stream);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
http::read_header(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
parser.eager(false);
|
||||
return detail::sync_read_op<
|
||||
detail::parser_is_header_done>(
|
||||
stream, buffer, parser, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
parser.eager(false);
|
||||
return net::async_compose<
|
||||
ReadHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::read_op<
|
||||
AsyncReadStream,
|
||||
DynamicBuffer,
|
||||
isRequest,
|
||||
detail::parser_is_header_done>(
|
||||
stream, buffer, parser),
|
||||
handler, stream);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
http::read(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
parser.eager(true);
|
||||
return detail::sync_read_op<
|
||||
detail::parser_is_done>(
|
||||
stream, buffer, parser, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
parser.eager(true);
|
||||
return net::async_compose<
|
||||
ReadHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::read_op<
|
||||
AsyncReadStream,
|
||||
DynamicBuffer,
|
||||
isRequest,
|
||||
detail::parser_is_done>(
|
||||
stream, buffer, parser),
|
||||
handler, stream);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
http::read(stream, buffer, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader type requirements not met");
|
||||
parser<isRequest, Body, Allocator> p(std::move(msg));
|
||||
p.eager(true);
|
||||
auto const bytes_transferred =
|
||||
http::read(stream, buffer, p, ec);
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
msg = p.release();
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader type requirements not met");
|
||||
return net::async_initiate<
|
||||
ReadHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_read_msg_op{},
|
||||
handler, &stream, &buffer, &msg);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_RFC7230_HPP
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
unquote(string_view sr, std::string & s);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
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()};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
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()};
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
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 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
|
||||
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/impl/rfc7230.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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/rfc7230.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
|
||||
void param_list::const_iterator::
|
||||
unquote(string_view sr, std::string &s)
|
||||
{
|
||||
s.clear();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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() == '"')
|
||||
{
|
||||
unquote(pi_.v.second, s_);
|
||||
pi_.v.second = string_view{
|
||||
s_.data(), s_.size()};
|
||||
}
|
||||
}
|
||||
|
||||
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)};
|
||||
if (it_ == last_)
|
||||
return;
|
||||
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_;
|
||||
}
|
||||
}
|
||||
|
||||
auto
|
||||
ext_list::
|
||||
find(string_view const& s) -> const_iterator
|
||||
{
|
||||
return std::find_if(begin(), end(),
|
||||
[&s](value_type const& v)
|
||||
{
|
||||
return beast::iequals(s, v.first);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ext_list::
|
||||
exists(string_view const& s)
|
||||
{
|
||||
return find(s) != end();
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
token_list::
|
||||
exists(string_view const& s)
|
||||
{
|
||||
return std::find_if(begin(), end(),
|
||||
[&s](value_type const& v)
|
||||
{
|
||||
return beast::iequals(s, v);
|
||||
}
|
||||
) != end();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_HPP
|
||||
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#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>::
|
||||
fwrinit(std::true_type)
|
||||
{
|
||||
fwr_.emplace(m_, m_.version(), m_.method());
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
fwrinit(std::false_type)
|
||||
{
|
||||
fwr_.emplace(m_, m_.version(), m_.result_int());
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
template<std::size_t I, class Visit>
|
||||
inline
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
do_visit(error_code& ec, Visit& visit)
|
||||
{
|
||||
pv_.template emplace<I>(limit_, v_.template get<I>());
|
||||
visit(ec, beast::detail::make_buffers_ref(
|
||||
pv_.template get<I>()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
serializer<isRequest, Body, Fields>::
|
||||
serializer(value_type& m)
|
||||
: m_(m)
|
||||
, wr_(m_.base(), m_.body())
|
||||
{
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
template<class Visit>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
next(error_code& ec, Visit&& visit)
|
||||
{
|
||||
switch(s_)
|
||||
{
|
||||
case do_construct:
|
||||
{
|
||||
fwrinit(std::integral_constant<bool,
|
||||
isRequest>{});
|
||||
if(m_.chunked())
|
||||
goto go_init_c;
|
||||
s_ = do_init;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_init:
|
||||
{
|
||||
wr_.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(split_)
|
||||
goto go_header_only;
|
||||
auto result = wr_.get(ec);
|
||||
if(ec == error::need_more)
|
||||
goto go_header_only;
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_header_only;
|
||||
more_ = result->second;
|
||||
v_.template emplace<2>(
|
||||
boost::in_place_init,
|
||||
fwr_->get(),
|
||||
result->first);
|
||||
s_ = do_header;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_header:
|
||||
do_visit<2>(ec, visit);
|
||||
break;
|
||||
|
||||
go_header_only:
|
||||
v_.template emplace<1>(fwr_->get());
|
||||
s_ = do_header_only;
|
||||
BOOST_FALLTHROUGH;
|
||||
case do_header_only:
|
||||
do_visit<1>(ec, visit);
|
||||
break;
|
||||
|
||||
case do_body:
|
||||
s_ = do_body + 1;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case do_body + 1:
|
||||
{
|
||||
auto result = wr_.get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_complete;
|
||||
more_ = result->second;
|
||||
v_.template emplace<3>(result->first);
|
||||
s_ = do_body + 2;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_body + 2:
|
||||
do_visit<3>(ec, visit);
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_init_c:
|
||||
s_ = do_init_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
case do_init_c:
|
||||
{
|
||||
wr_.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(split_)
|
||||
goto go_header_only_c;
|
||||
auto result = wr_.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;
|
||||
if(! more_)
|
||||
{
|
||||
// do it all in one buffer
|
||||
v_.template emplace<7>(
|
||||
boost::in_place_init,
|
||||
fwr_->get(),
|
||||
buffer_bytes(result->first),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{},
|
||||
detail::chunk_last(),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{});
|
||||
goto go_all_c;
|
||||
}
|
||||
v_.template emplace<4>(
|
||||
boost::in_place_init,
|
||||
fwr_->get(),
|
||||
buffer_bytes(result->first),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{});
|
||||
s_ = do_header_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_header_c:
|
||||
do_visit<4>(ec, visit);
|
||||
break;
|
||||
|
||||
go_header_only_c:
|
||||
v_.template emplace<1>(fwr_->get());
|
||||
s_ = do_header_only_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case do_header_only_c:
|
||||
do_visit<1>(ec, visit);
|
||||
break;
|
||||
|
||||
case do_body_c:
|
||||
s_ = do_body_c + 1;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case do_body_c + 1:
|
||||
{
|
||||
auto result = wr_.get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_final_c;
|
||||
more_ = result->second;
|
||||
if(! more_)
|
||||
{
|
||||
// do it all in one buffer
|
||||
v_.template emplace<6>(
|
||||
boost::in_place_init,
|
||||
buffer_bytes(result->first),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{},
|
||||
detail::chunk_last(),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{});
|
||||
goto go_body_final_c;
|
||||
}
|
||||
v_.template emplace<5>(
|
||||
boost::in_place_init,
|
||||
buffer_bytes(result->first),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{});
|
||||
s_ = do_body_c + 2;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
do_visit<5>(ec, visit);
|
||||
break;
|
||||
|
||||
go_body_final_c:
|
||||
s_ = do_body_final_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
case do_body_final_c:
|
||||
do_visit<6>(ec, visit);
|
||||
break;
|
||||
|
||||
go_all_c:
|
||||
s_ = do_all_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
case do_all_c:
|
||||
do_visit<7>(ec, visit);
|
||||
break;
|
||||
|
||||
go_final_c:
|
||||
case do_final_c:
|
||||
v_.template emplace<8>(
|
||||
boost::in_place_init,
|
||||
detail::chunk_last(),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{});
|
||||
s_ = do_final_c + 1;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case do_final_c + 1:
|
||||
do_visit<8>(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)
|
||||
{
|
||||
switch(s_)
|
||||
{
|
||||
case do_header:
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<2>()));
|
||||
v_.template get<2>().consume(n);
|
||||
if(buffer_bytes(v_.template get<2>()) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_.reset();
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
|
||||
case do_header_only:
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<1>()));
|
||||
v_.template get<1>().consume(n);
|
||||
if(buffer_bytes(v_.template get<1>()) > 0)
|
||||
break;
|
||||
fwr_ = boost::none;
|
||||
header_done_ = true;
|
||||
if(! split_)
|
||||
goto go_complete;
|
||||
s_ = do_body;
|
||||
break;
|
||||
|
||||
case do_body + 2:
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<3>()));
|
||||
v_.template get<3>().consume(n);
|
||||
if(buffer_bytes(v_.template get<3>()) > 0)
|
||||
break;
|
||||
v_.reset();
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_header_c:
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<4>()));
|
||||
v_.template get<4>().consume(n);
|
||||
if(buffer_bytes(v_.template get<4>()) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_.reset();
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
case do_header_only_c:
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<1>()));
|
||||
v_.template get<1>().consume(n);
|
||||
if(buffer_bytes(v_.template get<1>()) > 0)
|
||||
break;
|
||||
fwr_ = 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_bytes(v_.template get<5>()));
|
||||
v_.template get<5>().consume(n);
|
||||
if(buffer_bytes(v_.template get<5>()) > 0)
|
||||
break;
|
||||
v_.reset();
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
case do_body_final_c:
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<6>()));
|
||||
v_.template get<6>().consume(n);
|
||||
if(buffer_bytes(v_.template get<6>()) > 0)
|
||||
break;
|
||||
v_.reset();
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_all_c:
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<7>()));
|
||||
v_.template get<7>().consume(n);
|
||||
if(buffer_bytes(v_.template get<7>()) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_.reset();
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_final_c + 1:
|
||||
BOOST_ASSERT(buffer_bytes(v_.template get<8>()));
|
||||
v_.template get<8>().consume(n);
|
||||
if(buffer_bytes(v_.template get<8>()) > 0)
|
||||
break;
|
||||
v_.reset();
|
||||
goto go_complete;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
BOOST_ASSERT(false);
|
||||
case do_complete:
|
||||
break;
|
||||
|
||||
go_complete:
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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/http/status.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
status
|
||||
int_to_status(unsigned v)
|
||||
{
|
||||
switch(static_cast<status>(v))
|
||||
{
|
||||
// 1xx
|
||||
case status::continue_:
|
||||
case status::switching_protocols:
|
||||
case status::processing:
|
||||
BOOST_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_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_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_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
status_class
|
||||
to_status_class(status v)
|
||||
{
|
||||
return to_status_class(static_cast<int>(v));
|
||||
}
|
||||
|
||||
string_view
|
||||
obsolete_reason(status 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>";
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, status v)
|
||||
{
|
||||
return os << obsolete_reason(v);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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/http/verb.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
string_view
|
||||
to_string(verb v)
|
||||
{
|
||||
using namespace beast::detail::string_literals;
|
||||
switch(v)
|
||||
{
|
||||
case verb::delete_: return "DELETE"_sv;
|
||||
case verb::get: return "GET"_sv;
|
||||
case verb::head: return "HEAD"_sv;
|
||||
case verb::post: return "POST"_sv;
|
||||
case verb::put: return "PUT"_sv;
|
||||
case verb::connect: return "CONNECT"_sv;
|
||||
case verb::options: return "OPTIONS"_sv;
|
||||
case verb::trace: return "TRACE"_sv;
|
||||
|
||||
case verb::copy: return "COPY"_sv;
|
||||
case verb::lock: return "LOCK"_sv;
|
||||
case verb::mkcol: return "MKCOL"_sv;
|
||||
case verb::move: return "MOVE"_sv;
|
||||
case verb::propfind: return "PROPFIND"_sv;
|
||||
case verb::proppatch: return "PROPPATCH"_sv;
|
||||
case verb::search: return "SEARCH"_sv;
|
||||
case verb::unlock: return "UNLOCK"_sv;
|
||||
case verb::bind: return "BIND"_sv;
|
||||
case verb::rebind: return "REBIND"_sv;
|
||||
case verb::unbind: return "UNBIND"_sv;
|
||||
case verb::acl: return "ACL"_sv;
|
||||
|
||||
case verb::report: return "REPORT"_sv;
|
||||
case verb::mkactivity: return "MKACTIVITY"_sv;
|
||||
case verb::checkout: return "CHECKOUT"_sv;
|
||||
case verb::merge: return "MERGE"_sv;
|
||||
|
||||
case verb::msearch: return "M-SEARCH"_sv;
|
||||
case verb::notify: return "NOTIFY"_sv;
|
||||
case verb::subscribe: return "SUBSCRIBE"_sv;
|
||||
case verb::unsubscribe: return "UNSUBSCRIBE"_sv;
|
||||
|
||||
case verb::patch: return "PATCH"_sv;
|
||||
case verb::purge: return "PURGE"_sv;
|
||||
|
||||
case verb::mkcalendar: return "MKCALENDAR"_sv;
|
||||
|
||||
case verb::link: return "LINK"_sv;
|
||||
case verb::unlink: return "UNLINK"_sv;
|
||||
|
||||
case verb::unknown:
|
||||
return "<unknown>"_sv;
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"});
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
using namespace beast::detail::string_literals;
|
||||
if(v.size() < 3)
|
||||
return verb::unknown;
|
||||
auto c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'A':
|
||||
if(v == "CL"_sv)
|
||||
return verb::acl;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
if(v == "IND"_sv)
|
||||
return verb::bind;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'H':
|
||||
if(v == "ECKOUT"_sv)
|
||||
return verb::checkout;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(v == "NNECT"_sv)
|
||||
return verb::connect;
|
||||
if(v == "PY"_sv)
|
||||
return verb::copy;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
if(v == "ELETE"_sv)
|
||||
return verb::delete_;
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
if(v == "ET"_sv)
|
||||
return verb::get;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
if(v == "EAD"_sv)
|
||||
return verb::head;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
if(v == "INK"_sv)
|
||||
return verb::link;
|
||||
if(v == "OCK"_sv)
|
||||
return verb::lock;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case '-':
|
||||
if(v == "SEARCH"_sv)
|
||||
return verb::msearch;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
if(v == "RGE"_sv)
|
||||
return verb::merge;
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
if(v == "ACTIVITY"_sv)
|
||||
return verb::mkactivity;
|
||||
if(v[0] == 'C')
|
||||
{
|
||||
v.remove_prefix(1);
|
||||
if(v == "ALENDAR"_sv)
|
||||
return verb::mkcalendar;
|
||||
if(v == "OL"_sv)
|
||||
return verb::mkcol;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(v == "VE"_sv)
|
||||
return verb::move;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
if(v == "OTIFY"_sv)
|
||||
return verb::notify;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(v == "PTIONS"_sv)
|
||||
return verb::options;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'A':
|
||||
if(v == "TCH"_sv)
|
||||
return verb::patch;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(v == "ST"_sv)
|
||||
return verb::post;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if(v == "OPFIND"_sv)
|
||||
return verb::propfind;
|
||||
if(v == "OPPATCH"_sv)
|
||||
return verb::proppatch;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
if(v == "RGE"_sv)
|
||||
return verb::purge;
|
||||
if(v == "T"_sv)
|
||||
return verb::put;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if(v[0] != 'E')
|
||||
break;
|
||||
v.remove_prefix(1);
|
||||
if(v == "BIND"_sv)
|
||||
return verb::rebind;
|
||||
if(v == "PORT"_sv)
|
||||
return verb::report;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if(v == "EARCH"_sv)
|
||||
return verb::search;
|
||||
if(v == "UBSCRIBE"_sv)
|
||||
return verb::subscribe;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if(v == "RACE"_sv)
|
||||
return verb::trace;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
if(v[0] != 'N')
|
||||
break;
|
||||
v.remove_prefix(1);
|
||||
if(v == "BIND"_sv)
|
||||
return verb::unbind;
|
||||
if(v == "LINK"_sv)
|
||||
return verb::unlink;
|
||||
if(v == "LOCK"_sv)
|
||||
return verb::unlock;
|
||||
if(v == "SUBSCRIBE"_sv)
|
||||
return verb::unsubscribe;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return verb::unknown;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+997
@@ -0,0 +1,997 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_WRITE_HPP
|
||||
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/core/async_base.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/buffers_range.hpp>
|
||||
#include <boost/beast/core/make_printable.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <boost/beast/core/detail/is_invocable.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/dispatch.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 Handler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_some_op
|
||||
: public beast::async_base<
|
||||
Handler, beast::executor_type<Stream>>
|
||||
{
|
||||
Stream& s_;
|
||||
serializer<isRequest,Body, Fields>& sr_;
|
||||
|
||||
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)
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write_some"));
|
||||
|
||||
invoked = true;
|
||||
ec = {};
|
||||
op_.s_.async_write_some(
|
||||
buffers, std::move(op_));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template<class Handler_>
|
||||
write_some_op(
|
||||
Handler_&& h,
|
||||
Stream& s,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
: async_base<
|
||||
Handler, beast::executor_type<Stream>>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, s_(s)
|
||||
, sr_(sr)
|
||||
{
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
error_code ec;
|
||||
if(! sr_.is_done())
|
||||
{
|
||||
lambda f{*this};
|
||||
sr_.next(ec, f);
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write_some"));
|
||||
|
||||
auto ex = asio::get_associated_immediate_executor(*this, s_.get_executor());
|
||||
return net::dispatch(
|
||||
ex,
|
||||
beast::bind_front_handler(
|
||||
std::move(*this), ec, 0));
|
||||
}
|
||||
if(f.invoked)
|
||||
{
|
||||
// *this is now moved-from,
|
||||
return;
|
||||
}
|
||||
// What else could it be?
|
||||
BOOST_ASSERT(sr_.is_done());
|
||||
}
|
||||
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write_some"));
|
||||
|
||||
const auto ex = this->get_immediate_executor();
|
||||
return net::dispatch(
|
||||
ex,
|
||||
beast::bind_front_handler(
|
||||
std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
sr_.consume(bytes_transferred);
|
||||
this->complete_now(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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 Handler,
|
||||
class Stream,
|
||||
class Predicate,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
: public beast::async_base<
|
||||
Handler, beast::executor_type<Stream>>
|
||||
, public asio::coroutine
|
||||
{
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields>& sr_;
|
||||
std::size_t bytes_transferred_ = 0;
|
||||
net::cancellation_state st_{this->
|
||||
beast::async_base<Handler, beast::executor_type<Stream>>
|
||||
::get_cancellation_slot()};
|
||||
|
||||
public:
|
||||
using cancellation_slot_type = net::cancellation_slot;
|
||||
cancellation_slot_type get_cancellation_slot() const noexcept
|
||||
{
|
||||
return st_.slot();
|
||||
}
|
||||
|
||||
|
||||
template<class Handler_>
|
||||
write_op(
|
||||
Handler_&& h,
|
||||
Stream& s,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
: async_base<
|
||||
Handler, beast::executor_type<Stream>>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, s_(s)
|
||||
, sr_(sr)
|
||||
{
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec = {},
|
||||
std::size_t bytes_transferred = 0)
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
if(Predicate{}(sr_))
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write"));
|
||||
|
||||
const auto ex = this->get_immediate_executor();
|
||||
net::dispatch(
|
||||
ex,
|
||||
std::move(*this));
|
||||
}
|
||||
goto upcall;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write"));
|
||||
|
||||
beast::http::async_write_some(
|
||||
s_, sr_, std::move(*this));
|
||||
}
|
||||
bytes_transferred_ += bytes_transferred;
|
||||
if (!ec && st_.cancelled() != net::cancellation_type::none)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, net::error::operation_aborted);
|
||||
}
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(Predicate{}(sr_))
|
||||
break;
|
||||
}
|
||||
upcall:
|
||||
this->complete_now(ec, bytes_transferred_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Handler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_msg_op
|
||||
: public beast::stable_async_base<
|
||||
Handler, beast::executor_type<Stream>>
|
||||
{
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields>& sr_;
|
||||
|
||||
public:
|
||||
template<
|
||||
class Handler_,
|
||||
class... Args>
|
||||
write_msg_op(
|
||||
Handler_&& h,
|
||||
Stream& s,
|
||||
Args&&... args)
|
||||
: stable_async_base<
|
||||
Handler, beast::executor_type<Stream>>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, s_(s)
|
||||
, sr_(beast::allocate_stable<
|
||||
serializer<isRequest, Body, Fields>>(
|
||||
*this, std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write(msg)"));
|
||||
|
||||
async_write(s_, sr_, std::move(*this));
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
this->complete_now(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_write_some_op
|
||||
{
|
||||
template<
|
||||
class WriteHandler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
Stream* s,
|
||||
serializer<isRequest, Body, Fields>* sr)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_some_op<
|
||||
typename std::decay<WriteHandler>::type,
|
||||
Stream,
|
||||
isRequest, Body, Fields>(
|
||||
std::forward<WriteHandler>(h), *s, *sr);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_write_op
|
||||
{
|
||||
template<
|
||||
class WriteHandler,
|
||||
class Stream,
|
||||
class Predicate,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
Stream* s,
|
||||
Predicate const&,
|
||||
serializer<isRequest, Body, Fields>* sr)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_op<
|
||||
typename std::decay<WriteHandler>::type,
|
||||
Stream,
|
||||
Predicate,
|
||||
isRequest, Body, Fields>(
|
||||
std::forward<WriteHandler>(h), *s, *sr);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_write_msg_op
|
||||
{
|
||||
template<
|
||||
class WriteHandler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class... Args>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
Stream* s,
|
||||
message<isRequest, Body, Fields>* m,
|
||||
std::false_type,
|
||||
Args&&... args)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_msg_op<
|
||||
typename std::decay<WriteHandler>::type,
|
||||
Stream,
|
||||
isRequest, Body, Fields>(
|
||||
std::forward<WriteHandler>(h), *s, *m,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<
|
||||
class WriteHandler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class... Args>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
Stream* s,
|
||||
message<isRequest, Body, Fields> const* m,
|
||||
std::true_type,
|
||||
Args&&... args)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_msg_op<
|
||||
typename std::decay<WriteHandler>::type,
|
||||
Stream,
|
||||
isRequest, Body, Fields>(
|
||||
std::forward<WriteHandler>(h), *s, *m,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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 = net::write(
|
||||
stream_, buffers, ec);
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_some_impl(
|
||||
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 f.bytes_transferred;
|
||||
if(f.invoked)
|
||||
sr.consume(f.bytes_transferred);
|
||||
return f.bytes_transferred;
|
||||
}
|
||||
ec = {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_some_impl(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
run_write_some_op{},
|
||||
handler,
|
||||
&stream,
|
||||
&sr);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
return detail::write_some_impl(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_some(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
return detail::async_write_some_impl(stream, sr,
|
||||
std::forward<WriteHandler>(handler));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_header(SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write_header(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_header(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
sr.split(true);
|
||||
std::size_t bytes_transferred = 0;
|
||||
if(! sr.is_header_done())
|
||||
{
|
||||
detail::write_lambda<SyncWriteStream> f{stream};
|
||||
do
|
||||
{
|
||||
sr.next(ec, f);
|
||||
bytes_transferred += f.bytes_transferred;
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
BOOST_ASSERT(f.invoked);
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
while(! sr.is_header_done());
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_header(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
sr.split(true);
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_op{},
|
||||
handler,
|
||||
&stream,
|
||||
detail::serializer_is_header_done{},
|
||||
&sr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
std::size_t bytes_transferred = 0;
|
||||
sr.split(false);
|
||||
for(;;)
|
||||
{
|
||||
bytes_transferred +=
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
sr.split(false);
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_op{},
|
||||
handler,
|
||||
&stream,
|
||||
detail::serializer_is_done{},
|
||||
&sr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
return write(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
return write(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
WriteHandler&& handler,
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value>::type*)
|
||||
{
|
||||
static_assert(
|
||||
is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_msg_op{},
|
||||
handler,
|
||||
&stream,
|
||||
&msg,
|
||||
std::false_type{});
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
WriteHandler&& handler,
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value>::type*)
|
||||
{
|
||||
static_assert(
|
||||
is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_msg_op{},
|
||||
handler,
|
||||
&stream,
|
||||
&msg,
|
||||
std::true_type{});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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 = {};
|
||||
if(os_.fail())
|
||||
return;
|
||||
std::size_t bytes_transferred = 0;
|
||||
for(auto b : beast::buffers_range_ref(buffers))
|
||||
{
|
||||
os_.write(static_cast<char const*>(
|
||||
b.data()), b.size());
|
||||
if(os_.fail())
|
||||
return;
|
||||
bytes_transferred += b.size();
|
||||
}
|
||||
sr_.consume(bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<true, Fields> const& h)
|
||||
{
|
||||
typename Fields::writer fr{
|
||||
h, h.version(), h.method()};
|
||||
return os << beast::make_printable(fr.get());
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<false, Fields> const& h)
|
||||
{
|
||||
typename Fields::writer fr{
|
||||
h, h.version(), h.result_int()};
|
||||
return os << beast::make_printable(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 type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type 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)
|
||||
{
|
||||
os.setstate(std::ios::failbit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(! sr.is_done());
|
||||
return os;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user