Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+88
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
File diff suppressed because it is too large Load Diff
+138
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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