summaryrefslogtreecommitdiff
path: root/boost/beast/http/impl
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2019-12-05 15:11:01 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2019-12-05 15:11:01 +0900
commit3fdc3e5ee96dca5b11d1694975a65200787eab86 (patch)
tree5c1733853892b8397d67706fa453a9bd978d2102 /boost/beast/http/impl
parent88e602c57797660ebe0f9e15dbd64c1ff16dead3 (diff)
downloadboost-3fdc3e5ee96dca5b11d1694975a65200787eab86.tar.gz
boost-3fdc3e5ee96dca5b11d1694975a65200787eab86.tar.bz2
boost-3fdc3e5ee96dca5b11d1694975a65200787eab86.zip
Imported Upstream version 1.66.0upstream/1.66.0
Diffstat (limited to 'boost/beast/http/impl')
-rw-r--r--boost/beast/http/impl/basic_parser.ipp928
-rw-r--r--boost/beast/http/impl/chunk_encode.ipp707
-rw-r--r--boost/beast/http/impl/error.ipp120
-rw-r--r--boost/beast/http/impl/field.ipp561
-rw-r--r--boost/beast/http/impl/fields.ipp1380
-rw-r--r--boost/beast/http/impl/file_body_win32.ipp584
-rw-r--r--boost/beast/http/impl/message.ipp437
-rw-r--r--boost/beast/http/impl/parser.ipp56
-rw-r--r--boost/beast/http/impl/read.ipp821
-rw-r--r--boost/beast/http/impl/rfc7230.ipp572
-rw-r--r--boost/beast/http/impl/serializer.ipp430
-rw-r--r--boost/beast/http/impl/status.ipp252
-rw-r--r--boost/beast/http/impl/verb.ipp337
-rw-r--r--boost/beast/http/impl/write.ipp887
14 files changed, 8072 insertions, 0 deletions
diff --git a/boost/beast/http/impl/basic_parser.ipp b/boost/beast/http/impl/basic_parser.ipp
new file mode 100644
index 0000000000..355a76fb16
--- /dev/null
+++ b/boost/beast/http/impl/basic_parser.ipp
@@ -0,0 +1,928 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
+#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
+
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/make_unique.hpp>
+#include <algorithm>
+#include <utility>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<bool isRequest, class Derived>
+template<class OtherDerived>
+basic_parser<isRequest, Derived>::
+basic_parser(basic_parser<
+ isRequest, OtherDerived>&& other)
+ : body_limit_(other.body_limit_)
+ , len_(other.len_)
+ , buf_(std::move(other.buf_))
+ , buf_len_(other.buf_len_)
+ , skip_(other.skip_)
+ , header_limit_(other.header_limit_)
+ , status_(other.status_)
+ , state_(other.state_)
+ , f_(other.f_)
+{
+}
+
+template<bool isRequest, class Derived>
+bool
+basic_parser<isRequest, Derived>::
+keep_alive() const
+{
+ BOOST_ASSERT(is_header_done());
+ if(f_ & flagHTTP11)
+ {
+ if(f_ & flagConnectionClose)
+ return false;
+ }
+ else
+ {
+ if(! (f_ & flagConnectionKeepAlive))
+ return false;
+ }
+ return (f_ & flagNeedEOF) == 0;
+}
+
+template<bool isRequest, class Derived>
+boost::optional<std::uint64_t>
+basic_parser<isRequest, Derived>::
+content_length() const
+{
+ BOOST_ASSERT(is_header_done());
+ if(! (f_ & flagContentLength))
+ return boost::none;
+ return len_;
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+skip(bool v)
+{
+ BOOST_ASSERT(! got_some());
+ if(v)
+ f_ |= flagSkipBody;
+ else
+ f_ &= ~flagSkipBody;
+}
+
+template<bool isRequest, class Derived>
+template<class ConstBufferSequence>
+std::size_t
+basic_parser<isRequest, Derived>::
+put(ConstBufferSequence const& buffers,
+ error_code& ec)
+{
+ static_assert(boost::asio::is_const_buffer_sequence<
+ ConstBufferSequence>::value,
+ "ConstBufferSequence requirements not met");
+ using boost::asio::buffer_copy;
+ using boost::asio::buffer_size;
+ auto const p = boost::asio::buffer_sequence_begin(buffers);
+ auto const last = boost::asio::buffer_sequence_end(buffers);
+ if(p == last)
+ {
+ ec.assign(0, ec.category());
+ return 0;
+ }
+ if(std::next(p) == last)
+ {
+ // single buffer
+ return put(boost::asio::const_buffer(*p), ec);
+ }
+ auto const size = buffer_size(buffers);
+ if(size <= max_stack_buffer)
+ return put_from_stack(size, buffers, ec);
+ if(size > buf_len_)
+ {
+ // reallocate
+ buf_ = boost::make_unique_noinit<char[]>(size);
+ buf_len_ = size;
+ }
+ // flatten
+ buffer_copy(boost::asio::buffer(
+ buf_.get(), buf_len_), buffers);
+ return put(boost::asio::const_buffer{
+ buf_.get(), buf_len_}, ec);
+}
+
+template<bool isRequest, class Derived>
+std::size_t
+basic_parser<isRequest, Derived>::
+put(boost::asio::const_buffer const& buffer,
+ error_code& ec)
+{
+ BOOST_ASSERT(state_ != state::complete);
+ using boost::asio::buffer_size;
+ auto p = reinterpret_cast<
+ char const*>(buffer.data());
+ auto n = buffer.size();
+ auto const p0 = p;
+ auto const p1 = p0 + n;
+ ec.assign(0, ec.category());
+loop:
+ switch(state_)
+ {
+ case state::nothing_yet:
+ if(n == 0)
+ {
+ ec = error::need_more;
+ return 0;
+ }
+ state_ = state::start_line;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case state::start_line:
+ {
+ maybe_need_more(p, n, ec);
+ if(ec)
+ goto done;
+ parse_start_line(p, p + (std::min<std::size_t>)(
+ header_limit_, n), ec, is_request{});
+ if(ec)
+ {
+ if(ec == error::need_more)
+ {
+ if(n >= header_limit_)
+ {
+ ec = error::header_limit;
+ goto done;
+ }
+ if(p + 3 <= p1)
+ skip_ = static_cast<
+ std::size_t>(p1 - p - 3);
+ }
+ goto done;
+ }
+ BOOST_ASSERT(! is_done());
+ n = static_cast<std::size_t>(p1 - p);
+ if(p >= p1)
+ {
+ ec = error::need_more;
+ goto done;
+ }
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case state::fields:
+ maybe_need_more(p, n, ec);
+ if(ec)
+ goto done;
+ parse_fields(p, p + (std::min<std::size_t>)(
+ header_limit_, n), ec);
+ if(ec)
+ {
+ if(ec == error::need_more)
+ {
+ if(n >= header_limit_)
+ {
+ ec = error::header_limit;
+ goto done;
+ }
+ if(p + 3 <= p1)
+ skip_ = static_cast<
+ std::size_t>(p1 - p - 3);
+ }
+ goto done;
+ }
+ finish_header(ec, is_request{});
+ break;
+
+ case state::body0:
+ BOOST_ASSERT(! skip_);
+ impl().on_body_init_impl(content_length(), ec);
+ if(ec)
+ goto done;
+ state_ = state::body;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case state::body:
+ BOOST_ASSERT(! skip_);
+ parse_body(p, n, ec);
+ if(ec)
+ goto done;
+ break;
+
+ case state::body_to_eof0:
+ BOOST_ASSERT(! skip_);
+ impl().on_body_init_impl(content_length(), ec);
+ if(ec)
+ goto done;
+ state_ = state::body_to_eof;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case state::body_to_eof:
+ BOOST_ASSERT(! skip_);
+ parse_body_to_eof(p, n, ec);
+ if(ec)
+ goto done;
+ break;
+
+ case state::chunk_header0:
+ impl().on_body_init_impl(content_length(), ec);
+ if(ec)
+ goto done;
+ state_ = state::chunk_header;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case state::chunk_header:
+ parse_chunk_header(p, n, ec);
+ if(ec)
+ goto done;
+ break;
+
+ case state::chunk_body:
+ parse_chunk_body(p, n, ec);
+ if(ec)
+ goto done;
+ break;
+
+ case state::complete:
+ ec.assign(0, ec.category());
+ goto done;
+ }
+ if(p < p1 && ! is_done() && eager())
+ {
+ n = static_cast<std::size_t>(p1 - p);
+ goto loop;
+ }
+done:
+ return static_cast<std::size_t>(p - p0);
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+put_eof(error_code& ec)
+{
+ BOOST_ASSERT(got_some());
+ if( state_ == state::start_line ||
+ state_ == state::fields)
+ {
+ ec = error::partial_message;
+ return;
+ }
+ if(f_ & (flagContentLength | flagChunked))
+ {
+ if(state_ != state::complete)
+ {
+ ec = error::partial_message;
+ return;
+ }
+ ec.assign(0, ec.category());
+ return;
+ }
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ state_ = state::complete;
+}
+
+template<bool isRequest, class Derived>
+template<class ConstBufferSequence>
+std::size_t
+basic_parser<isRequest, Derived>::
+put_from_stack(std::size_t size,
+ ConstBufferSequence const& buffers,
+ error_code& ec)
+{
+ char buf[max_stack_buffer];
+ using boost::asio::buffer;
+ using boost::asio::buffer_copy;
+ buffer_copy(buffer(buf, sizeof(buf)), buffers);
+ return put(boost::asio::const_buffer{
+ buf, size}, ec);
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+maybe_need_more(
+ char const* p, std::size_t n,
+ error_code& ec)
+{
+ if(skip_ == 0)
+ return;
+ if( n > header_limit_)
+ n = header_limit_;
+ if(n < skip_ + 4)
+ {
+ ec = error::need_more;
+ return;
+ }
+ auto const term =
+ find_eom(p + skip_, p + n);
+ if(! term)
+ {
+ skip_ = n - 3;
+ if(skip_ + 4 > header_limit_)
+ {
+ ec = error::header_limit;
+ return;
+ }
+ ec = error::need_more;
+ return;
+ }
+ skip_ = 0;
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_start_line(
+ char const*& in, char const* last,
+ error_code& ec, std::true_type)
+{
+/*
+ request-line = method SP request-target SP HTTP-version CRLF
+ method = token
+*/
+ auto p = in;
+
+ string_view method;
+ parse_method(p, last, method, ec);
+ if(ec)
+ return;
+
+ string_view target;
+ parse_target(p, last, target, ec);
+ if(ec)
+ return;
+
+ int version = 0;
+ parse_version(p, last, version, ec);
+ if(ec)
+ return;
+ if(version < 10 || version > 11)
+ {
+ ec = error::bad_version;
+ return;
+ }
+
+ if(p + 2 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(p[0] != '\r' || p[1] != '\n')
+ {
+ ec = error::bad_version;
+ return;
+ }
+ p += 2;
+
+ if(version >= 11)
+ f_ |= flagHTTP11;
+
+ impl().on_request_impl(string_to_verb(method),
+ method, target, version, ec);
+ if(ec)
+ return;
+
+ in = p;
+ state_ = state::fields;
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_start_line(
+ char const*& in, char const* last,
+ error_code& ec, std::false_type)
+{
+/*
+ status-line = HTTP-version SP status-code SP reason-phrase CRLF
+ status-code = 3*DIGIT
+ reason-phrase = *( HTAB / SP / VCHAR / obs-text )
+*/
+ auto p = in;
+
+ int version = 0;
+ parse_version(p, last, version, ec);
+ if(ec)
+ return;
+ if(version < 10 || version > 11)
+ {
+ ec = error::bad_version;
+ return;
+ }
+
+ // SP
+ if(p + 1 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(*p++ != ' ')
+ {
+ ec = error::bad_version;
+ return;
+ }
+
+ parse_status(p, last, status_, ec);
+ if(ec)
+ return;
+
+ // parse reason CRLF
+ string_view reason;
+ parse_reason(p, last, reason, ec);
+ if(ec)
+ return;
+
+ if(version >= 11)
+ f_ |= flagHTTP11;
+
+ impl().on_response_impl(
+ status_, reason, version, ec);
+ if(ec)
+ return;
+
+ in = p;
+ state_ = state::fields;
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+parse_fields(char const*& in,
+ char const* last, error_code& ec)
+{
+ string_view name;
+ string_view value;
+ // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
+ static_string<max_obs_fold> buf;
+ auto p = in;
+ for(;;)
+ {
+ if(p + 2 > last)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(p[0] == '\r')
+ {
+ if(p[1] != '\n')
+ ec = error::bad_line_ending;
+ in = p + 2;
+ return;
+ }
+ parse_field(p, last, name, value, buf, ec);
+ if(ec)
+ return;
+ auto const f = string_to_field(name);
+ do_field(f, value, ec);
+ if(ec)
+ return;
+ impl().on_field_impl(f, name, value, ec);
+ if(ec)
+ return;
+ in = p;
+ }
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+finish_header(error_code& ec, std::true_type)
+{
+ // RFC 7230 section 3.3
+ // https://tools.ietf.org/html/rfc7230#section-3.3
+
+ if(f_ & flagSkipBody)
+ {
+ state_ = state::complete;
+ }
+ else if(f_ & flagContentLength)
+ {
+ if(len_ > body_limit_)
+ {
+ 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;
+ state_ = state::complete;
+ }
+
+ impl().on_header_impl(ec);
+ if(ec)
+ return;
+ if(state_ == state::complete)
+ {
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ }
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+finish_header(error_code& ec, std::false_type)
+{
+ // RFC 7230 section 3.3
+ // https://tools.ietf.org/html/rfc7230#section-3.3
+
+ if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
+ status_ / 100 == 1 || // 1xx e.g. Continue
+ status_ == 204 || // No Content
+ status_ == 304) // Not Modified
+ {
+ // 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_ > body_limit_)
+ {
+ 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
+ {
+ f_ |= flagHasBody;
+ f_ |= flagNeedEOF;
+ state_ = state::body_to_eof0;
+ }
+
+ impl().on_header_impl(ec);
+ if(ec)
+ return;
+ if(state_ == state::complete)
+ {
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ }
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_body(char const*& p,
+ std::size_t n, error_code& ec)
+{
+ n = impl().on_body_impl(string_view{p,
+ beast::detail::clamp(len_, n)}, ec);
+ p += n;
+ len_ -= n;
+ if(ec)
+ return;
+ if(len_ > 0)
+ return;
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ state_ = state::complete;
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_body_to_eof(char const*& p,
+ std::size_t n, error_code& ec)
+{
+ if(n > body_limit_)
+ {
+ ec = error::body_limit;
+ return;
+ }
+ body_limit_ = body_limit_ - n;
+ n = impl().on_body_impl(string_view{p, n}, ec);
+ p += n;
+ if(ec)
+ return;
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+parse_chunk_header(char const*& p0,
+ std::size_t n, error_code& ec)
+{
+/*
+ chunked-body = *chunk last-chunk trailer-part CRLF
+
+ chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
+ last-chunk = 1*("0") [ chunk-ext ] CRLF
+ trailer-part = *( header-field CRLF )
+
+ chunk-size = 1*HEXDIG
+ chunk-data = 1*OCTET ; a sequence of chunk-size octets
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+*/
+
+ auto p = p0;
+ auto const pend = p + n;
+ char const* eol;
+
+ if(! (f_ & flagFinalChunk))
+ {
+ if(n < skip_ + 2)
+ {
+ ec = error::need_more;
+ return;
+ }
+ if(f_ & flagExpectCRLF)
+ {
+ // Treat the last CRLF in a chunk as
+ // part of the next chunk, so p can
+ // be parsed in one call instead of two.
+ if(! parse_crlf(p))
+ {
+ ec = error::bad_chunk;
+ return;
+ }
+ }
+ eol = find_eol(p0 + skip_, pend, ec);
+ if(ec)
+ return;
+ if(! eol)
+ {
+ ec = error::need_more;
+ skip_ = n - 1;
+ return;
+ }
+ skip_ = static_cast<
+ std::size_t>(eol - 2 - p0);
+
+ std::uint64_t size;
+ if(! parse_hex(p, size))
+ {
+ ec = error::bad_chunk;
+ return;
+ }
+ if(size != 0)
+ {
+ if(size > body_limit_)
+ {
+ ec = error::body_limit;
+ return;
+ }
+ body_limit_ -= size;
+ auto const start = p;
+ parse_chunk_extensions(p, pend, ec);
+ if(ec)
+ return;
+ if(p != eol -2 )
+ {
+ ec = error::bad_chunk_extension;
+ return;
+ }
+ auto const ext = make_string(start, p);
+ impl().on_chunk_header_impl(size, ext, ec);
+ if(ec)
+ return;
+ len_ = size;
+ skip_ = 2;
+ p0 = eol;
+ f_ |= flagExpectCRLF;
+ state_ = state::chunk_body;
+ return;
+ }
+
+ f_ |= flagFinalChunk;
+ }
+ else
+ {
+ BOOST_ASSERT(n >= 5);
+ if(f_ & flagExpectCRLF)
+ BOOST_VERIFY(parse_crlf(p));
+ std::uint64_t size;
+ BOOST_VERIFY(parse_hex(p, size));
+ eol = find_eol(p, pend, ec);
+ BOOST_ASSERT(! ec);
+ }
+
+ auto eom = find_eom(p0 + skip_, pend);
+ if(! eom)
+ {
+ BOOST_ASSERT(n >= 3);
+ skip_ = n - 3;
+ ec = error::need_more;
+ return;
+ }
+
+ auto const start = p;
+ parse_chunk_extensions(p, pend, ec);
+ if(ec)
+ return;
+ if(p != eol - 2)
+ {
+ ec = error::bad_chunk_extension;
+ return;
+ }
+ auto const ext = make_string(start, p);
+ impl().on_chunk_header_impl(0, ext, ec);
+ if(ec)
+ return;
+ p = eol;
+ parse_fields(p, eom, ec);
+ if(ec)
+ return;
+ BOOST_ASSERT(p == eom);
+ p0 = eom;
+
+ impl().on_finish_impl(ec);
+ if(ec)
+ return;
+ state_ = state::complete;
+}
+
+template<bool isRequest, class Derived>
+inline
+void
+basic_parser<isRequest, Derived>::
+parse_chunk_body(char const*& p,
+ std::size_t n, error_code& ec)
+{
+ n = impl().on_chunk_body_impl(
+ len_, string_view{p,
+ beast::detail::clamp(len_, n)}, ec);
+ p += n;
+ len_ -= n;
+ if(len_ == 0)
+ state_ = state::chunk_header;
+}
+
+template<bool isRequest, class Derived>
+void
+basic_parser<isRequest, Derived>::
+do_field(field f,
+ string_view value, error_code& ec)
+{
+ // Connection
+ if(f == field::connection ||
+ f == field::proxy_connection)
+ {
+ auto const list = opt_token_list{value};
+ if(! validate_list(list))
+ {
+ // VFALCO Should this be a field specific error?
+ ec = error::bad_value;
+ return;
+ }
+ for(auto const& s : list)
+ {
+ if(iequals({"close", 5}, s))
+ {
+ f_ |= flagConnectionClose;
+ continue;
+ }
+
+ if(iequals({"keep-alive", 10}, s))
+ {
+ f_ |= flagConnectionKeepAlive;
+ continue;
+ }
+
+ if(iequals({"upgrade", 7}, s))
+ {
+ f_ |= flagConnectionUpgrade;
+ continue;
+ }
+ }
+ ec.assign(0, ec.category());
+ return;
+ }
+
+ // Content-Length
+ if(f == field::content_length)
+ {
+ if(f_ & flagContentLength)
+ {
+ // duplicate
+ ec = error::bad_content_length;
+ return;
+ }
+
+ if(f_ & flagChunked)
+ {
+ // conflicting field
+ ec = error::bad_content_length;
+ return;
+ }
+
+ std::uint64_t v;
+ if(! parse_dec(
+ value.begin(), value.end(), v))
+ {
+ ec = error::bad_content_length;
+ return;
+ }
+
+ ec.assign(0, ec.category());
+ len_ = v;
+ f_ |= flagContentLength;
+ return;
+ }
+
+ // Transfer-Encoding
+ if(f == field::transfer_encoding)
+ {
+ if(f_ & flagChunked)
+ {
+ // duplicate
+ ec = error::bad_transfer_encoding;
+ return;
+ }
+
+ if(f_ & flagContentLength)
+ {
+ // conflicting field
+ ec = error::bad_transfer_encoding;
+ return;
+ }
+
+ ec.assign(0, ec.category());
+ auto const v = token_list{value};
+ auto const p = std::find_if(v.begin(), v.end(),
+ [&](typename token_list::value_type const& s)
+ {
+ return iequals({"chunked", 7}, s);
+ });
+ if(p == v.end())
+ return;
+ if(std::next(p) != v.end())
+ return;
+ len_ = 0;
+ f_ |= flagChunked;
+ return;
+ }
+
+ // Upgrade
+ if(f == field::upgrade)
+ {
+ ec.assign(0, ec.category());
+ f_ |= flagUpgrade;
+ return;
+ }
+
+ ec.assign(0, ec.category());
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/chunk_encode.ipp b/boost/beast/http/impl/chunk_encode.ipp
new file mode 100644
index 0000000000..51296041f7
--- /dev/null
+++ b/boost/beast/http/impl/chunk_encode.ipp
@@ -0,0 +1,707 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP
+#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP
+
+#include <boost/beast/core/detail/varint.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/detail/rfc7230.hpp>
+#include <algorithm>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+inline
+chunk_header::
+chunk_header(std::size_t size)
+ : view_(
+ size,
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{})
+{
+ BOOST_ASSERT(size > 0);
+}
+
+inline
+chunk_header::
+chunk_header(
+ std::size_t size,
+ string_view extensions)
+ : view_(
+ size,
+ boost::asio::const_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_(
+ boost::asio::buffer_size(buffers),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ buffers,
+ chunk_crlf{})
+{
+}
+
+template<class ConstBufferSequence>
+chunk_body<ConstBufferSequence>::
+chunk_body(
+ ConstBufferSequence const& buffers,
+ string_view extensions)
+ : view_(
+ boost::asio::buffer_size(buffers),
+ boost::asio::const_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_(
+ boost::asio::buffer_size(buffers),
+ exts_->str(),
+ chunk_crlf{},
+ buffers,
+ chunk_crlf{})
+{
+}
+
+template<class ConstBufferSequence>
+template<class ChunkExtensions, class Allocator, class>
+chunk_body<ConstBufferSequence>::
+chunk_body(
+ ConstBufferSequence const& buffers,
+ ChunkExtensions&& extensions,
+ Allocator const& allocator)
+ : exts_(std::allocate_shared<detail::chunk_extensions_impl<
+ typename std::decay<ChunkExtensions>::type>>(allocator,
+ std::forward<ChunkExtensions>(extensions)))
+ , view_(
+ boost::asio::buffer_size(buffers),
+ exts_->str(),
+ chunk_crlf{},
+ buffers,
+ chunk_crlf{})
+{
+}
+
+//------------------------------------------------------------------------------
+
+template<class Trailer>
+template<class Allocator>
+auto
+chunk_last<Trailer>::
+prepare(Trailer const& trailer, Allocator const& allocator) ->
+ buffers_type
+{
+ auto sp = std::allocate_shared<typename
+ Trailer::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.assign(0, ec.category());
+ return it;
+ }
+ // BWS
+ if(*it == ' ' || *it == '\t')
+ {
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ }
+ }
+ // ';'
+ if(*it != ';')
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+semi:
+ ++it; // skip ';'
+ // BWS
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ }
+ // chunk-ext-name
+ {
+ if(! detail::is_token_char(*it))
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ auto const first = it;
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ {
+ emit_string_plus_empty(first, it);
+ return it;
+ }
+ if(! detail::is_token_char(*it))
+ break;
+ }
+ emit_string(first, it);
+ }
+ // BWS [ ";" / "=" ]
+ for(;;)
+ {
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ }
+ if(*it == ';')
+ {
+ emit_empty_string();
+ goto semi;
+ }
+ if(*it != '=')
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ ++it; // skip '='
+ // BWS
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ if(*it != ' ' && *it != '\t')
+ break;
+ ++it;
+ }
+ // chunk-ext-val
+ if(*it != '"')
+ {
+ // token
+ if(! detail::is_token_char(*it))
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ auto const first = it;
+ for(;;)
+ {
+ ++it;
+ if(it == last)
+ break;
+ if(! detail::is_token_char(*it))
+ break;
+ }
+ emit_string(first, it);
+ if(it == last)
+ return it;
+ }
+ else
+ {
+ // quoted-string
+ auto const first = ++it; // skip DQUOTE
+ // first pass, count chars
+ std::size_t len = 0;
+ for(;;)
+ {
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ if(*it == '"')
+ break;
+ if(*it == '\\')
+ {
+ ++it;
+ if(it == last)
+ {
+ ec = error::bad_chunk_extension;
+ return it;
+ }
+ }
+ ++len;
+ ++it;
+ }
+ // now build the string
+ auto const offset = range_.size();
+ range_.resize(
+ offset +
+ varint_size(len) +
+ len);
+ auto dest = &range_[offset];
+ varint_write(dest, len);
+ it = first;
+ for(;;)
+ {
+ BOOST_ASSERT(it != last);
+ if(*it == '"')
+ break;
+ if(*it == '\\')
+ {
+ ++it;
+ BOOST_ASSERT(it != last);
+ }
+ Traits::assign(*dest++, *it++);
+ }
+ ++it; // skip DQUOTE
+ }
+ goto loop;
+}
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+do_insert(string_view name, string_view value)
+{
+/*
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-string
+ token = 1*tchar
+ quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
+ qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
+ quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
+ obs-text = %x80-FF
+*/
+ if(value.empty())
+ {
+ s_.reserve(1 + name.size());
+ s_.push_back(';');
+ s_.append(name.data(), name.size());
+ return;
+ }
+
+ bool is_token = true;
+ for(auto const c : value)
+ {
+ if(! detail::is_token_char(c))
+ {
+ is_token = false;
+ break;
+ }
+ }
+ if(is_token)
+ {
+ // token
+ s_.reserve(1 + name.size() + 1 + value.size());
+ s_.push_back(';');
+ s_.append(name.data(), name.size());
+ s_.push_back('=');
+ s_.append(value.data(), value.size());
+ }
+ else
+ {
+ // quoted-string
+ s_.reserve(
+ 1 + name.size() + 1 +
+ 1 + value.size() + 20 + 1);
+ s_.push_back(';');
+ s_.append(name.data(), name.size());
+ s_.append("=\"", 2);
+ for(auto const c : value)
+ {
+ if(c == '\\')
+ s_.append(R"(\\)", 2);
+ else if(c == '\"')
+ s_.append(R"(\")", 2);
+ else
+ s_.push_back(c);
+ }
+ s_.push_back('"');
+ }
+}
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+parse(string_view s, error_code& ec)
+{
+ do_parse(s.data(), s.data() + s.size(), ec);
+ if(! ec)
+ {
+ s_.clear();
+ for(auto const& v : *this)
+ do_insert(v.first, v.second);
+ }
+}
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+insert(string_view name)
+{
+ do_insert(name, {});
+
+ using beast::detail::varint_size;
+ using beast::detail::varint_write;
+ auto const offset = range_.size();
+ range_.resize(
+ offset +
+ varint_size(name.size()) +
+ name.size() +
+ varint_size(0));
+ auto dest = &range_[offset];
+ varint_write(dest, name.size());
+ std::memcpy(dest, name.data(), name.size());
+ dest += name.size();
+ varint_write(dest, 0);
+}
+
+template<class Allocator>
+void
+basic_chunk_extensions<Allocator>::
+insert(string_view name, string_view value)
+{
+ do_insert(name, value);
+
+ using beast::detail::varint_size;
+ using beast::detail::varint_write;
+ auto const offset = range_.size();
+ range_.resize(
+ offset +
+ varint_size(name.size()) +
+ name.size() +
+ varint_size(value.size()) +
+ value.size());
+ auto dest = &range_[offset];
+ varint_write(dest, name.size());
+ std::memcpy(dest, name.data(), name.size());
+ dest += name.size();
+ varint_write(dest, value.size());
+ std::memcpy(dest, value.data(), value.size());
+}
+
+template<class Allocator>
+inline
+auto
+basic_chunk_extensions<Allocator>::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{range_.data()};
+}
+
+template<class Allocator>
+inline
+auto
+basic_chunk_extensions<Allocator>::
+end() const ->
+ const_iterator
+{
+ return const_iterator{
+ range_.data() + range_.size()};
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/error.ipp b/boost/beast/http/impl/error.ipp
new file mode 100644
index 0000000000..45075f5c18
--- /dev/null
+++ b/boost/beast/http/impl/error.ipp
@@ -0,0 +1,120 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_ERROR_IPP
+#define BOOST_BEAST_HTTP_IMPL_ERROR_IPP
+
+#include <type_traits>
+
+namespace boost {
+
+namespace system {
+template<>
+struct is_error_code_enum<beast::http::error>
+{
+ static bool const value = true;
+};
+} // system
+
+namespace beast {
+namespace http {
+namespace detail {
+
+class http_error_category : public error_category
+{
+public:
+ const char*
+ name() const noexcept override
+ {
+ return "beast.http";
+ }
+
+ std::string
+ message(int ev) const override
+ {
+ switch(static_cast<error>(ev))
+ {
+ case error::end_of_stream: return "end of stream";
+ case error::partial_message: return "partial message";
+ case error::need_more: return "need more";
+ case error::unexpected_body: return "unexpected body";
+ case error::need_buffer: return "need buffer";
+ case error::end_of_chunk: return "end of chunk";
+ case error::buffer_overflow: return "buffer overflow";
+ case error::header_limit: return "header limit exceeded";
+ case error::body_limit: return "body limit exceeded";
+ case error::bad_alloc: return "bad alloc";
+ case error::bad_line_ending: return "bad line ending";
+ case error::bad_method: return "bad method";
+ case error::bad_target: return "bad target";
+ case error::bad_version: return "bad version";
+ case error::bad_status: return "bad status";
+ case error::bad_reason: return "bad reason";
+ case error::bad_field: return "bad field";
+ case error::bad_value: return "bad value";
+ case error::bad_content_length: return "bad Content-Length";
+ case error::bad_transfer_encoding: return "bad Transfer-Encoding";
+ case error::bad_chunk: return "bad chunk";
+ case error::bad_chunk_extension: return "bad chunk extension";
+ case error::bad_obs_fold: return "bad obs-fold";
+
+ default:
+ return "beast.http error";
+ }
+ }
+
+ error_condition
+ default_error_condition(
+ int ev) const noexcept override
+ {
+ return error_condition{ev, *this};
+ }
+
+ bool
+ equivalent(int ev,
+ error_condition const& condition
+ ) const noexcept override
+ {
+ return condition.value() == ev &&
+ &condition.category() == this;
+ }
+
+ bool
+ equivalent(error_code const& error,
+ int ev) const noexcept override
+ {
+ return error.value() == ev &&
+ &error.category() == this;
+ }
+};
+
+inline
+error_category const&
+get_http_error_category()
+{
+ static http_error_category const cat{};
+ return cat;
+}
+
+} // detail
+
+inline
+error_code
+make_error_code(error ev)
+{
+ return error_code{
+ static_cast<std::underlying_type<error>::type>(ev),
+ detail::get_http_error_category()};
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/field.ipp b/boost/beast/http/impl/field.ipp
new file mode 100644
index 0000000000..8da470ff24
--- /dev/null
+++ b/boost/beast/http/impl/field.ipp
@@ -0,0 +1,561 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_FIELD_IPP
+#define BOOST_BEAST_HTTP_IMPL_FIELD_IPP
+
+#include <boost/beast/core/string.hpp>
+#include <algorithm>
+#include <array>
+#include <unordered_map>
+#include <vector>
+#include <boost/assert.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+
+struct field_table
+{
+ using array_type =
+ std::array<string_view, 352>;
+
+ struct hash
+ {
+ std::size_t
+ operator()(string_view s) const
+ {
+ auto const n = s.size();
+ return
+ beast::detail::ascii_tolower(s[0]) *
+ beast::detail::ascii_tolower(s[n/2]) ^
+ beast::detail::ascii_tolower(s[n-1]); // hist[] = 331, 10, max_load_factor = 0.15f
+ }
+ };
+
+ struct iequal
+ {
+ // assumes inputs have equal length
+ bool
+ operator()(
+ string_view lhs,
+ string_view rhs) const
+ {
+ auto p1 = lhs.data();
+ auto p2 = rhs.data();
+ auto pend = lhs.end();
+ char a, b;
+ while(p1 < pend)
+ {
+ a = *p1++;
+ b = *p2++;
+ if(a != b)
+ goto slow;
+ }
+ return true;
+
+ while(p1 < pend)
+ {
+ slow:
+ if( beast::detail::ascii_tolower(a) !=
+ beast::detail::ascii_tolower(b))
+ return false;
+ a = *p1++;
+ b = *p2++;
+ }
+ return true;
+ }
+ };
+
+ using map_type = std::unordered_map<
+ string_view, field, hash, iequal>;
+
+ array_type by_name_;
+ std::vector<map_type> by_size_;
+/*
+ From:
+
+ https://www.iana.org/assignments/message-headers/message-headers.xhtml
+*/
+ field_table()
+ : by_name_({{
+ "<unknown-field>",
+ "A-IM",
+ "Accept",
+ "Accept-Additions",
+ "Accept-Charset",
+ "Accept-Datetime",
+ "Accept-Encoding",
+ "Accept-Features",
+ "Accept-Language",
+ "Accept-Patch",
+ "Accept-Post",
+ "Accept-Ranges",
+ "Access-Control",
+ "Access-Control-Allow-Credentials",
+ "Access-Control-Allow-Headers",
+ "Access-Control-Allow-Methods",
+ "Access-Control-Allow-Origin",
+ "Access-Control-Max-Age",
+ "Access-Control-Request-Headers",
+ "Access-Control-Request-Method",
+ "Age",
+ "Allow",
+ "ALPN",
+ "Also-Control",
+ "Alt-Svc",
+ "Alt-Used",
+ "Alternate-Recipient",
+ "Alternates",
+ "Apparently-To",
+ "Apply-To-Redirect-Ref",
+ "Approved",
+ "Archive",
+ "Archived-At",
+ "Article-Names",
+ "Article-Updates",
+ "Authentication-Control",
+ "Authentication-Info",
+ "Authentication-Results",
+ "Authorization",
+ "Auto-Submitted",
+ "Autoforwarded",
+ "Autosubmitted",
+ "Base",
+ "Bcc",
+ "Body",
+ "C-Ext",
+ "C-Man",
+ "C-Opt",
+ "C-PEP",
+ "C-PEP-Info",
+ "Cache-Control",
+ "CalDAV-Timezones",
+ "Cancel-Key",
+ "Cancel-Lock",
+ "Cc",
+ "Close",
+ "Comments",
+ "Compliance",
+ "Connection",
+ "Content-Alternative",
+ "Content-Base",
+ "Content-Description",
+ "Content-Disposition",
+ "Content-Duration",
+ "Content-Encoding",
+ "Content-features",
+ "Content-ID",
+ "Content-Identifier",
+ "Content-Language",
+ "Content-Length",
+ "Content-Location",
+ "Content-MD5",
+ "Content-Range",
+ "Content-Return",
+ "Content-Script-Type",
+ "Content-Style-Type",
+ "Content-Transfer-Encoding",
+ "Content-Type",
+ "Content-Version",
+ "Control",
+ "Conversion",
+ "Conversion-With-Loss",
+ "Cookie",
+ "Cookie2",
+ "Cost",
+ "DASL",
+ "Date",
+ "Date-Received",
+ "DAV",
+ "Default-Style",
+ "Deferred-Delivery",
+ "Delivery-Date",
+ "Delta-Base",
+ "Depth",
+ "Derived-From",
+ "Destination",
+ "Differential-ID",
+ "Digest",
+ "Discarded-X400-IPMS-Extensions",
+ "Discarded-X400-MTS-Extensions",
+ "Disclose-Recipients",
+ "Disposition-Notification-Options",
+ "Disposition-Notification-To",
+ "Distribution",
+ "DKIM-Signature",
+ "DL-Expansion-History",
+ "Downgraded-Bcc",
+ "Downgraded-Cc",
+ "Downgraded-Disposition-Notification-To",
+ "Downgraded-Final-Recipient",
+ "Downgraded-From",
+ "Downgraded-In-Reply-To",
+ "Downgraded-Mail-From",
+ "Downgraded-Message-Id",
+ "Downgraded-Original-Recipient",
+ "Downgraded-Rcpt-To",
+ "Downgraded-References",
+ "Downgraded-Reply-To",
+ "Downgraded-Resent-Bcc",
+ "Downgraded-Resent-Cc",
+ "Downgraded-Resent-From",
+ "Downgraded-Resent-Reply-To",
+ "Downgraded-Resent-Sender",
+ "Downgraded-Resent-To",
+ "Downgraded-Return-Path",
+ "Downgraded-Sender",
+ "Downgraded-To",
+ "EDIINT-Features",
+ "Eesst-Version",
+ "Encoding",
+ "Encrypted",
+ "Errors-To",
+ "ETag",
+ "Expect",
+ "Expires",
+ "Expiry-Date",
+ "Ext",
+ "Followup-To",
+ "Forwarded",
+ "From",
+ "Generate-Delivery-Report",
+ "GetProfile",
+ "Hobareg",
+ "Host",
+ "HTTP2-Settings",
+ "If",
+ "If-Match",
+ "If-Modified-Since",
+ "If-None-Match",
+ "If-Range",
+ "If-Schedule-Tag-Match",
+ "If-Unmodified-Since",
+ "IM",
+ "Importance",
+ "In-Reply-To",
+ "Incomplete-Copy",
+ "Injection-Date",
+ "Injection-Info",
+ "Jabber-ID",
+ "Keep-Alive",
+ "Keywords",
+ "Label",
+ "Language",
+ "Last-Modified",
+ "Latest-Delivery-Time",
+ "Lines",
+ "Link",
+ "List-Archive",
+ "List-Help",
+ "List-ID",
+ "List-Owner",
+ "List-Post",
+ "List-Subscribe",
+ "List-Unsubscribe",
+ "List-Unsubscribe-Post",
+ "Location",
+ "Lock-Token",
+ "Man",
+ "Max-Forwards",
+ "Memento-Datetime",
+ "Message-Context",
+ "Message-ID",
+ "Message-Type",
+ "Meter",
+ "Method-Check",
+ "Method-Check-Expires",
+ "MIME-Version",
+ "MMHS-Acp127-Message-Identifier",
+ "MMHS-Authorizing-Users",
+ "MMHS-Codress-Message-Indicator",
+ "MMHS-Copy-Precedence",
+ "MMHS-Exempted-Address",
+ "MMHS-Extended-Authorisation-Info",
+ "MMHS-Handling-Instructions",
+ "MMHS-Message-Instructions",
+ "MMHS-Message-Type",
+ "MMHS-Originator-PLAD",
+ "MMHS-Originator-Reference",
+ "MMHS-Other-Recipients-Indicator-CC",
+ "MMHS-Other-Recipients-Indicator-To",
+ "MMHS-Primary-Precedence",
+ "MMHS-Subject-Indicator-Codes",
+ "MT-Priority",
+ "Negotiate",
+ "Newsgroups",
+ "NNTP-Posting-Date",
+ "NNTP-Posting-Host",
+ "Non-Compliance",
+ "Obsoletes",
+ "Opt",
+ "Optional",
+ "Optional-WWW-Authenticate",
+ "Ordering-Type",
+ "Organization",
+ "Origin",
+ "Original-Encoded-Information-Types",
+ "Original-From",
+ "Original-Message-ID",
+ "Original-Recipient",
+ "Original-Sender",
+ "Original-Subject",
+ "Originator-Return-Address",
+ "Overwrite",
+ "P3P",
+ "Path",
+ "PEP",
+ "Pep-Info",
+ "PICS-Label",
+ "Position",
+ "Posting-Version",
+ "Pragma",
+ "Prefer",
+ "Preference-Applied",
+ "Prevent-NonDelivery-Report",
+ "Priority",
+ "Privicon",
+ "ProfileObject",
+ "Protocol",
+ "Protocol-Info",
+ "Protocol-Query",
+ "Protocol-Request",
+ "Proxy-Authenticate",
+ "Proxy-Authentication-Info",
+ "Proxy-Authorization",
+ "Proxy-Connection",
+ "Proxy-Features",
+ "Proxy-Instruction",
+ "Public",
+ "Public-Key-Pins",
+ "Public-Key-Pins-Report-Only",
+ "Range",
+ "Received",
+ "Received-SPF",
+ "Redirect-Ref",
+ "References",
+ "Referer",
+ "Referer-Root",
+ "Relay-Version",
+ "Reply-By",
+ "Reply-To",
+ "Require-Recipient-Valid-Since",
+ "Resent-Bcc",
+ "Resent-Cc",
+ "Resent-Date",
+ "Resent-From",
+ "Resent-Message-ID",
+ "Resent-Reply-To",
+ "Resent-Sender",
+ "Resent-To",
+ "Resolution-Hint",
+ "Resolver-Location",
+ "Retry-After",
+ "Return-Path",
+ "Safe",
+ "Schedule-Reply",
+ "Schedule-Tag",
+ "Sec-WebSocket-Accept",
+ "Sec-WebSocket-Extensions",
+ "Sec-WebSocket-Key",
+ "Sec-WebSocket-Protocol",
+ "Sec-WebSocket-Version",
+ "Security-Scheme",
+ "See-Also",
+ "Sender",
+ "Sensitivity",
+ "Server",
+ "Set-Cookie",
+ "Set-Cookie2",
+ "SetProfile",
+ "SIO-Label",
+ "SIO-Label-History",
+ "SLUG",
+ "SoapAction",
+ "Solicitation",
+ "Status-URI",
+ "Strict-Transport-Security",
+ "Subject",
+ "SubOK",
+ "Subst",
+ "Summary",
+ "Supersedes",
+ "Surrogate-Capability",
+ "Surrogate-Control",
+ "TCN",
+ "TE",
+ "Timeout",
+ "Title",
+ "To",
+ "Topic",
+ "Trailer",
+ "Transfer-Encoding",
+ "TTL",
+ "UA-Color",
+ "UA-Media",
+ "UA-Pixels",
+ "UA-Resolution",
+ "UA-Windowpixels",
+ "Upgrade",
+ "Urgency",
+ "URI",
+ "User-Agent",
+ "Variant-Vary",
+ "Vary",
+ "VBR-Info",
+ "Version",
+ "Via",
+ "Want-Digest",
+ "Warning",
+ "WWW-Authenticate",
+ "X-Archived-At",
+ "X-Device-Accept",
+ "X-Device-Accept-Charset",
+ "X-Device-Accept-Encoding",
+ "X-Device-Accept-Language",
+ "X-Device-User-Agent",
+ "X-Frame-Options",
+ "X-Mittente",
+ "X-PGP-Sig",
+ "X-Ricevuta",
+ "X-Riferimento-Message-ID",
+ "X-TipoRicevuta",
+ "X-Trasporto",
+ "X-VerificaSicurezza",
+ "X400-Content-Identifier",
+ "X400-Content-Return",
+ "X400-Content-Type",
+ "X400-MTS-Identifier",
+ "X400-Originator",
+ "X400-Received",
+ "X400-Recipients",
+ "X400-Trace",
+ "Xref"
+ }})
+ {
+ // find the longest field length
+ std::size_t high = 0;
+ for(auto const& s : by_name_)
+ if(high < s.size())
+ high = s.size();
+ // build by_size map
+ // skip field::unknown
+ by_size_.resize(high + 1);
+ for(auto& map : by_size_)
+ map.max_load_factor(.15f);
+ for(std::size_t i = 1;
+ i < by_name_.size(); ++i)
+ {
+ auto const& s = by_name_[i];
+ by_size_[s.size()].emplace(
+ s, static_cast<field>(i));
+ }
+
+#if 0
+ // This snippet calculates the performance
+ // of the hash function and map settings
+ {
+ std::vector<std::size_t> hist;
+ for(auto const& map : by_size_)
+ {
+ for(std::size_t i = 0; i < map.bucket_count(); ++i)
+ {
+ auto const n = map.bucket_size(i);
+ if(n > 0)
+ {
+ if(hist.size() < n)
+ hist.resize(n);
+ ++hist[n-1];
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ field
+ string_to_field(string_view s) const
+ {
+ if(s.size() >= by_size_.size())
+ return field::unknown;
+ auto const& map = by_size_[s.size()];
+ if(map.empty())
+ return field::unknown;
+ auto it = map.find(s);
+ if(it == map.end())
+ return field::unknown;
+ return it->second;
+ }
+
+ //
+ // Deprecated
+ //
+
+ using const_iterator =
+ array_type::const_iterator;
+
+ std::size_t
+ size() const
+ {
+ return by_name_.size();
+ }
+
+ const_iterator
+ begin() const
+ {
+ return by_name_.begin();
+ }
+
+ const_iterator
+ end() const
+ {
+ return by_name_.end();
+ }
+};
+
+inline
+field_table const&
+get_field_table()
+{
+ static field_table const tab;
+ return tab;
+}
+
+template<class = void>
+string_view
+to_string(field f)
+{
+ auto const& v = get_field_table();
+ BOOST_ASSERT(static_cast<unsigned>(f) < v.size());
+ return v.begin()[static_cast<unsigned>(f)];
+}
+
+} // detail
+
+inline
+string_view
+to_string(field f)
+{
+ return detail::to_string(f);
+}
+
+inline
+field
+string_to_field(string_view s)
+{
+ return detail::get_field_table().string_to_field(s);
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/fields.ipp b/boost/beast/http/impl/fields.ipp
new file mode 100644
index 0000000000..67d7cc45b4
--- /dev/null
+++ b/boost/beast/http/impl/fields.ipp
@@ -0,0 +1,1380 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
+#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
+
+#include <boost/beast/core/buffers_cat.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/core/static_string.hpp>
+#include <boost/beast/core/detail/buffers_ref.hpp>
+#include <boost/beast/http/verb.hpp>
+#include <boost/beast/http/rfc7230.hpp>
+#include <boost/beast/http/status.hpp>
+#include <boost/beast/http/chunk_encode.hpp>
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+#include <string>
+
+#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
+#ifndef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+#define BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+#endif
+#endif
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<class Allocator>
+class basic_fields<Allocator>::writer
+{
+public:
+ using iter_type = typename list_t::const_iterator;
+
+ struct field_iterator
+ {
+ iter_type it_;
+
+ using value_type = boost::asio::const_buffer;
+ using pointer = value_type const*;
+ using reference = value_type const;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category =
+ std::bidirectional_iterator_tag;
+
+ field_iterator() = default;
+ field_iterator(field_iterator&& other) = default;
+ field_iterator(field_iterator const& other) = default;
+ field_iterator& operator=(field_iterator&& other) = default;
+ field_iterator& operator=(field_iterator const& other) = default;
+
+ explicit
+ field_iterator(iter_type it)
+ : it_(it)
+ {
+ }
+
+ bool
+ operator==(field_iterator const& other) const
+ {
+ return it_ == other.it_;
+ }
+
+ bool
+ operator!=(field_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return it_->buffer();
+ }
+
+ field_iterator&
+ operator++()
+ {
+ ++it_;
+ return *this;
+ }
+
+ field_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ field_iterator&
+ operator--()
+ {
+ --it_;
+ return *this;
+ }
+
+ field_iterator
+ operator--(int)
+ {
+ auto temp = *this;
+ --(*this);
+ return temp;
+ }
+ };
+
+ class field_range
+ {
+ field_iterator first_;
+ field_iterator last_;
+
+ public:
+ using const_iterator =
+ field_iterator;
+
+ using value_type =
+ typename const_iterator::value_type;
+
+ field_range(iter_type first, iter_type last)
+ : first_(first)
+ , last_(last)
+ {
+ }
+
+ const_iterator
+ begin() const
+ {
+ return first_;
+ }
+
+ const_iterator
+ end() const
+ {
+ return last_;
+ }
+ };
+
+ using view_type = buffers_cat_view<
+ boost::asio::const_buffer,
+ boost::asio::const_buffer,
+ boost::asio::const_buffer,
+ field_range,
+ chunk_crlf>;
+
+ basic_fields const& f_;
+ boost::optional<view_type> view_;
+ char buf_[13];
+
+public:
+ using const_buffers_type =
+ beast::detail::buffers_ref<view_type>;
+
+ writer(basic_fields const& f,
+ unsigned version, verb v);
+
+ writer(basic_fields const& f,
+ unsigned version, unsigned code);
+
+ writer(basic_fields const& f);
+
+ const_buffers_type
+ get() const
+ {
+ return const_buffers_type(*view_);
+ }
+};
+
+template<class Allocator>
+basic_fields<Allocator>::writer::
+writer(basic_fields const& f)
+ : f_(f)
+{
+ view_.emplace(
+ boost::asio::const_buffer{nullptr, 0},
+ boost::asio::const_buffer{nullptr, 0},
+ boost::asio::const_buffer{nullptr, 0},
+ field_range(f_.list_.begin(), f_.list_.end()),
+ chunk_crlf());
+}
+
+template<class Allocator>
+basic_fields<Allocator>::writer::
+writer(basic_fields const& f,
+ unsigned version, verb v)
+ : f_(f)
+{
+/*
+ request
+ "<method>"
+ " <target>"
+ " HTTP/X.Y\r\n" (11 chars)
+*/
+ string_view sv;
+ if(v == verb::unknown)
+ sv = f_.get_method_impl();
+ else
+ sv = to_string(v);
+
+ // target_or_reason_ has a leading SP
+
+ buf_[0] = ' ';
+ buf_[1] = 'H';
+ buf_[2] = 'T';
+ buf_[3] = 'T';
+ buf_[4] = 'P';
+ buf_[5] = '/';
+ buf_[6] = '0' + static_cast<char>(version / 10);
+ buf_[7] = '.';
+ buf_[8] = '0' + static_cast<char>(version % 10);
+ buf_[9] = '\r';
+ buf_[10]= '\n';
+
+ view_.emplace(
+ boost::asio::const_buffer{sv.data(), sv.size()},
+ boost::asio::const_buffer{
+ f_.target_or_reason_.data(),
+ f_.target_or_reason_.size()},
+ boost::asio::const_buffer{buf_, 11},
+ field_range(f_.list_.begin(), f_.list_.end()),
+ chunk_crlf());
+}
+
+template<class Allocator>
+basic_fields<Allocator>::writer::
+writer(basic_fields const& f,
+ unsigned version, unsigned code)
+ : f_(f)
+{
+/*
+ response
+ "HTTP/X.Y ### " (13 chars)
+ "<reason>"
+ "\r\n"
+*/
+ buf_[0] = 'H';
+ buf_[1] = 'T';
+ buf_[2] = 'T';
+ buf_[3] = 'P';
+ buf_[4] = '/';
+ buf_[5] = '0' + static_cast<char>(version / 10);
+ buf_[6] = '.';
+ buf_[7] = '0' + static_cast<char>(version % 10);
+ buf_[8] = ' ';
+ buf_[9] = '0' + static_cast<char>(code / 100);
+ buf_[10]= '0' + static_cast<char>((code / 10) % 10);
+ buf_[11]= '0' + static_cast<char>(code % 10);
+ buf_[12]= ' ';
+
+ string_view sv;
+ if(! f_.target_or_reason_.empty())
+ sv = f_.target_or_reason_;
+ else
+ sv = obsolete_reason(static_cast<status>(code));
+
+ view_.emplace(
+ boost::asio::const_buffer{buf_, 13},
+ boost::asio::const_buffer{sv.data(), sv.size()},
+ boost::asio::const_buffer{"\r\n", 2},
+ field_range(f_.list_.begin(), f_.list_.end()),
+ chunk_crlf{});
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+basic_fields<Allocator>::
+value_type::
+value_type(field name,
+ string_view sname, string_view value)
+ : off_(static_cast<off_t>(sname.size() + 2))
+ , len_(static_cast<off_t>(value.size()))
+ , f_(name)
+{
+ //BOOST_ASSERT(name == field::unknown ||
+ // iequals(sname, to_string(name)));
+ char* p = reinterpret_cast<char*>(this + 1);
+ p[off_-2] = ':';
+ p[off_-1] = ' ';
+ p[off_ + len_] = '\r';
+ p[off_ + len_ + 1] = '\n';
+ std::memcpy(p, sname.data(), sname.size());
+ std::memcpy(p + off_, value.data(), value.size());
+}
+
+template<class Allocator>
+inline
+field const
+basic_fields<Allocator>::
+value_type::
+name() const
+{
+ return f_;
+}
+
+template<class Allocator>
+inline
+string_view const
+basic_fields<Allocator>::
+value_type::
+name_string() const
+{
+ return {reinterpret_cast<
+ char const*>(this + 1),
+ static_cast<std::size_t>(off_ - 2)};
+}
+
+template<class Allocator>
+inline
+string_view const
+basic_fields<Allocator>::
+value_type::
+value() const
+{
+ return {reinterpret_cast<
+ char const*>(this + 1) + off_,
+ static_cast<std::size_t>(len_)};
+}
+
+template<class Allocator>
+inline
+boost::asio::const_buffer
+basic_fields<Allocator>::
+value_type::
+buffer() const
+{
+ return boost::asio::const_buffer{
+ reinterpret_cast<char const*>(this + 1),
+ static_cast<std::size_t>(off_) + len_ + 2};
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+basic_fields<Allocator>::
+~basic_fields()
+{
+ delete_list();
+ realloc_string(method_, {});
+ realloc_string(
+ target_or_reason_, {});
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(Allocator const& alloc)
+ : alloc_(alloc)
+{
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(basic_fields&& other)
+ : alloc_(std::move(other.alloc_))
+ , set_(std::move(other.set_))
+ , list_(std::move(other.list_))
+ , method_(other.method_)
+ , target_or_reason_(other.target_or_reason_)
+{
+ other.method_.clear();
+ other.target_or_reason_.clear();
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(basic_fields&& other, Allocator const& alloc)
+ : alloc_(alloc)
+{
+ if(alloc_ != other.alloc_)
+ {
+ copy_all(other);
+ other.clear_all();
+ }
+ else
+ {
+ set_ = std::move(other.set_);
+ list_ = std::move(other.list_);
+ method_ = other.method_;
+ target_or_reason_ = other.target_or_reason_;
+ }
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(basic_fields const& other)
+ : alloc_(alloc_traits::
+ select_on_container_copy_construction(other.alloc_))
+{
+ copy_all(other);
+}
+
+template<class Allocator>
+basic_fields<Allocator>::
+basic_fields(basic_fields const& other,
+ Allocator const& alloc)
+ : alloc_(alloc)
+{
+ copy_all(other);
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+basic_fields<Allocator>::
+basic_fields(basic_fields<OtherAlloc> const& other)
+{
+ copy_all(other);
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+basic_fields<Allocator>::
+basic_fields(basic_fields<OtherAlloc> const& other,
+ Allocator const& alloc)
+ : alloc_(alloc)
+{
+ copy_all(other);
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+operator=(basic_fields&& other) ->
+ basic_fields&
+{
+ if(this == &other)
+ return *this;
+ move_assign(other, std::integral_constant<bool,
+ alloc_traits:: propagate_on_container_move_assignment::value>{});
+ return *this;
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+operator=(basic_fields const& other) ->
+ basic_fields&
+{
+ copy_assign(other, std::integral_constant<bool,
+ alloc_traits::propagate_on_container_copy_assignment::value>{});
+ return *this;
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+auto
+basic_fields<Allocator>::
+operator=(basic_fields<OtherAlloc> const& other) ->
+ basic_fields&
+{
+ clear_all();
+ copy_all(other);
+ return *this;
+}
+
+//------------------------------------------------------------------------------
+//
+// Element access
+//
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+string_view const
+basic_fields<Allocator>::
+at(field name) const
+{
+ BOOST_ASSERT(name != field::unknown);
+ auto const it = find(name);
+ if(it == end())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "field not found"});
+ return it->value();
+}
+
+template<class Allocator>
+string_view const
+basic_fields<Allocator>::
+at(string_view name) const
+{
+ auto const it = find(name);
+ if(it == end())
+ BOOST_THROW_EXCEPTION(std::out_of_range{
+ "field not found"});
+ return it->value();
+}
+
+template<class Allocator>
+string_view const
+basic_fields<Allocator>::
+operator[](field name) const
+{
+ BOOST_ASSERT(name != field::unknown);
+ auto const it = find(name);
+ if(it == end())
+ return {};
+ return it->value();
+}
+
+template<class Allocator>
+string_view const
+basic_fields<Allocator>::
+operator[](string_view name) const
+{
+ auto const it = find(name);
+ if(it == end())
+ return {};
+ return it->value();
+}
+
+//------------------------------------------------------------------------------
+//
+// Modifiers
+//
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+clear()
+{
+ delete_list();
+ set_.clear();
+ list_.clear();
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+insert(field name, string_param const& value)
+{
+ BOOST_ASSERT(name != field::unknown);
+ insert(name, to_string(name), value);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+insert(string_view sname, string_param const& value)
+{
+ auto const name =
+ string_to_field(sname);
+ insert(name, sname, value);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+insert(field name,
+ string_view sname, string_param const& value)
+{
+ auto& e = new_element(name, sname,
+ static_cast<string_view>(value));
+ auto const before =
+ set_.upper_bound(sname, key_compare{});
+ if(before == set_.begin())
+ {
+ BOOST_ASSERT(count(sname) == 0);
+ set_.insert_before(before, e);
+ list_.push_back(e);
+ return;
+ }
+ auto const last = std::prev(before);
+ // VFALCO is it worth comparing `field name` first?
+ if(! iequals(sname, last->name_string()))
+ {
+ BOOST_ASSERT(count(sname) == 0);
+ set_.insert_before(before, e);
+ list_.push_back(e);
+ return;
+ }
+ // keep duplicate fields together in the list
+ set_.insert_before(before, e);
+ list_.insert(++list_.iterator_to(*last), e);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set(field name, string_param const& value)
+{
+ BOOST_ASSERT(name != field::unknown);
+ set_element(new_element(name, to_string(name),
+ static_cast<string_view>(value)));
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set(string_view sname, string_param const& value)
+{
+ set_element(new_element(
+ string_to_field(sname), sname,
+ static_cast<string_view>(value)));
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+erase(const_iterator pos) ->
+ const_iterator
+{
+ auto next = pos.iter();
+ auto& e = *next++;
+ set_.erase(e);
+ list_.erase(e);
+ delete_element(e);
+ return next;
+}
+
+template<class Allocator>
+std::size_t
+basic_fields<Allocator>::
+erase(field name)
+{
+ BOOST_ASSERT(name != field::unknown);
+ return erase(to_string(name));
+}
+
+template<class Allocator>
+std::size_t
+basic_fields<Allocator>::
+erase(string_view name)
+{
+ std::size_t n =0;
+ set_.erase_and_dispose(name, key_compare{},
+ [&](value_type* e)
+ {
+ ++n;
+ list_.erase(list_.iterator_to(*e));
+ delete_element(*e);
+ });
+ return n;
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+swap(basic_fields<Allocator>& other)
+{
+ swap(other, std::integral_constant<bool,
+ alloc_traits::propagate_on_container_swap::value>{});
+}
+
+template<class Allocator>
+void
+swap(
+ basic_fields<Allocator>& lhs,
+ basic_fields<Allocator>& rhs)
+{
+ lhs.swap(rhs);
+}
+
+//------------------------------------------------------------------------------
+//
+// Lookup
+//
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+inline
+std::size_t
+basic_fields<Allocator>::
+count(field name) const
+{
+ BOOST_ASSERT(name != field::unknown);
+ return count(to_string(name));
+}
+
+template<class Allocator>
+std::size_t
+basic_fields<Allocator>::
+count(string_view name) const
+{
+ return set_.count(name, key_compare{});
+}
+
+template<class Allocator>
+inline
+auto
+basic_fields<Allocator>::
+find(field name) const ->
+ const_iterator
+{
+ BOOST_ASSERT(name != field::unknown);
+ return find(to_string(name));
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+find(string_view name) const ->
+ const_iterator
+{
+ auto const it = set_.find(
+ name, key_compare{});
+ if(it == set_.end())
+ return list_.end();
+ return list_.iterator_to(*it);
+}
+
+template<class Allocator>
+inline
+auto
+basic_fields<Allocator>::
+equal_range(field name) const ->
+ std::pair<const_iterator, const_iterator>
+{
+ BOOST_ASSERT(name != field::unknown);
+ return equal_range(to_string(name));
+}
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+equal_range(string_view name) const ->
+ std::pair<const_iterator, const_iterator>
+{
+ auto result =
+ set_.equal_range(name, key_compare{});
+ if(result.first == result.second)
+ return {list_.end(), list_.end()};
+ return {
+ list_.iterator_to(*result.first),
+ ++list_.iterator_to(*(--result.second))};
+}
+
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+// Filter a token list
+//
+template<class String, class Pred>
+void
+filter_token_list(
+ String& s,
+ string_view value,
+ Pred&& 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->data(), it->size());
+ while(++it != last)
+ {
+ if(! pred(*it))
+ {
+ s.append(", ");
+ s.append(it->data(), it->size());
+ }
+ }
+}
+
+// Filter the last item in a token list
+template<class String, class Pred>
+void
+filter_token_list_last(
+ String& s,
+ string_view value,
+ Pred&& 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->data(), it->size());
+ return;
+ }
+ s.append(it->data(), it->size());
+ for(;;)
+ {
+ it = next;
+ next = std::next(it);
+ if(next == te.end())
+ {
+ if(! pred(*it))
+ {
+ s.append(", ");
+ s.append(it->data(), it->size());
+ }
+ return;
+ }
+ s.append(", ");
+ s.append(it->data(), it->size());
+ }
+ }
+}
+
+template<class String>
+void
+keep_alive_impl(
+ String& s, string_view value,
+ unsigned version, bool keep_alive)
+{
+ if(version < 11)
+ {
+ if(keep_alive)
+ {
+ // remove close
+ filter_token_list(s, value,
+ [](string_view s)
+ {
+ return iequals(s, "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,
+ [](string_view s)
+ {
+ return
+ iequals(s, "close") ||
+ iequals(s, "keep-alive");
+ });
+ }
+ }
+ else
+ {
+ if(keep_alive)
+ {
+ // remove close and keep-alive
+ filter_token_list(s, value,
+ [](string_view s)
+ {
+ return
+ iequals(s, "close") ||
+ iequals(s, "keep-alive");
+ });
+ }
+ else
+ {
+ // remove keep-alive
+ filter_token_list(s, value,
+ [](string_view s)
+ {
+ return iequals(s, "keep-alive");
+ });
+ // add close
+ if(s.empty())
+ s.append("close");
+ else if(! token_list{value}.exists("close"))
+ s.append(", close");
+ }
+ }
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+// Fields
+
+template<class Allocator>
+inline
+string_view
+basic_fields<Allocator>::
+get_method_impl() const
+{
+ return method_;
+}
+
+template<class Allocator>
+inline
+string_view
+basic_fields<Allocator>::
+get_target_impl() const
+{
+ if(target_or_reason_.empty())
+ return target_or_reason_;
+ return {
+ target_or_reason_.data() + 1,
+ target_or_reason_.size() - 1};
+}
+
+template<class Allocator>
+inline
+string_view
+basic_fields<Allocator>::
+get_reason_impl() const
+{
+ return target_or_reason_;
+}
+
+template<class Allocator>
+bool
+basic_fields<Allocator>::
+get_chunked_impl() const
+{
+ auto const te = token_list{
+ (*this)[field::transfer_encoding]};
+ for(auto it = te.begin(); it != te.end();)
+ {
+ auto const next = std::next(it);
+ if(next == te.end())
+ return iequals(*it, "chunked");
+ it = next;
+ }
+ return false;
+}
+
+template<class Allocator>
+bool
+basic_fields<Allocator>::
+get_keep_alive_impl(unsigned version) const
+{
+ auto const it = find(field::connection);
+ if(version < 11)
+ {
+ if(it == end())
+ return false;
+ return token_list{
+ it->value()}.exists("keep-alive");
+ }
+ if(it == end())
+ return true;
+ return ! token_list{
+ it->value()}.exists("close");
+}
+
+template<class Allocator>
+bool
+basic_fields<Allocator>::
+has_content_length_impl() const
+{
+ return count(field::content_length) > 0;
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+set_method_impl(string_view s)
+{
+ realloc_string(method_, s);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+set_target_impl(string_view s)
+{
+ realloc_target(
+ target_or_reason_, s);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+set_reason_impl(string_view s)
+{
+ realloc_string(
+ target_or_reason_, s);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set_chunked_impl(bool value)
+{
+ auto it = find(field::transfer_encoding);
+ if(value)
+ {
+ // append "chunked"
+ if(it == end())
+ {
+ set(field::transfer_encoding, "chunked");
+ return;
+ }
+ auto const te = token_list{it->value()};
+ for(auto itt = te.begin();;)
+ {
+ auto const next = std::next(itt);
+ if(next == te.end())
+ {
+ if(iequals(*itt, "chunked"))
+ return; // already set
+ break;
+ }
+ itt = next;
+ }
+ static_string<max_static_buffer> buf;
+ if(it->value().size() <= buf.size() + 9)
+ {
+ buf.append(it->value().data(), it->value().size());
+ buf.append(", chunked", 9);
+ set(field::transfer_encoding, buf);
+ }
+ else
+ {
+ #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
+ std::string s;
+ #else
+ using rebind_type =
+ typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<char>;
+ std::basic_string<
+ char,
+ std::char_traits<char>,
+ rebind_type> s{rebind_type{alloc_}};
+ #endif
+ s.reserve(it->value().size() + 9);
+ s.append(it->value().data(), it->value().size());
+ s.append(", chunked", 9);
+ set(field::transfer_encoding, s);
+ }
+ return;
+ }
+ // filter "chunked"
+ if(it == end())
+ return;
+ try
+ {
+ static_string<max_static_buffer> buf;
+ detail::filter_token_list_last(buf, it->value(),
+ [](string_view s)
+ {
+ return iequals(s, "chunked");
+ });
+ if(! buf.empty())
+ set(field::transfer_encoding, buf);
+ else
+ erase(field::transfer_encoding);
+ }
+ catch(std::length_error const&)
+ {
+ #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
+ std::string s;
+ #else
+ using rebind_type =
+ typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<char>;
+ std::basic_string<
+ char,
+ std::char_traits<char>,
+ rebind_type> s{rebind_type{alloc_}};
+ #endif
+ s.reserve(it->value().size());
+ detail::filter_token_list_last(s, it->value(),
+ [](string_view s)
+ {
+ return iequals(s, "chunked");
+ });
+ if(! s.empty())
+ set(field::transfer_encoding, s);
+ else
+ erase(field::transfer_encoding);
+ }
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set_content_length_impl(
+ boost::optional<std::uint64_t> const& value)
+{
+ if(! value)
+ erase(field::content_length);
+ else
+ set(field::content_length, *value);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set_keep_alive_impl(
+ unsigned version, bool keep_alive)
+{
+ // VFALCO What about Proxy-Connection ?
+ auto const value = (*this)[field::connection];
+ try
+ {
+ static_string<max_static_buffer> buf;
+ detail::keep_alive_impl(
+ buf, value, version, keep_alive);
+ if(buf.empty())
+ erase(field::connection);
+ else
+ set(field::connection, buf);
+ }
+ catch(std::length_error const&)
+ {
+ #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
+ // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
+ std::string s;
+ #else
+ using rebind_type =
+ typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<char>;
+ std::basic_string<
+ char,
+ std::char_traits<char>,
+ rebind_type> s{rebind_type{alloc_}};
+ #endif
+ s.reserve(value.size());
+ detail::keep_alive_impl(
+ s, value, version, keep_alive);
+ if(s.empty())
+ erase(field::connection);
+ else
+ set(field::connection, s);
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+auto
+basic_fields<Allocator>::
+new_element(field name,
+ string_view sname, string_view value) ->
+ value_type&
+{
+ if(sname.size() + 2 >
+ (std::numeric_limits<off_t>::max)())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "field name too large"});
+ if(value.size() + 2 >
+ (std::numeric_limits<off_t>::max)())
+ BOOST_THROW_EXCEPTION(std::length_error{
+ "field value too large"});
+ value = detail::trim(value);
+ std::uint16_t const off =
+ static_cast<off_t>(sname.size() + 2);
+ std::uint16_t const len =
+ static_cast<off_t>(value.size());
+ auto const p = alloc_traits::allocate(alloc_,
+ 1 + (off + len + 2 + sizeof(value_type) - 1) /
+ sizeof(value_type));
+ // VFALCO allocator can't call the constructor because its private
+ //alloc_traits::construct(alloc_, p, name, sname, value);
+ new(p) value_type{name, sname, value};
+ return *p;
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+delete_element(value_type& e)
+{
+ auto const n = 1 + (e.off_ + e.len_ + 2 +
+ sizeof(value_type) - 1) / sizeof(value_type);
+ alloc_traits::destroy(alloc_, &e);
+ alloc_traits::deallocate(alloc_, &e, n);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+set_element(value_type& e)
+{
+ auto it = set_.lower_bound(
+ e.name_string(), key_compare{});
+ if(it == set_.end() || ! iequals(
+ e.name_string(), it->name_string()))
+ {
+ set_.insert_before(it, e);
+ list_.push_back(e);
+ return;
+ }
+ for(;;)
+ {
+ auto next = it;
+ ++next;
+ set_.erase(it);
+ list_.erase(list_.iterator_to(*it));
+ delete_element(*it);
+ it = next;
+ if(it == set_.end() ||
+ ! iequals(e.name_string(), it->name_string()))
+ break;
+ }
+ set_.insert_before(it, e);
+ list_.push_back(e);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+realloc_string(string_view& dest, string_view s)
+{
+ if(dest.empty() && s.empty())
+ return;
+ auto a = typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<
+ char>(alloc_);
+ if(! dest.empty())
+ {
+ a.deallocate(const_cast<char*>(
+ dest.data()), dest.size());
+ dest.clear();
+ }
+ if(! s.empty())
+ {
+ auto const p = a.allocate(s.size());
+ std::memcpy(p, s.data(), s.size());
+ dest = {p, s.size()};
+ }
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+realloc_target(
+ string_view& dest, string_view s)
+{
+ // The target string are stored with an
+ // extra space at the beginning to help
+ // the writer class.
+ if(dest.empty() && s.empty())
+ return;
+ auto a = typename beast::detail::allocator_traits<
+ Allocator>::template rebind_alloc<
+ char>(alloc_);
+ if(! dest.empty())
+ {
+ a.deallocate(const_cast<char*>(
+ dest.data()), dest.size());
+ dest.clear();
+ }
+ if(! s.empty())
+ {
+ auto const p = a.allocate(1 + s.size());
+ p[0] = ' ';
+ std::memcpy(p + 1, s.data(), s.size());
+ dest = {p, 1 + s.size()};
+ }
+}
+
+template<class Allocator>
+template<class OtherAlloc>
+void
+basic_fields<Allocator>::
+copy_all(basic_fields<OtherAlloc> const& other)
+{
+ for(auto const& e : other.list_)
+ insert(e.name(), e.name_string(), e.value());
+ realloc_string(method_, other.method_);
+ realloc_string(target_or_reason_,
+ other.target_or_reason_);
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+clear_all()
+{
+ clear();
+ realloc_string(method_, {});
+ realloc_string(target_or_reason_, {});
+}
+
+template<class Allocator>
+void
+basic_fields<Allocator>::
+delete_list()
+{
+ for(auto it = list_.begin(); it != list_.end();)
+ delete_element(*it++);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+move_assign(basic_fields& other, std::true_type)
+{
+ clear_all();
+ set_ = std::move(other.set_);
+ list_ = std::move(other.list_);
+ method_ = other.method_;
+ target_or_reason_ = other.target_or_reason_;
+ other.method_.clear();
+ other.target_or_reason_.clear();
+ alloc_ = other.alloc_;
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+move_assign(basic_fields& other, std::false_type)
+{
+ clear_all();
+ if(alloc_ != other.alloc_)
+ {
+ copy_all(other);
+ other.clear_all();
+ }
+ else
+ {
+ set_ = std::move(other.set_);
+ list_ = std::move(other.list_);
+ method_ = other.method_;
+ target_or_reason_ = other.target_or_reason_;
+ other.method_.clear();
+ other.target_or_reason_.clear();
+ }
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+copy_assign(basic_fields const& other, std::true_type)
+{
+ clear_all();
+ alloc_ = other.alloc_;
+ copy_all(other);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+copy_assign(basic_fields const& other, std::false_type)
+{
+ clear_all();
+ copy_all(other);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+swap(basic_fields& other, std::true_type)
+{
+ using std::swap;
+ swap(alloc_, other.alloc_);
+ swap(set_, other.set_);
+ swap(list_, other.list_);
+ swap(method_, other.method_);
+ swap(target_or_reason_, other.target_or_reason_);
+}
+
+template<class Allocator>
+inline
+void
+basic_fields<Allocator>::
+swap(basic_fields& other, std::false_type)
+{
+ BOOST_ASSERT(alloc_ == other.alloc_);
+ using std::swap;
+ swap(set_, other.set_);
+ swap(list_, other.list_);
+ swap(method_, other.method_);
+ swap(target_or_reason_, other.target_or_reason_);
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/file_body_win32.ipp b/boost/beast/http/impl/file_body_win32.ipp
new file mode 100644
index 0000000000..e11c443ed2
--- /dev/null
+++ b/boost/beast/http/impl/file_body_win32.ipp
@@ -0,0 +1,584 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
+#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
+
+#if BOOST_BEAST_USE_WIN32_FILE
+
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/http/serializer.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/basic_stream_socket.hpp>
+#include <boost/asio/handler_continuation_hook.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 <algorithm>
+#include <cstring>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+template<class, class, bool, class>
+class write_some_win32_op;
+} // detail
+
+template<>
+struct basic_file_body<file_win32>
+{
+ using file_type = file_win32;
+
+ class writer;
+ class reader;
+
+ //--------------------------------------------------------------------------
+
+ class value_type
+ {
+ friend class writer;
+ friend class reader;
+ friend struct basic_file_body<file_win32>;
+
+ template<class, class, bool, class>
+ friend class detail::write_some_win32_op;
+ template<
+ class Protocol, bool isRequest, class Fields>
+ friend
+ std::size_t
+ write_some(
+ boost::asio::basic_stream_socket<Protocol>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr,
+ error_code& ec);
+
+ file_win32 file_;
+ std::uint64_t size_ = 0; // cached file size
+ std::uint64_t first_; // starting offset of the range
+ std::uint64_t last_; // ending offset of the range
+
+ public:
+ ~value_type() = default;
+ value_type() = default;
+ value_type(value_type&& other) = default;
+ value_type& operator=(value_type&& other) = default;
+
+ bool
+ is_open() const
+ {
+ return file_.is_open();
+ }
+
+ std::uint64_t
+ size() const
+ {
+ return size_;
+ }
+
+ void
+ close();
+
+ void
+ open(char const* path, file_mode mode, error_code& ec);
+
+ void
+ reset(file_win32&& file, error_code& ec);
+ };
+
+ //--------------------------------------------------------------------------
+
+ class writer
+ {
+ template<class, class, bool, class>
+ friend class detail::write_some_win32_op;
+ template<
+ class Protocol, bool isRequest, class Fields>
+ friend
+ std::size_t
+ write_some(
+ boost::asio::basic_stream_socket<Protocol>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr,
+ error_code& ec);
+
+ value_type& body_; // The body we are reading from
+ std::uint64_t pos_; // The current position in the file
+ char buf_[4096]; // Small buffer for reading
+
+ public:
+ using const_buffers_type =
+ boost::asio::const_buffer;
+
+ template<bool isRequest, class Fields>
+ writer(message<isRequest,
+ basic_file_body<file_win32>, Fields>& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(error_code&)
+ {
+ BOOST_ASSERT(body_.file_.is_open());
+ pos_ = body_.first_;
+ }
+
+ boost::optional<std::pair<const_buffers_type, bool>>
+ get(error_code& ec)
+ {
+ std::size_t const n = (std::min)(sizeof(buf_),
+ beast::detail::clamp(body_.last_ - pos_));
+ if(n == 0)
+ {
+ ec.assign(0, ec.category());
+ return boost::none;
+ }
+ auto const nread = body_.file_.read(buf_, n, ec);
+ if(ec)
+ return boost::none;
+ BOOST_ASSERT(nread != 0);
+ pos_ += nread;
+ ec.assign(0, ec.category());
+ return {{
+ {buf_, nread}, // buffer to return.
+ pos_ < body_.last_}}; // `true` if there are more buffers.
+ }
+ };
+
+ //--------------------------------------------------------------------------
+
+ class reader
+ {
+ value_type& body_;
+
+ public:
+ template<bool isRequest, class Fields>
+ explicit
+ reader(message<isRequest, basic_file_body, Fields>& m)
+ : body_(m.body())
+ {
+ }
+
+ void
+ init(boost::optional<
+ std::uint64_t> const& content_length,
+ error_code& ec)
+ {
+ // VFALCO We could reserve space in the file
+ boost::ignore_unused(content_length);
+ BOOST_ASSERT(body_.file_.is_open());
+ ec.assign(0, ec.category());
+ }
+
+ template<class ConstBufferSequence>
+ std::size_t
+ put(ConstBufferSequence const& buffers,
+ error_code& ec)
+ {
+ std::size_t nwritten = 0;
+ for(auto buffer : beast::detail::buffers_range(buffers))
+ {
+ nwritten += body_.file_.write(
+ buffer.data(), buffer.size(), ec);
+ if(ec)
+ return nwritten;
+ }
+ ec.assign(0, ec.category());
+ return nwritten;
+ }
+
+ void
+ finish(error_code& ec)
+ {
+ ec.assign(0, ec.category());
+ }
+ };
+
+ //--------------------------------------------------------------------------
+
+ static
+ std::uint64_t
+ size(value_type const& body)
+ {
+ return body.size();
+ }
+};
+
+//------------------------------------------------------------------------------
+
+inline
+void
+basic_file_body<file_win32>::
+value_type::
+close()
+{
+ error_code ignored;
+ file_.close(ignored);
+}
+
+inline
+void
+basic_file_body<file_win32>::
+value_type::
+open(char const* path, file_mode mode, error_code& ec)
+{
+ file_.open(path, mode, ec);
+ if(ec)
+ return;
+ size_ = file_.size(ec);
+ if(ec)
+ {
+ close();
+ return;
+ }
+ first_ = 0;
+ last_ = size_;
+}
+
+inline
+void
+basic_file_body<file_win32>::
+value_type::
+reset(file_win32&& file, error_code& ec)
+{
+ if(file_.is_open())
+ {
+ error_code ignored;
+ file_.close(ignored);
+ }
+ file_ = std::move(file);
+ if(file_.is_open())
+ {
+ size_ = file_.size(ec);
+ if(ec)
+ {
+ close();
+ return;
+ }
+ first_ = 0;
+ last_ = size_;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+template<class Unsigned>
+inline
+boost::winapi::DWORD_
+lowPart(Unsigned n)
+{
+ return static_cast<
+ boost::winapi::DWORD_>(
+ n & 0xffffffff);
+}
+
+template<class Unsigned>
+inline
+boost::winapi::DWORD_
+highPart(Unsigned n, std::true_type)
+{
+ return static_cast<
+ boost::winapi::DWORD_>(
+ (n>>32)&0xffffffff);
+}
+
+template<class Unsigned>
+inline
+boost::winapi::DWORD_
+highPart(Unsigned, std::false_type)
+{
+ return 0;
+}
+
+template<class Unsigned>
+inline
+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);
+ }
+};
+
+//------------------------------------------------------------------------------
+
+#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
+
+template<
+ class Protocol, class Handler,
+ bool isRequest, class Fields>
+class write_some_win32_op
+{
+ boost::asio::basic_stream_socket<Protocol>& sock_;
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr_;
+ std::size_t bytes_transferred_ = 0;
+ Handler h_;
+ bool header_ = false;
+
+public:
+ write_some_win32_op(write_some_win32_op&&) = default;
+ write_some_win32_op(write_some_win32_op const&) = default;
+
+ template<class DeducedHandler>
+ write_some_win32_op(
+ DeducedHandler&& h,
+ boost::asio::basic_stream_socket<Protocol>& s,
+ serializer<isRequest,
+ basic_file_body<file_win32>,Fields>& sr)
+ : sock_(s)
+ , sr_(sr)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type =
+ boost::asio::associated_executor_t<Handler, decltype(std::declval<
+ boost::asio::basic_stream_socket<Protocol>&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, sock_.get_executor());
+ }
+
+ void
+ operator()();
+
+ void
+ operator()(
+ error_code ec,
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(write_some_win32_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<
+ class Protocol, class Handler,
+ bool isRequest, class Fields>
+void
+write_some_win32_op<
+ Protocol, Handler, isRequest, Fields>::
+operator()()
+{
+ if(! sr_.is_header_done())
+ {
+ header_ = true;
+ sr_.split(true);
+ return detail::async_write_some(
+ sock_, sr_, std::move(*this));
+ }
+ if(sr_.get().chunked())
+ {
+ return detail::async_write_some(
+ sock_, sr_, std::move(*this));
+ }
+ auto& r = sr_.reader_impl();
+ boost::winapi::DWORD_ const nNumberOfBytesToWrite =
+ static_cast<boost::winapi::DWORD_>(
+ (std::min<std::uint64_t>)(
+ (std::min<std::uint64_t>)(r.body_.last_ - r.pos_, sr_.limit()),
+ (std::numeric_limits<boost::winapi::DWORD_>::max)()));
+ boost::asio::windows::overlapped_ptr overlapped{
+ sock_.get_executor().context(), *this};
+ auto& ov = *overlapped.get();
+ ov.Offset = lowPart(r.pos_);
+ ov.OffsetHigh = highPart(r.pos_);
+ auto const bSuccess = ::TransmitFile(
+ sock_.native_handle(),
+ sr_.get().body().file_.native_handle(),
+ nNumberOfBytesToWrite,
+ 0,
+ overlapped.get(),
+ nullptr,
+ 0);
+ auto const dwError = ::GetLastError();
+ if(! bSuccess && dwError !=
+ boost::winapi::ERROR_IO_PENDING_)
+ {
+ // VFALCO This needs review, is 0 the right number?
+ // completed immediately (with error?)
+ overlapped.complete(error_code{static_cast<int>(
+ boost::winapi::GetLastError()),
+ system_category()}, 0);
+ return;
+ }
+ overlapped.release();
+}
+
+template<
+ class Protocol, class Handler,
+ bool isRequest, class Fields>
+void
+write_some_win32_op<
+ Protocol, Handler, isRequest, Fields>::
+operator()(
+ error_code ec, std::size_t bytes_transferred)
+{
+ bytes_transferred_ += bytes_transferred;
+ if(! ec)
+ {
+ if(header_)
+ {
+ header_ = false;
+ return (*this)();
+ }
+ auto& r = sr_.reader_impl();
+ r.pos_ += bytes_transferred;
+ BOOST_ASSERT(r.pos_ <= r.body_.last_);
+ if(r.pos_ >= r.body_.last_)
+ {
+ sr_.next(ec, null_lambda{});
+ BOOST_ASSERT(! ec);
+ BOOST_ASSERT(sr_.is_done());
+ }
+ }
+ h_(ec, bytes_transferred_);
+}
+
+#endif
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+template<class Protocol, bool isRequest, class Fields>
+std::size_t
+write_some(
+ boost::asio::basic_stream_socket<Protocol>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr,
+ error_code& ec)
+{
+ if(! sr.is_header_done())
+ {
+ sr.split(true);
+ auto const bytes_transferred =
+ detail::write_some(sock, sr, ec);
+ if(ec)
+ return bytes_transferred;
+ return bytes_transferred;
+ }
+ if(sr.get().chunked())
+ {
+ auto const bytes_transferred =
+ detail::write_some(sock, sr, ec);
+ if(ec)
+ return bytes_transferred;
+ return bytes_transferred;
+ }
+ auto& r = sr.reader_impl();
+ r.body_.file_.seek(r.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>)(r.body_.last_ - r.pos_, sr.limit()),
+ (std::numeric_limits<boost::winapi::DWORD_>::max)()));
+ auto const bSuccess = ::TransmitFile(
+ sock.native_handle(),
+ r.body_.file_.native_handle(),
+ nNumberOfBytesToWrite,
+ 0,
+ nullptr,
+ nullptr,
+ 0);
+ if(! bSuccess)
+ {
+ ec.assign(static_cast<int>(
+ boost::winapi::GetLastError()),
+ system_category());
+ return 0;
+ }
+ r.pos_ += nNumberOfBytesToWrite;
+ BOOST_ASSERT(r.pos_ <= r.body_.last_);
+ if(r.pos_ < r.body_.last_)
+ {
+ ec.assign(0, ec.category());
+ }
+ else
+ {
+ sr.next(ec, detail::null_lambda{});
+ BOOST_ASSERT(! ec);
+ BOOST_ASSERT(sr.is_done());
+ }
+ return nNumberOfBytesToWrite;
+}
+
+#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
+
+template<
+ class Protocol,
+ bool isRequest, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_some(
+ boost::asio::basic_stream_socket<Protocol>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr,
+ WriteHandler&& handler)
+{
+ boost::asio::async_completion<WriteHandler,
+ void(error_code)> init{handler};
+ detail::write_some_win32_op<
+ Protocol,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ isRequest, Fields>{
+ init.completion_handler, sock, sr}();
+ return init.result.get();
+}
+
+#endif
+
+} // http
+} // beast
+} // boost
+
+#endif
+
+#endif
diff --git a/boost/beast/http/impl/message.ipp b/boost/beast/http/impl/message.ipp
new file mode 100644
index 0000000000..64d96eb6f1
--- /dev/null
+++ b/boost/beast/http/impl/message.ipp
@@ -0,0 +1,437 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP
+#define BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP
+
+#include <boost/beast/core/error.hpp>
+#include <boost/beast/core/detail/type_traits.hpp>
+#include <boost/assert.hpp>
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<class Fields>
+template<class Arg1, class... ArgN, class>
+header<true, Fields>::
+header(Arg1&& arg1, ArgN&&... argn)
+ : Fields(std::forward<Arg1>(arg1),
+ std::forward<ArgN>(argn)...)
+{
+}
+
+template<class Fields>
+inline
+verb
+header<true, Fields>::
+method() const
+{
+ return method_;
+}
+
+template<class Fields>
+void
+header<true, Fields>::
+method(verb v)
+{
+ if(v == verb::unknown)
+ BOOST_THROW_EXCEPTION(
+ std::invalid_argument{"unknown method"});
+ method_ = v;
+ this->set_method_impl({});
+}
+
+template<class Fields>
+string_view
+header<true, Fields>::
+method_string() const
+{
+ if(method_ != verb::unknown)
+ return to_string(method_);
+ return this->get_method_impl();
+}
+
+template<class Fields>
+void
+header<true, Fields>::
+method_string(string_view s)
+{
+ method_ = string_to_verb(s);
+ if(method_ != verb::unknown)
+ this->set_method_impl({});
+ else
+ this->set_method_impl(s);
+}
+
+template<class Fields>
+inline
+string_view
+header<true, Fields>::
+target() const
+{
+ return this->get_target_impl();
+}
+
+template<class Fields>
+inline
+void
+header<true, Fields>::
+target(string_view s)
+{
+ this->set_target_impl(s);
+}
+
+template<class Fields>
+void
+swap(
+ header<true, Fields>& h1,
+ header<true, Fields>& h2)
+{
+ using std::swap;
+ swap(
+ static_cast<Fields&>(h1),
+ static_cast<Fields&>(h2));
+ swap(h1.version_, h2.version_);
+ swap(h1.method_, h2.method_);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Fields>
+template<class Arg1, class... ArgN, class>
+header<false, Fields>::
+header(Arg1&& arg1, ArgN&&... argn)
+ : Fields(std::forward<Arg1>(arg1),
+ std::forward<ArgN>(argn)...)
+{
+}
+
+template<class Fields>
+inline
+status
+header<false, Fields>::
+result() const
+{
+ return int_to_status(
+ static_cast<int>(result_));
+}
+
+template<class Fields>
+inline
+void
+header<false, Fields>::
+result(status v)
+{
+ result_ = v;
+}
+
+template<class Fields>
+inline
+void
+header<false, Fields>::
+result(unsigned v)
+{
+ if(v > 999)
+ BOOST_THROW_EXCEPTION(
+ std::invalid_argument{
+ "invalid status-code"});
+ result_ = static_cast<status>(v);
+}
+
+template<class Fields>
+inline
+unsigned
+header<false, Fields>::
+result_int() const
+{
+ return static_cast<unsigned>(result_);
+}
+
+template<class Fields>
+string_view
+header<false, Fields>::
+reason() const
+{
+ auto const s = this->get_reason_impl();
+ if(! s.empty())
+ return s;
+ return obsolete_reason(result_);
+}
+
+template<class Fields>
+inline
+void
+header<false, Fields>::
+reason(string_view s)
+{
+ this->set_reason_impl(s);
+}
+
+template<class Fields>
+void
+swap(
+ header<false, Fields>& h1,
+ header<false, Fields>& h2)
+{
+ using std::swap;
+ swap(
+ static_cast<Fields&>(h1),
+ static_cast<Fields&>(h2));
+ swap(h1.version_, h2.version_);
+ swap(h1.result_, h2.result_);
+}
+
+//------------------------------------------------------------------------------
+
+template<bool isRequest, class Body, class Fields>
+template<class... BodyArgs>
+message<isRequest, Body, Fields>::
+message(header_type&& h, BodyArgs&&... body_args)
+ : header_type(std::move(h))
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ 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)
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ 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)
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ 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))
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ 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)
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ 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))
+ , beast::detail::empty_base_optimization<
+ typename Body::value_type>(
+ std::forward<BodyArg>(body_arg))
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+message<isRequest, Body, Fields>::
+message(std::piecewise_construct_t)
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class... BodyArgs>
+message<isRequest, Body, Fields>::
+message(std::piecewise_construct_t,
+ std::tuple<BodyArgs...> body_args)
+ : message(std::piecewise_construct,
+ body_args,
+ beast::detail::make_index_sequence<
+ sizeof...(BodyArgs)>{})
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+template<class... BodyArgs, class... FieldsArgs>
+message<isRequest, Body, Fields>::
+message(std::piecewise_construct_t,
+ std::tuple<BodyArgs...> body_args,
+ std::tuple<FieldsArgs...> fields_args)
+ : message(std::piecewise_construct,
+ body_args,
+ fields_args,
+ beast::detail::make_index_sequence<
+ sizeof...(BodyArgs)>{},
+ beast::detail::make_index_sequence<
+ sizeof...(FieldsArgs)>{})
+{
+}
+
+template<bool isRequest, class Body, class Fields>
+void
+message<isRequest, Body, Fields>::
+chunked(bool value)
+{
+ this->set_chunked_impl(value);
+ this->set_content_length_impl(boost::none);
+}
+
+template<bool isRequest, class Body, class Fields>
+void
+message<isRequest, Body, Fields>::
+content_length(
+ boost::optional<std::uint64_t> const& value)
+{
+ this->set_content_length_impl(value);
+ this->set_chunked_impl(false);
+}
+
+template<bool isRequest, class Body, class Fields>
+boost::optional<std::uint64_t>
+message<isRequest, Body, Fields>::
+payload_size() const
+{
+ return payload_size(detail::is_body_sized<Body>{});
+}
+
+template<bool isRequest, class Body, class Fields>
+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
diff --git a/boost/beast/http/impl/parser.ipp b/boost/beast/http/impl/parser.ipp
new file mode 100644
index 0000000000..99745da3f0
--- /dev/null
+++ b/boost/beast/http/impl/parser.ipp
@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_PARSER_IPP
+#define BOOST_BEAST_HTTP_IMPL_PARSER_IPP
+
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<bool isRequest, class Body, class Allocator>
+parser<isRequest, Body, Allocator>::
+parser()
+ : wr_(m_)
+{
+}
+
+template<bool isRequest, class Body, class Allocator>
+template<class Arg1, class... ArgN, class>
+parser<isRequest, Body, Allocator>::
+parser(Arg1&& arg1, ArgN&&... argn)
+ : m_(std::forward<Arg1>(arg1),
+ std::forward<ArgN>(argn)...)
+ , wr_(m_)
+{
+ 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)
+ : base_type(std::move(other))
+ , m_(other.release(), std::forward<Args>(args)...)
+ , wr_(m_)
+{
+ if(other.rd_inited_)
+ BOOST_THROW_EXCEPTION(std::invalid_argument{
+ "moved-from parser has a body"});
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/read.ipp b/boost/beast/http/impl/read.ipp
new file mode 100644
index 0000000000..5ecfae7d8b
--- /dev/null
+++ b/boost/beast/http/impl/read.ipp
@@ -0,0 +1,821 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_READ_IPP_HPP
+#define BOOST_BEAST_HTTP_IMPL_READ_IPP_HPP
+
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/parser.hpp>
+#include <boost/beast/http/read.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/read_size.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/assert.hpp>
+#include <boost/config.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+
+//------------------------------------------------------------------------------
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Derived, class Handler>
+class read_some_op
+{
+ int state_ = 0;
+ Stream& s_;
+ DynamicBuffer& b_;
+ basic_parser<isRequest, Derived>& p_;
+ boost::optional<typename
+ DynamicBuffer::mutable_buffers_type> mb_;
+ std::size_t bytes_transferred_ = 0;
+ Handler h_;
+
+public:
+ read_some_op(read_some_op&&) = default;
+ read_some_op(read_some_op const&) = default;
+
+ template<class DeducedHandler>
+ read_some_op(DeducedHandler&& h, Stream& s,
+ DynamicBuffer& b, basic_parser<isRequest, Derived>& p)
+ : s_(s)
+ , b_(b)
+ , p_(p)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(read_some_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->state_ >= 2 ? true :
+ asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Derived, class Handler>
+void
+read_some_op<Stream, DynamicBuffer,
+ isRequest, Derived, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred)
+{
+ switch(state_)
+ {
+ case 0:
+ state_ = 1;
+ if(b_.size() == 0)
+ goto do_read;
+ goto do_parse;
+
+ case 1:
+ state_ = 2;
+ case 2:
+ if(ec == boost::asio::error::eof)
+ {
+ BOOST_ASSERT(bytes_transferred == 0);
+ if(p_.got_some())
+ {
+ // caller sees EOF on next read
+ ec.assign(0, ec.category());
+ p_.put_eof(ec);
+ if(ec)
+ goto upcall;
+ BOOST_ASSERT(p_.is_done());
+ goto upcall;
+ }
+ ec = error::end_of_stream;
+ goto upcall;
+ }
+ if(ec)
+ goto upcall;
+ b_.commit(bytes_transferred);
+
+ do_parse:
+ {
+ auto const used = p_.put(b_.data(), ec);
+ bytes_transferred_ += used;
+ b_.consume(used);
+ if(! ec || ec != http::error::need_more)
+ goto do_upcall;
+ ec.assign(0, ec.category());
+ }
+
+ do_read:
+ try
+ {
+ mb_.emplace(b_.prepare(
+ read_size_or_throw(b_, 65536)));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ goto do_upcall;
+ }
+ return s_.async_read_some(*mb_, std::move(*this));
+
+ do_upcall:
+ if(state_ >= 2)
+ goto upcall;
+ state_ = 3;
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+
+ case 3:
+ break;
+ }
+upcall:
+ h_(ec, bytes_transferred_);
+}
+
+//------------------------------------------------------------------------------
+
+struct parser_is_done
+{
+ template<bool isRequest, class Derived>
+ bool
+ operator()(basic_parser<
+ isRequest, Derived> const& p) const
+ {
+ return p.is_done();
+ }
+};
+
+struct parser_is_header_done
+{
+ template<bool isRequest, class Derived>
+ bool
+ operator()(basic_parser<
+ isRequest, Derived> const& p) const
+ {
+ return p.is_header_done();
+ }
+};
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Derived, class Condition,
+ class Handler>
+class read_op
+{
+ int state_ = 0;
+ Stream& s_;
+ DynamicBuffer& b_;
+ basic_parser<isRequest, Derived>& p_;
+ std::size_t bytes_transferred_ = 0;
+ Handler h_;
+
+public:
+ read_op(read_op&&) = default;
+ read_op(read_op const&) = default;
+
+ template<class DeducedHandler>
+ read_op(DeducedHandler&& h, Stream& s,
+ DynamicBuffer& b, basic_parser<isRequest,
+ Derived>& p)
+ : s_(s)
+ , b_(b)
+ , p_(p)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(read_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->state_ >= 3 ? true :
+ asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Derived, class Condition,
+ class Handler>
+void
+read_op<Stream, DynamicBuffer,
+ isRequest, Derived, Condition, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred)
+{
+ switch(state_)
+ {
+ case 0:
+ if(Condition{}(p_))
+ {
+ state_ = 1;
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec));
+ }
+ state_ = 2;
+
+ do_read:
+ return async_read_some(
+ s_, b_, p_, std::move(*this));
+
+ case 1:
+ goto upcall;
+
+ case 2:
+ case 3:
+ if(ec)
+ goto upcall;
+ bytes_transferred_ += bytes_transferred;
+ if(Condition{}(p_))
+ goto upcall;
+ state_ = 3;
+ goto do_read;
+ }
+upcall:
+ h_(ec, bytes_transferred_);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Body, class Allocator,
+ class Handler>
+class read_msg_op
+{
+ using parser_type =
+ parser<isRequest, Body, Allocator>;
+
+ using message_type =
+ typename parser_type::value_type;
+
+ struct data
+ {
+ int state = 0;
+ Stream& s;
+ DynamicBuffer& b;
+ message_type& m;
+ parser_type p;
+ std::size_t bytes_transferred = 0;
+
+ data(Handler&, Stream& s_,
+ DynamicBuffer& b_, message_type& m_)
+ : s(s_)
+ , b(b_)
+ , m(m_)
+ , p(std::move(m))
+ {
+ p.eager(true);
+ }
+ };
+
+ handler_ptr<data, Handler> d_;
+
+public:
+ read_msg_op(read_msg_op&&) = default;
+ read_msg_op(read_msg_op const&) = default;
+
+ template<class DeducedHandler, class... Args>
+ read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
+ : d_(std::forward<DeducedHandler>(h),
+ s, std::forward<Args>(args)...)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->s.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(read_msg_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->d_->state >= 2 ? true :
+ asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class Stream, class DynamicBuffer,
+ bool isRequest, class Body, class Allocator,
+ class Handler>
+void
+read_msg_op<Stream, DynamicBuffer,
+ isRequest, Body, Allocator, Handler>::
+operator()(
+ error_code ec,
+ std::size_t bytes_transferred)
+{
+ auto& d = *d_;
+ switch(d.state)
+ {
+ case 0:
+ d.state = 1;
+
+ do_read:
+ return async_read_some(
+ d.s, d.b, d.p, std::move(*this));
+
+ case 1:
+ case 2:
+ if(ec)
+ goto upcall;
+ d.bytes_transferred +=
+ bytes_transferred;
+ if(d.p.is_done())
+ {
+ d.m = d.p.release();
+ goto upcall;
+ }
+ d.state = 2;
+ goto do_read;
+ }
+upcall:
+ bytes_transferred = d.bytes_transferred;
+ d_.invoke(ec, bytes_transferred);
+}
+
+} // detail
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_some(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ BOOST_ASSERT(! parser.is_done());
+ error_code ec;
+ auto const bytes_transferred =
+ read_some(stream, buffer, parser, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_some(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ BOOST_ASSERT(! parser.is_done());
+ std::size_t bytes_transferred = 0;
+ if(buffer.size() == 0)
+ goto do_read;
+ for(;;)
+ {
+ // invoke parser
+ {
+ auto const n = parser.put(buffer.data(), ec);
+ bytes_transferred += n;
+ buffer.consume(n);
+ if(! ec)
+ break;
+ if(ec != http::error::need_more)
+ break;
+ }
+ do_read:
+ boost::optional<typename
+ DynamicBuffer::mutable_buffers_type> b;
+ try
+ {
+ b.emplace(buffer.prepare(
+ read_size_or_throw(buffer, 65536)));
+ }
+ catch(std::length_error const&)
+ {
+ ec = error::buffer_overflow;
+ return bytes_transferred;
+ }
+ auto const n = stream.read_some(*b, ec);
+ if(ec == boost::asio::error::eof)
+ {
+ BOOST_ASSERT(n == 0);
+ if(parser.got_some())
+ {
+ // caller sees EOF on next read
+ parser.put_eof(ec);
+ if(ec)
+ break;
+ BOOST_ASSERT(parser.is_done());
+ break;
+ }
+ ec = error::end_of_stream;
+ break;
+ }
+ if(ec)
+ break;
+ buffer.commit(n);
+ }
+ return bytes_transferred;
+}
+
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read_some(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ BOOST_ASSERT(! parser.is_done());
+ boost::asio::async_completion<ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::read_some_op<AsyncReadStream,
+ DynamicBuffer, isRequest, Derived, BOOST_ASIO_HANDLER_TYPE(
+ ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, stream, buffer, parser}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_header(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ read_header(stream, buffer, parser, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read_header(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ parser.eager(false);
+ if(parser.is_header_done())
+ {
+ ec.assign(0, ec.category());
+ return 0;
+ }
+ std::size_t bytes_transferred = 0;
+ do
+ {
+ bytes_transferred += read_some(
+ stream, buffer, parser, ec);
+ if(ec)
+ return bytes_transferred;
+ }
+ while(! parser.is_header_done());
+ return bytes_transferred;
+}
+
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read_header(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ parser.eager(false);
+ boost::asio::async_completion<ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::read_op<AsyncReadStream, DynamicBuffer,
+ isRequest, Derived, detail::parser_is_header_done,
+ BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, stream, buffer, parser}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ read(stream, buffer, parser, ec);
+ if(ec)
+ BOOST_THROW_EXCEPTION(system_error{ec});
+ return bytes_transferred;
+}
+
+template<
+ class SyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived>
+std::size_t
+read(
+ SyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ error_code& ec)
+{
+ static_assert(is_sync_read_stream<SyncReadStream>::value,
+ "SyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ parser.eager(true);
+ if(parser.is_done())
+ {
+ ec.assign(0, ec.category());
+ return 0;
+ }
+ std::size_t bytes_transferred = 0;
+ do
+ {
+ bytes_transferred += read_some(
+ stream, buffer, parser, ec);
+ if(ec)
+ return bytes_transferred;
+ }
+ while(! parser.is_done());
+ return bytes_transferred;
+}
+
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Derived,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ basic_parser<isRequest, Derived>& parser,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ parser.eager(true);
+ boost::asio::async_completion<
+ ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::read_op<AsyncReadStream, DynamicBuffer,
+ isRequest, Derived, detail::parser_is_done,
+ BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, stream, buffer, parser}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+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 requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_reader<Body>::value,
+ "BodyReader requirements not met");
+ error_code ec;
+ auto const bytes_transferred =
+ 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 requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_reader<Body>::value,
+ "BodyReader requirements not met");
+ parser<isRequest, Body, Allocator> p{std::move(msg)};
+ p.eager(true);
+ auto const bytes_transferred =
+ read(stream, buffer, p.base(), ec);
+ if(ec)
+ return bytes_transferred;
+ msg = p.release();
+ return bytes_transferred;
+}
+
+template<
+ class AsyncReadStream,
+ class DynamicBuffer,
+ bool isRequest, class Body, class Allocator,
+ class ReadHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ ReadHandler, void(error_code, std::size_t))
+async_read(
+ AsyncReadStream& stream,
+ DynamicBuffer& buffer,
+ message<isRequest, Body, basic_fields<Allocator>>& msg,
+ ReadHandler&& handler)
+{
+ static_assert(is_async_read_stream<AsyncReadStream>::value,
+ "AsyncReadStream requirements not met");
+ static_assert(
+ boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
+ "DynamicBuffer requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_reader<Body>::value,
+ "BodyReader requirements not met");
+ boost::asio::async_completion<
+ ReadHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::read_msg_op<
+ AsyncReadStream,
+ DynamicBuffer,
+ isRequest, Body, Allocator,
+ BOOST_ASIO_HANDLER_TYPE(
+ ReadHandler, void(error_code, std::size_t))>{
+ init.completion_handler, stream, buffer, msg}();
+ return init.result.get();
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/rfc7230.ipp b/boost/beast/http/impl/rfc7230.ipp
new file mode 100644
index 0000000000..96ec902ceb
--- /dev/null
+++ b/boost/beast/http/impl/rfc7230.ipp
@@ -0,0 +1,572 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
+#define BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
+
+#include <boost/beast/http/detail/rfc7230.hpp>
+#include <iterator>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+class param_list::const_iterator
+{
+ using iter_type = string_view::const_iterator;
+
+ std::string s_;
+ detail::param_iter pi_;
+
+public:
+ using value_type = param_list::value_type;
+ using pointer = value_type const*;
+ using reference = value_type const&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::input_iterator_tag;
+
+ const_iterator() = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return
+ other.pi_.it == pi_.it &&
+ other.pi_.last == pi_.last &&
+ other.pi_.first == pi_.first;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return pi_.v;
+ }
+
+ pointer
+ operator->() const
+ {
+ return &*(*this);
+ }
+
+ const_iterator&
+ operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+private:
+ friend class param_list;
+
+ const_iterator(iter_type first, iter_type last)
+ {
+ pi_.it = first;
+ pi_.first = first;
+ pi_.last = last;
+ increment();
+ }
+
+ template<class = void>
+ static
+ std::string
+ unquote(string_view sr);
+
+ template<class = void>
+ void
+ increment();
+};
+
+inline
+auto
+param_list::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+param_list::
+end() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+inline
+auto
+param_list::
+cbegin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+param_list::
+cend() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+template<class>
+std::string
+param_list::const_iterator::
+unquote(string_view sr)
+{
+ std::string s;
+ s.reserve(sr.size());
+ auto it = sr.begin() + 1;
+ auto end = sr.end() - 1;
+ while(it != end)
+ {
+ if(*it == '\\')
+ ++it;
+ s.push_back(*it);
+ ++it;
+ }
+ return s;
+}
+
+template<class>
+void
+param_list::const_iterator::
+increment()
+{
+ s_.clear();
+ pi_.increment();
+ if(pi_.empty())
+ {
+ pi_.it = pi_.last;
+ pi_.first = pi_.last;
+ }
+ else if(! pi_.v.second.empty() &&
+ pi_.v.second.front() == '"')
+ {
+ s_ = unquote(pi_.v.second);
+ pi_.v.second = string_view{
+ s_.data(), s_.size()};
+ }
+}
+
+//------------------------------------------------------------------------------
+
+class ext_list::const_iterator
+{
+ ext_list::value_type v_;
+ iter_type it_;
+ iter_type first_;
+ iter_type last_;
+
+public:
+ using value_type = ext_list::value_type;
+ using pointer = value_type const*;
+ using reference = value_type const&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::forward_iterator_tag;
+
+ const_iterator() = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return
+ other.it_ == it_ &&
+ other.first_ == first_ &&
+ other.last_ == last_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return v_;
+ }
+
+ pointer
+ operator->() const
+ {
+ return &*(*this);
+ }
+
+ const_iterator&
+ operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+private:
+ friend class ext_list;
+
+ const_iterator(iter_type begin, iter_type end)
+ {
+ it_ = begin;
+ first_ = begin;
+ last_ = end;
+ increment();
+ }
+
+ template<class = void>
+ void
+ increment();
+};
+
+inline
+auto
+ext_list::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+ext_list::
+end() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+inline
+auto
+ext_list::
+cbegin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+ext_list::
+cend() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+template<class T>
+auto
+ext_list::
+find(T const& s) ->
+ const_iterator
+{
+ return std::find_if(begin(), end(),
+ [&s](value_type const& v)
+ {
+ return iequals(s, v.first);
+ });
+}
+
+template<class T>
+bool
+ext_list::
+exists(T const& s)
+{
+ return find(s) != end();
+}
+
+template<class>
+void
+ext_list::const_iterator::
+increment()
+{
+ /*
+ ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
+ ext = token param-list
+ param-list = *( OWS ";" OWS param )
+ param = token OWS "=" OWS ( token / quoted-string )
+
+ chunked;a=b;i=j,gzip;windowBits=12
+ x,y
+ ,,,,,chameleon
+ */
+ auto const err =
+ [&]
+ {
+ it_ = last_;
+ first_ = last_;
+ };
+ auto need_comma = it_ != first_;
+ v_.first = {};
+ first_ = it_;
+ for(;;)
+ {
+ detail::skip_ows(it_, last_);
+ if(it_ == last_)
+ return err();
+ auto const c = *it_;
+ if(detail::is_token_char(c))
+ {
+ if(need_comma)
+ return err();
+ auto const p0 = it_;
+ for(;;)
+ {
+ ++it_;
+ if(it_ == last_)
+ break;
+ if(! detail::is_token_char(*it_))
+ break;
+ }
+ v_.first = string_view{&*p0,
+ static_cast<std::size_t>(it_ - p0)};
+ detail::param_iter pi;
+ pi.it = it_;
+ pi.first = it_;
+ pi.last = last_;
+ for(;;)
+ {
+ pi.increment();
+ if(pi.empty())
+ break;
+ }
+ v_.second = param_list{string_view{&*it_,
+ static_cast<std::size_t>(pi.it - it_)}};
+ it_ = pi.it;
+ return;
+ }
+ if(c != ',')
+ return err();
+ need_comma = false;
+ ++it_;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+class token_list::const_iterator
+{
+ token_list::value_type v_;
+ iter_type it_;
+ iter_type first_;
+ iter_type last_;
+
+public:
+ using value_type = token_list::value_type;
+ using pointer = value_type const*;
+ using reference = value_type const&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::forward_iterator_tag;
+
+ const_iterator() = default;
+
+ bool
+ operator==(const_iterator const& other) const
+ {
+ return
+ other.it_ == it_ &&
+ other.first_ == first_ &&
+ other.last_ == last_;
+ }
+
+ bool
+ operator!=(const_iterator const& other) const
+ {
+ return !(*this == other);
+ }
+
+ reference
+ operator*() const
+ {
+ return v_;
+ }
+
+ pointer
+ operator->() const
+ {
+ return &*(*this);
+ }
+
+ const_iterator&
+ operator++()
+ {
+ increment();
+ return *this;
+ }
+
+ const_iterator
+ operator++(int)
+ {
+ auto temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+private:
+ friend class token_list;
+
+ const_iterator(iter_type begin, iter_type end)
+ {
+ it_ = begin;
+ first_ = begin;
+ last_ = end;
+ increment();
+ }
+
+ template<class = void>
+ void
+ increment();
+};
+
+inline
+auto
+token_list::
+begin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+token_list::
+end() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+inline
+auto
+token_list::
+cbegin() const ->
+ const_iterator
+{
+ return const_iterator{s_.begin(), s_.end()};
+}
+
+inline
+auto
+token_list::
+cend() const ->
+ const_iterator
+{
+ return const_iterator{s_.end(), s_.end()};
+}
+
+template<class>
+void
+token_list::const_iterator::
+increment()
+{
+ /*
+ token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
+ */
+ auto const err =
+ [&]
+ {
+ it_ = last_;
+ first_ = last_;
+ };
+ auto need_comma = it_ != first_;
+ v_ = {};
+ first_ = it_;
+ for(;;)
+ {
+ detail::skip_ows(it_, last_);
+ if(it_ == last_)
+ return err();
+ auto const c = *it_;
+ if(detail::is_token_char(c))
+ {
+ if(need_comma)
+ return err();
+ auto const p0 = it_;
+ for(;;)
+ {
+ ++it_;
+ if(it_ == last_)
+ break;
+ if(! detail::is_token_char(*it_))
+ break;
+ }
+ v_ = string_view{&*p0,
+ static_cast<std::size_t>(it_ - p0)};
+ return;
+ }
+ if(c != ',')
+ return err();
+ need_comma = false;
+ ++it_;
+ }
+}
+
+template<class T>
+bool
+token_list::
+exists(T const& s)
+{
+ return std::find_if(begin(), end(),
+ [&s](value_type const& v)
+ {
+ return iequals(s, v);
+ }
+ ) != end();
+}
+
+template<class Policy>
+bool
+validate_list(detail::basic_parsed_list<
+ Policy> const& list)
+{
+ auto const last = list.end();
+ auto it = list.begin();
+ if(it.error())
+ return false;
+ while(it != last)
+ {
+ ++it;
+ if(it.error())
+ return false;
+ if(it == last)
+ break;
+ }
+ return true;
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
+
diff --git a/boost/beast/http/impl/serializer.ipp b/boost/beast/http/impl/serializer.ipp
new file mode 100644
index 0000000000..11cf857845
--- /dev/null
+++ b/boost/beast/http/impl/serializer.ipp
@@ -0,0 +1,430 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP
+#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP
+
+#include <boost/beast/core/detail/buffers_ref.hpp>
+#include <boost/beast/http/error.hpp>
+#include <boost/beast/http/status.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/assert.hpp>
+#include <ostream>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+template<
+ bool isRequest, class Body, class Fields>
+void
+serializer<isRequest, Body, Fields>::
+frdinit(std::true_type)
+{
+ frd_.emplace(m_, m_.version(), m_.method());
+}
+
+template<
+ bool isRequest, class Body, class Fields>
+void
+serializer<isRequest, Body, Fields>::
+frdinit(std::false_type)
+{
+ frd_.emplace(m_, m_.version(), m_.result_int());
+}
+
+template<
+ bool isRequest, class Body, class Fields>
+template<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)
+ , rd_(m_)
+{
+}
+
+template<
+ bool isRequest, class Body, class Fields>
+template<class Visit>
+void
+serializer<isRequest, Body, Fields>::
+next(error_code& ec, Visit&& visit)
+{
+ using boost::asio::buffer_size;
+ switch(s_)
+ {
+ case do_construct:
+ {
+ frdinit(std::integral_constant<bool,
+ isRequest>{});
+ if(m_.chunked())
+ goto go_init_c;
+ s_ = do_init;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_init:
+ {
+ rd_.init(ec);
+ if(ec)
+ return;
+ if(split_)
+ goto go_header_only;
+ auto result = rd_.get(ec);
+ if(ec == error::need_more)
+ goto go_header_only;
+ if(ec)
+ return;
+ if(! result)
+ goto go_header_only;
+ more_ = result->second;
+ v_.template emplace<2>(
+ boost::in_place_init,
+ frd_->get(),
+ result->first);
+ s_ = do_header;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_header:
+ do_visit<2>(ec, visit);
+ break;
+
+ go_header_only:
+ v_.template emplace<1>(frd_->get());
+ s_ = do_header_only;
+ BOOST_BEAST_FALLTHROUGH;
+ case do_header_only:
+ do_visit<1>(ec, visit);
+ break;
+
+ case do_body:
+ s_ = do_body + 1;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case do_body + 1:
+ {
+ auto result = rd_.get(ec);
+ if(ec)
+ return;
+ if(! result)
+ goto go_complete;
+ more_ = result->second;
+ v_.template emplace<3>(result->first);
+ s_ = do_body + 2;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_body + 2:
+ do_visit<3>(ec, visit);
+ break;
+
+ //----------------------------------------------------------------------
+
+ go_init_c:
+ s_ = do_init_c;
+ BOOST_BEAST_FALLTHROUGH;
+ case do_init_c:
+ {
+ rd_.init(ec);
+ if(ec)
+ return;
+ if(split_)
+ goto go_header_only_c;
+ auto result = rd_.get(ec);
+ if(ec == error::need_more)
+ goto go_header_only_c;
+ if(ec)
+ return;
+ if(! result)
+ goto go_header_only_c;
+ more_ = result->second;
+ if(! more_)
+ {
+ // do it all in one buffer
+ v_.template emplace<7>(
+ boost::in_place_init,
+ frd_->get(),
+ buffer_size(result->first),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ result->first,
+ chunk_crlf{},
+ detail::chunk_last(),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{});
+ goto go_all_c;
+ }
+ v_.template emplace<4>(
+ boost::in_place_init,
+ frd_->get(),
+ buffer_size(result->first),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ result->first,
+ chunk_crlf{});
+ s_ = do_header_c;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_header_c:
+ do_visit<4>(ec, visit);
+ break;
+
+ go_header_only_c:
+ v_.template emplace<1>(frd_->get());
+ s_ = do_header_only_c;
+ case do_header_only_c:
+ do_visit<1>(ec, visit);
+ break;
+
+ case do_body_c:
+ s_ = do_body_c + 1;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case do_body_c + 1:
+ {
+ auto result = rd_.get(ec);
+ if(ec)
+ return;
+ if(! result)
+ goto go_final_c;
+ more_ = result->second;
+ if(! more_)
+ {
+ // do it all in one buffer
+ v_.template emplace<6>(
+ boost::in_place_init,
+ buffer_size(result->first),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ result->first,
+ chunk_crlf{},
+ detail::chunk_last(),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{});
+ goto go_body_final_c;
+ }
+ v_.template emplace<5>(
+ boost::in_place_init,
+ buffer_size(result->first),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{},
+ result->first,
+ chunk_crlf{});
+ s_ = do_body_c + 2;
+ BOOST_BEAST_FALLTHROUGH;
+ }
+
+ case do_body_c + 2:
+ do_visit<5>(ec, visit);
+ break;
+
+ go_body_final_c:
+ s_ = do_body_final_c;
+ BOOST_BEAST_FALLTHROUGH;
+ case do_body_final_c:
+ do_visit<6>(ec, visit);
+ break;
+
+ go_all_c:
+ s_ = do_all_c;
+ BOOST_BEAST_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(),
+ boost::asio::const_buffer{nullptr, 0},
+ chunk_crlf{});
+ s_ = do_final_c + 1;
+ BOOST_BEAST_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)
+{
+ using boost::asio::buffer_size;
+ switch(s_)
+ {
+ case do_header:
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<2>()));
+ v_.template get<2>().consume(n);
+ if(buffer_size(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_size(v_.template get<1>()));
+ v_.template get<1>().consume(n);
+ if(buffer_size(v_.template get<1>()) > 0)
+ break;
+ frd_ = boost::none;
+ header_done_ = true;
+ if(! split_)
+ goto go_complete;
+ s_ = do_body;
+ break;
+
+ case do_body + 2:
+ {
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<3>()));
+ v_.template get<3>().consume(n);
+ if(buffer_size(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_size(v_.template get<4>()));
+ v_.template get<4>().consume(n);
+ if(buffer_size(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_size(v_.template get<1>()));
+ v_.template get<1>().consume(n);
+ if(buffer_size(v_.template get<1>()) > 0)
+ break;
+ frd_ = boost::none;
+ header_done_ = true;
+ if(! split_)
+ {
+ s_ = do_final_c;
+ break;
+ }
+ s_ = do_body_c;
+ break;
+ }
+
+ case do_body_c + 2:
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<5>()));
+ v_.template get<5>().consume(n);
+ if(buffer_size(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_size(v_.template get<6>()));
+ v_.template get<6>().consume(n);
+ if(buffer_size(v_.template get<6>()) > 0)
+ break;
+ v_.reset();
+ s_ = do_complete;
+ break;
+ }
+
+ case do_all_c:
+ {
+ BOOST_ASSERT(
+ n <= buffer_size(v_.template get<7>()));
+ v_.template get<7>().consume(n);
+ if(buffer_size(v_.template get<7>()) > 0)
+ break;
+ header_done_ = true;
+ v_.reset();
+ s_ = do_complete;
+ break;
+ }
+
+ case do_final_c + 1:
+ BOOST_ASSERT(buffer_size(v_.template get<8>()));
+ v_.template get<8>().consume(n);
+ if(buffer_size(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
diff --git a/boost/beast/http/impl/status.ipp b/boost/beast/http/impl/status.ipp
new file mode 100644
index 0000000000..1cc8f34b63
--- /dev/null
+++ b/boost/beast/http/impl/status.ipp
@@ -0,0 +1,252 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_STATUS_IPP
+#define BOOST_BEAST_HTTP_IMPL_STATUS_IPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/throw_exception.hpp>
+
+namespace boost {
+namespace beast {
+namespace http {
+namespace detail {
+
+template<class = void>
+status
+int_to_status(unsigned v)
+{
+ switch(static_cast<status>(v))
+ {
+ // 1xx
+ case status::continue_:
+ case status::switching_protocols:
+ case status::processing:
+ BOOST_BEAST_FALLTHROUGH;
+
+ // 2xx
+ case status::ok:
+ case status::created:
+ case status::accepted:
+ case status::non_authoritative_information:
+ case status::no_content:
+ case status::reset_content:
+ case status::partial_content:
+ case status::multi_status:
+ case status::already_reported:
+ case status::im_used:
+ BOOST_BEAST_FALLTHROUGH;
+
+ // 3xx
+ case status::multiple_choices:
+ case status::moved_permanently:
+ case status::found:
+ case status::see_other:
+ case status::not_modified:
+ case status::use_proxy:
+ case status::temporary_redirect:
+ case status::permanent_redirect:
+ BOOST_BEAST_FALLTHROUGH;
+
+ // 4xx
+ case status::bad_request:
+ case status::unauthorized:
+ case status::payment_required:
+ case status::forbidden:
+ case status::not_found:
+ case status::method_not_allowed:
+ case status::not_acceptable:
+ case status::proxy_authentication_required:
+ case status::request_timeout:
+ case status::conflict:
+ case status::gone:
+ case status::length_required:
+ case status::precondition_failed:
+ case status::payload_too_large:
+ case status::uri_too_long:
+ case status::unsupported_media_type:
+ case status::range_not_satisfiable:
+ case status::expectation_failed:
+ case status::misdirected_request:
+ case status::unprocessable_entity:
+ case status::locked:
+ case status::failed_dependency:
+ case status::upgrade_required:
+ case status::precondition_required:
+ case status::too_many_requests:
+ case status::request_header_fields_too_large:
+ case status::connection_closed_without_response:
+ case status::unavailable_for_legal_reasons:
+ case status::client_closed_request:
+ BOOST_BEAST_FALLTHROUGH;
+
+ // 5xx
+ case status::internal_server_error:
+ case status::not_implemented:
+ case status::bad_gateway:
+ case status::service_unavailable:
+ case status::gateway_timeout:
+ case status::http_version_not_supported:
+ case status::variant_also_negotiates:
+ case status::insufficient_storage:
+ case status::loop_detected:
+ case status::not_extended:
+ case status::network_authentication_required:
+ case status::network_connect_timeout_error:
+ return static_cast<status>(v);
+
+ default:
+ break;
+ }
+ return status::unknown;
+}
+
+template<class = void>
+string_view
+status_to_string(unsigned v)
+{
+ switch(static_cast<status>(v))
+ {
+ // 1xx
+ case status::continue_: return "Continue";
+ case status::switching_protocols: return "Switching Protocols";
+ case status::processing: return "Processing";
+
+ // 2xx
+ case status::ok: return "OK";
+ case status::created: return "Created";
+ case status::accepted: return "Accepted";
+ case status::non_authoritative_information: return "Non-Authoritative Information";
+ case status::no_content: return "No Content";
+ case status::reset_content: return "Reset Content";
+ case status::partial_content: return "Partial Content";
+ case status::multi_status: return "Multi-Status";
+ case status::already_reported: return "Already Reported";
+ case status::im_used: return "IM Used";
+
+ // 3xx
+ case status::multiple_choices: return "Multiple Choices";
+ case status::moved_permanently: return "Moved Permanently";
+ case status::found: return "Found";
+ case status::see_other: return "See Other";
+ case status::not_modified: return "Not Modified";
+ case status::use_proxy: return "Use Proxy";
+ case status::temporary_redirect: return "Temporary Redirect";
+ case status::permanent_redirect: return "Permanent Redirect";
+
+ // 4xx
+ case status::bad_request: return "Bad Request";
+ case status::unauthorized: return "Unauthorized";
+ case status::payment_required: return "Payment Required";
+ case status::forbidden: return "Forbidden";
+ case status::not_found: return "Not Found";
+ case status::method_not_allowed: return "Method Not Allowed";
+ case status::not_acceptable: return "Not Acceptable";
+ case status::proxy_authentication_required: return "Proxy Authentication Required";
+ case status::request_timeout: return "Request Timeout";
+ case status::conflict: return "Conflict";
+ case status::gone: return "Gone";
+ case status::length_required: return "Length Required";
+ case status::precondition_failed: return "Precondition Failed";
+ case status::payload_too_large: return "Payload Too Large";
+ case status::uri_too_long: return "URI Too Long";
+ case status::unsupported_media_type: return "Unsupported Media Type";
+ case status::range_not_satisfiable: return "Range Not Satisfiable";
+ case status::expectation_failed: return "Expectation Failed";
+ case status::misdirected_request: return "Misdirected Request";
+ case status::unprocessable_entity: return "Unprocessable Entity";
+ case status::locked: return "Locked";
+ case status::failed_dependency: return "Failed Dependency";
+ case status::upgrade_required: return "Upgrade Required";
+ case status::precondition_required: return "Precondition Required";
+ case status::too_many_requests: return "Too Many Requests";
+ case status::request_header_fields_too_large: return "Request Header Fields Too Large";
+ case status::connection_closed_without_response: return "Connection Closed Without Response";
+ case status::unavailable_for_legal_reasons: return "Unavailable For Legal Reasons";
+ case status::client_closed_request: return "Client Closed Request";
+ // 5xx
+ case status::internal_server_error: return "Internal Server Error";
+ case status::not_implemented: return "Not Implemented";
+ case status::bad_gateway: return "Bad Gateway";
+ case status::service_unavailable: return "Service Unavailable";
+ case status::gateway_timeout: return "Gateway Timeout";
+ case status::http_version_not_supported: return "HTTP Version Not Supported";
+ case status::variant_also_negotiates: return "Variant Also Negotiates";
+ case status::insufficient_storage: return "Insufficient Storage";
+ case status::loop_detected: return "Loop Detected";
+ case status::not_extended: return "Not Extended";
+ case status::network_authentication_required: return "Network Authentication Required";
+ case status::network_connect_timeout_error: return "Network Connect Timeout Error";
+
+ default:
+ break;
+ }
+ return "<unknown-status>";
+}
+
+template<class = void>
+status_class
+to_status_class(unsigned v)
+{
+ switch(v / 100)
+ {
+ case 1: return status_class::informational;
+ case 2: return status_class::successful;
+ case 3: return status_class::redirection;
+ case 4: return status_class::client_error;
+ case 5: return status_class::server_error;
+ default:
+ break;
+ }
+ return status_class::unknown;
+}
+
+} // detail
+
+inline
+status
+int_to_status(unsigned v)
+{
+ return detail::int_to_status(v);
+}
+
+inline
+status_class
+to_status_class(unsigned v)
+{
+ return detail::to_status_class(v);
+}
+
+inline
+status_class
+to_status_class(status v)
+{
+ return to_status_class(static_cast<int>(v));
+}
+
+inline
+string_view
+obsolete_reason(status v)
+{
+ return detail::status_to_string(
+ static_cast<unsigned>(v));
+}
+
+inline
+std::ostream&
+operator<<(std::ostream& os, status v)
+{
+ return os << obsolete_reason(v);
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/verb.ipp b/boost/beast/http/impl/verb.ipp
new file mode 100644
index 0000000000..820609165b
--- /dev/null
+++ b/boost/beast/http/impl/verb.ipp
@@ -0,0 +1,337 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_VERB_IPP
+#define BOOST_BEAST_HTTP_IMPL_VERB_IPP
+
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/throw_exception.hpp>
+#include <stdexcept>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+
+template<class = void>
+inline
+string_view
+verb_to_string(verb v)
+{
+ switch(v)
+ {
+ case verb::delete_: return "DELETE";
+ case verb::get: return "GET";
+ case verb::head: return "HEAD";
+ case verb::post: return "POST";
+ case verb::put: return "PUT";
+ case verb::connect: return "CONNECT";
+ case verb::options: return "OPTIONS";
+ case verb::trace: return "TRACE";
+
+ case verb::copy: return "COPY";
+ case verb::lock: return "LOCK";
+ case verb::mkcol: return "MKCOL";
+ case verb::move: return "MOVE";
+ case verb::propfind: return "PROPFIND";
+ case verb::proppatch: return "PROPPATCH";
+ case verb::search: return "SEARCH";
+ case verb::unlock: return "UNLOCK";
+ case verb::bind: return "BIND";
+ case verb::rebind: return "REBIND";
+ case verb::unbind: return "UNBIND";
+ case verb::acl: return "ACL";
+
+ case verb::report: return "REPORT";
+ case verb::mkactivity: return "MKACTIVITY";
+ case verb::checkout: return "CHECKOUT";
+ case verb::merge: return "MERGE";
+
+ case verb::msearch: return "M-SEARCH";
+ case verb::notify: return "NOTIFY";
+ case verb::subscribe: return "SUBSCRIBE";
+ case verb::unsubscribe: return "UNSUBSCRIBE";
+
+ case verb::patch: return "PATCH";
+ case verb::purge: return "PURGE";
+
+ case verb::mkcalendar: return "MKCALENDAR";
+
+ case verb::link: return "LINK";
+ case verb::unlink: return "UNLINK";
+
+ case verb::unknown:
+ return "<unknown>";
+ }
+
+ BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"});
+}
+
+template<class = void>
+verb
+string_to_verb(string_view v)
+{
+/*
+ ACL
+ BIND
+ CHECKOUT
+ CONNECT
+ COPY
+ DELETE
+ GET
+ HEAD
+ LINK
+ LOCK
+ M-SEARCH
+ MERGE
+ MKACTIVITY
+ MKCALENDAR
+ MKCOL
+ MOVE
+ NOTIFY
+ OPTIONS
+ PATCH
+ POST
+ PROPFIND
+ PROPPATCH
+ PURGE
+ PUT
+ REBIND
+ REPORT
+ SEARCH
+ SUBSCRIBE
+ TRACE
+ UNBIND
+ UNLINK
+ UNLOCK
+ UNSUBSCRIBE
+*/
+ if(v.size() < 3)
+ return verb::unknown;
+ // s must be null terminated
+ auto const eq =
+ [](string_view sv, char const* s)
+ {
+ auto p = sv.data();
+ for(;;)
+ {
+ if(*s != *p)
+ return false;
+ ++s;
+ ++p;
+ if(! *s)
+ return p == sv.end();
+ }
+ };
+ auto c = v[0];
+ v.remove_prefix(1);
+ switch(c)
+ {
+ case 'A':
+ if(v == "CL")
+ return verb::acl;
+ break;
+
+ case 'B':
+ if(v == "IND")
+ return verb::bind;
+ break;
+
+ case 'C':
+ c = v[0];
+ v.remove_prefix(1);
+ switch(c)
+ {
+ case 'H':
+ if(eq(v, "ECKOUT"))
+ return verb::checkout;
+ break;
+
+ case 'O':
+ if(eq(v, "NNECT"))
+ return verb::connect;
+ if(eq(v, "PY"))
+ return verb::copy;
+ BOOST_BEAST_FALLTHROUGH;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'D':
+ if(eq(v, "ELETE"))
+ return verb::delete_;
+ break;
+
+ case 'G':
+ if(eq(v, "ET"))
+ return verb::get;
+ break;
+
+ case 'H':
+ if(eq(v, "EAD"))
+ return verb::head;
+ break;
+
+ case 'L':
+ if(eq(v, "INK"))
+ return verb::link;
+ if(eq(v, "OCK"))
+ return verb::lock;
+ break;
+
+ case 'M':
+ c = v[0];
+ v.remove_prefix(1);
+ switch(c)
+ {
+ case '-':
+ if(eq(v, "SEARCH"))
+ return verb::msearch;
+ break;
+
+ case 'E':
+ if(eq(v, "RGE"))
+ return verb::merge;
+ break;
+
+ case 'K':
+ if(eq(v, "ACTIVITY"))
+ return verb::mkactivity;
+ if(v[0] == 'C')
+ {
+ v.remove_prefix(1);
+ if(eq(v, "ALENDAR"))
+ return verb::mkcalendar;
+ if(eq(v, "OL"))
+ return verb::mkcol;
+ break;
+ }
+ break;
+
+ case 'O':
+ if(eq(v, "VE"))
+ return verb::move;
+ BOOST_BEAST_FALLTHROUGH;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'N':
+ if(eq(v, "OTIFY"))
+ return verb::notify;
+ break;
+
+ case 'O':
+ if(eq(v, "PTIONS"))
+ return verb::options;
+ break;
+
+ case 'P':
+ c = v[0];
+ v.remove_prefix(1);
+ switch(c)
+ {
+ case 'A':
+ if(eq(v, "TCH"))
+ return verb::patch;
+ break;
+
+ case 'O':
+ if(eq(v, "ST"))
+ return verb::post;
+ break;
+
+ case 'R':
+ if(eq(v, "OPFIND"))
+ return verb::propfind;
+ if(eq(v, "OPPATCH"))
+ return verb::proppatch;
+ break;
+
+ case 'U':
+ if(eq(v, "RGE"))
+ return verb::purge;
+ if(eq(v, "T"))
+ return verb::put;
+ BOOST_BEAST_FALLTHROUGH;
+
+ default:
+ break;
+ }
+ break;
+
+ case 'R':
+ if(v[0] != 'E')
+ break;
+ v.remove_prefix(1);
+ if(eq(v, "BIND"))
+ return verb::rebind;
+ if(eq(v, "PORT"))
+ return verb::report;
+ break;
+
+ case 'S':
+ if(eq(v, "EARCH"))
+ return verb::search;
+ if(eq(v, "UBSCRIBE"))
+ return verb::subscribe;
+ break;
+
+ case 'T':
+ if(eq(v, "RACE"))
+ return verb::trace;
+ break;
+
+ case 'U':
+ if(v[0] != 'N')
+ break;
+ v.remove_prefix(1);
+ if(eq(v, "BIND"))
+ return verb::unbind;
+ if(eq(v, "LINK"))
+ return verb::unlink;
+ if(eq(v, "LOCK"))
+ return verb::unlock;
+ if(eq(v, "SUBSCRIBE"))
+ return verb::unsubscribe;
+ break;
+
+ default:
+ break;
+ }
+
+ return verb::unknown;
+}
+
+} // detail
+
+inline
+string_view
+to_string(verb v)
+{
+ return detail::verb_to_string(v);
+}
+
+inline
+verb
+string_to_verb(string_view s)
+{
+ return detail::string_to_verb(s);
+}
+
+} // http
+} // beast
+} // boost
+
+#endif
diff --git a/boost/beast/http/impl/write.ipp b/boost/beast/http/impl/write.ipp
new file mode 100644
index 0000000000..9d5be277ec
--- /dev/null
+++ b/boost/beast/http/impl/write.ipp
@@ -0,0 +1,887 @@
+//
+// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/boostorg/beast
+//
+
+#ifndef BOOST_BEAST_HTTP_IMPL_WRITE_IPP
+#define BOOST_BEAST_HTTP_IMPL_WRITE_IPP
+
+#include <boost/beast/http/type_traits.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/ostream.hpp>
+#include <boost/beast/core/handler_ptr.hpp>
+#include <boost/beast/core/type_traits.hpp>
+#include <boost/beast/core/detail/config.hpp>
+#include <boost/asio/associated_allocator.hpp>
+#include <boost/asio/associated_executor.hpp>
+#include <boost/asio/handler_continuation_hook.hpp>
+#include <boost/asio/post.hpp>
+#include <boost/asio/write.hpp>
+#include <boost/optional.hpp>
+#include <boost/throw_exception.hpp>
+#include <ostream>
+#include <sstream>
+
+namespace boost {
+namespace beast {
+namespace http {
+namespace detail {
+
+template<
+ class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+class write_some_op
+{
+ Stream& s_;
+ serializer<isRequest,Body, Fields>& sr_;
+ Handler h_;
+
+ class lambda
+ {
+ write_some_op& op_;
+
+ public:
+ bool invoked = false;
+
+ explicit
+ lambda(write_some_op& op)
+ : op_(op)
+ {
+ }
+
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffers)
+ {
+ invoked = true;
+ ec.assign(0, ec.category());
+ return op_.s_.async_write_some(
+ buffers, std::move(op_));
+ }
+ };
+
+public:
+ write_some_op(write_some_op&&) = default;
+ write_some_op(write_some_op const&) = default;
+
+ template<class DeducedHandler>
+ write_some_op(DeducedHandler&& h, Stream& s,
+ serializer<isRequest, Body, Fields>& sr)
+ : s_(s)
+ , sr_(sr)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()();
+
+ void
+ operator()(
+ error_code ec,
+ std::size_t bytes_transferred);
+
+ friend
+ bool asio_handler_is_continuation(write_some_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<
+ class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+void
+write_some_op<
+ Stream, Handler, isRequest, Body, Fields>::
+operator()()
+{
+ error_code ec;
+ if(! sr_.is_done())
+ {
+ lambda f{*this};
+ sr_.next(ec, f);
+ if(ec)
+ {
+ BOOST_ASSERT(! f.invoked);
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+ }
+ if(f.invoked)
+ {
+ // *this has been moved from,
+ // cannot access members here.
+ return;
+ }
+ // What else could it be?
+ BOOST_ASSERT(sr_.is_done());
+ }
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+}
+
+template<
+ class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+void
+write_some_op<
+ Stream, Handler, isRequest, Body, Fields>::
+operator()(
+ error_code ec, std::size_t bytes_transferred)
+{
+ if(! ec)
+ sr_.consume(bytes_transferred);
+ h_(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 Stream, class Handler, class Predicate,
+ bool isRequest, class Body, class Fields>
+class write_op
+{
+ int state_ = 0;
+ Stream& s_;
+ serializer<isRequest, Body, Fields>& sr_;
+ std::size_t bytes_transferred_ = 0;
+ Handler h_;
+
+public:
+ write_op(write_op&&) = default;
+ write_op(write_op const&) = default;
+
+ template<class DeducedHandler>
+ write_op(DeducedHandler&& h, Stream& s,
+ serializer<isRequest, Body, Fields>& sr)
+ : s_(s)
+ , sr_(sr)
+ , h_(std::forward<DeducedHandler>(h))
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(h_);
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ h_, s_.get_executor());
+ }
+
+ void
+ operator()(
+ error_code ec = {},
+ std::size_t bytes_transferred = 0);
+
+ friend
+ bool asio_handler_is_continuation(write_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return op->state_ >= 3 ||
+ asio_handler_is_continuation(
+ std::addressof(op->h_));
+ }
+};
+
+template<
+ class Stream, class Handler, class Predicate,
+ bool isRequest, class Body, class Fields>
+void
+write_op<Stream, Handler, Predicate,
+ isRequest, Body, Fields>::
+operator()(
+ error_code ec, std::size_t bytes_transferred)
+{
+ if(ec)
+ goto upcall;
+ switch(state_)
+ {
+ case 0:
+ {
+ if(Predicate{}(sr_))
+ {
+ state_ = 1;
+ return boost::asio::post(
+ s_.get_executor(),
+ bind_handler(std::move(*this), ec, 0));
+ }
+ state_ = 2;
+ return beast::http::async_write_some(
+ s_, sr_, std::move(*this));
+ }
+
+ case 1:
+ goto upcall;
+
+ case 2:
+ state_ = 3;
+ BOOST_BEAST_FALLTHROUGH;
+
+ case 3:
+ {
+ bytes_transferred_ += bytes_transferred;
+ if(Predicate{}(sr_))
+ goto upcall;
+ return beast::http::async_write_some(
+ s_, sr_, std::move(*this));
+ }
+ }
+upcall:
+ h_(ec, bytes_transferred_);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+class write_msg_op
+{
+ struct data
+ {
+ Stream& s;
+ serializer<isRequest, Body, Fields> sr;
+
+ data(Handler&, Stream& s_, message<
+ isRequest, Body, Fields>& m_)
+ : s(s_)
+ , sr(m_)
+ {
+ }
+ };
+
+ handler_ptr<data, Handler> d_;
+
+public:
+ write_msg_op(write_msg_op&&) = default;
+ write_msg_op(write_msg_op const&) = default;
+
+ template<class DeducedHandler, class... Args>
+ write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
+ : d_(std::forward<DeducedHandler>(h),
+ s, std::forward<Args>(args)...)
+ {
+ }
+
+ using allocator_type =
+ boost::asio::associated_allocator_t<Handler>;
+
+ allocator_type
+ get_allocator() const noexcept
+ {
+ return boost::asio::get_associated_allocator(d_.handler());
+ }
+
+ using executor_type = boost::asio::associated_executor_t<
+ Handler, decltype(std::declval<Stream&>().get_executor())>;
+
+ executor_type
+ get_executor() const noexcept
+ {
+ return boost::asio::get_associated_executor(
+ d_.handler(), d_->s.get_executor());
+ }
+
+ void
+ operator()();
+
+ void
+ operator()(
+ error_code ec, std::size_t bytes_transferred);
+
+ friend
+ bool asio_handler_is_continuation(write_msg_op* op)
+ {
+ using boost::asio::asio_handler_is_continuation;
+ return asio_handler_is_continuation(
+ std::addressof(op->d_.handler()));
+ }
+};
+
+template<class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+void
+write_msg_op<
+ Stream, Handler, isRequest, Body, Fields>::
+operator()()
+{
+ auto& d = *d_;
+ return async_write(d.s, d.sr, std::move(*this));
+}
+
+template<class Stream, class Handler,
+ bool isRequest, class Body, class Fields>
+void
+write_msg_op<
+ Stream, Handler, isRequest, Body, Fields>::
+operator()(error_code ec, std::size_t bytes_transferred)
+{
+ d_.invoke(ec, bytes_transferred);
+}
+
+//------------------------------------------------------------------------------
+
+template<class Stream>
+class write_some_lambda
+{
+ Stream& stream_;
+
+public:
+ bool invoked = false;
+ std::size_t bytes_transferred = 0;
+
+ explicit
+ write_some_lambda(Stream& stream)
+ : stream_(stream)
+ {
+ }
+
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffers)
+ {
+ invoked = true;
+ bytes_transferred =
+ stream_.write_some(buffers, ec);
+ }
+};
+
+template<class Stream>
+class write_lambda
+{
+ Stream& stream_;
+
+public:
+ bool invoked = false;
+ std::size_t bytes_transferred = 0;
+
+ explicit
+ write_lambda(Stream& stream)
+ : stream_(stream)
+ {
+ }
+
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffers)
+ {
+ invoked = true;
+ bytes_transferred = boost::asio::write(
+ stream_, buffers, ec);
+ }
+};
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write_some(
+ SyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ error_code& ec)
+{
+ if(! sr.is_done())
+ {
+ write_some_lambda<SyncWriteStream> f{stream};
+ sr.next(ec, f);
+ if(ec)
+ return f.bytes_transferred;
+ if(f.invoked)
+ sr.consume(f.bytes_transferred);
+ return f.bytes_transferred;
+ }
+ ec.assign(0, ec.category());
+ return 0;
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_some(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler)
+{
+ boost::asio::async_completion<
+ WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::write_some_op<
+ AsyncWriteStream,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ isRequest, Body, Fields>{
+ init.completion_handler, stream, sr}();
+ return init.result.get();
+}
+
+} // 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 requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ error_code ec;
+ 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 requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ return detail::write_some(stream, sr, ec);
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_some(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler)
+{
+ static_assert(is_async_write_stream<
+ AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ return detail::async_write_some(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 requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ error_code ec;
+ 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 requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter 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.assign(0, ec.category());
+ }
+ return bytes_transferred;
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write_header(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler)
+{
+ static_assert(is_async_write_stream<
+ AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ sr.split(true);
+ boost::asio::async_completion<
+ WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::write_op<
+ AsyncWriteStream,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ detail::serializer_is_header_done,
+ isRequest, Body, Fields>{
+ init.completion_handler, stream, sr}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+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 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 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,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write(
+ AsyncWriteStream& stream,
+ serializer<isRequest, Body, Fields>& sr,
+ WriteHandler&& handler)
+{
+ static_assert(is_async_write_stream<
+ AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ sr.split(false);
+ boost::asio::async_completion<
+ WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::write_op<
+ AsyncWriteStream,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ detail::serializer_is_done,
+ isRequest, Body, Fields>{
+ init.completion_handler, stream, sr}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+template<
+ class SyncWriteStream,
+ bool isRequest, class Body, class Fields>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ message<isRequest, Body, Fields> const& msg)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter 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>
+std::size_t
+write(
+ SyncWriteStream& stream,
+ message<isRequest, Body, Fields> const& msg,
+ error_code& ec)
+{
+ static_assert(is_sync_write_stream<SyncWriteStream>::value,
+ "SyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ serializer<isRequest, Body, Fields> sr{msg};
+ return write(stream, sr, ec);
+}
+
+template<
+ class AsyncWriteStream,
+ bool isRequest, class Body, class Fields,
+ class WriteHandler>
+BOOST_ASIO_INITFN_RESULT_TYPE(
+ WriteHandler, void(error_code, std::size_t))
+async_write(
+ AsyncWriteStream& stream,
+ message<isRequest, Body, Fields>& msg,
+ WriteHandler&& handler)
+{
+ static_assert(
+ is_async_write_stream<AsyncWriteStream>::value,
+ "AsyncWriteStream requirements not met");
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter requirements not met");
+ boost::asio::async_completion<
+ WriteHandler,
+ void(error_code, std::size_t)> init{handler};
+ detail::write_msg_op<
+ AsyncWriteStream,
+ BOOST_ASIO_HANDLER_TYPE(WriteHandler,
+ void(error_code, std::size_t)),
+ isRequest, Body, Fields>{
+ init.completion_handler, stream, msg}();
+ return init.result.get();
+}
+
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+template<class Serializer>
+class write_ostream_lambda
+{
+ std::ostream& os_;
+ Serializer& sr_;
+
+public:
+ write_ostream_lambda(std::ostream& os,
+ Serializer& sr)
+ : os_(os)
+ , sr_(sr)
+ {
+ }
+
+ template<class ConstBufferSequence>
+ void
+ operator()(error_code& ec,
+ ConstBufferSequence const& buffers) const
+ {
+ ec.assign(0, ec.category());
+ if(os_.fail())
+ return;
+ std::size_t bytes_transferred = 0;
+ for(auto b : buffers_range(buffers))
+ {
+ os_.write(reinterpret_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 << buffers(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 << buffers(fr.get());
+}
+
+template<bool isRequest, class Body, class Fields>
+std::ostream&
+operator<<(std::ostream& os,
+ message<isRequest, Body, Fields> const& msg)
+{
+ static_assert(is_body<Body>::value,
+ "Body requirements not met");
+ static_assert(is_body_writer<Body>::value,
+ "BodyWriter 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