diff options
Diffstat (limited to 'boost/beast/http/impl/chunk_encode.ipp')
-rw-r--r-- | boost/beast/http/impl/chunk_encode.ipp | 707 |
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 |