summaryrefslogtreecommitdiff
path: root/boost/beast/http/impl/chunk_encode.ipp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/beast/http/impl/chunk_encode.ipp')
-rw-r--r--boost/beast/http/impl/chunk_encode.ipp707
1 files changed, 707 insertions, 0 deletions
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