// // Copyright (c) 2013-2016 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) // #ifndef BEAST_HTTP_CHUNK_ENCODE_HPP #define BEAST_HTTP_CHUNK_ENCODE_HPP #include #include #include #include #include #include #include namespace beast { namespace http { namespace detail { template class chunk_encoded_buffers { private: using const_buffer = boost::asio::const_buffer; Buffers buffers_; const_buffer head_; const_buffer tail_; // Storage for the longest hex string we might need, plus delimiters. std::array data_; public: using value_type = boost::asio::const_buffer; class const_iterator; chunk_encoded_buffers() = delete; chunk_encoded_buffers (chunk_encoded_buffers const&) = default; chunk_encoded_buffers& operator= (chunk_encoded_buffers const&) = default; chunk_encoded_buffers (Buffers const& buffers, bool final_chunk); const_iterator begin() const { return const_iterator(*this, false); } const_iterator end() const { return const_iterator(*this, true); } private: // Unchecked conversion of unsigned to hex string template static std::enable_if_t::value, OutIter> to_hex(OutIter const first, OutIter const last, Unsigned n); }; template class chunk_encoded_buffers::const_iterator : public std::iterator { private: using iterator = typename Buffers::const_iterator; enum class Where { head, input, end }; chunk_encoded_buffers const* buffers_; Where where_; iterator iter_; public: const_iterator(); const_iterator (const_iterator const&) = default; const_iterator& operator= (const_iterator const&) = default; bool operator== (const_iterator const& other) const; bool operator!= (const_iterator const& other) const; const_iterator& operator++(); const_iterator& operator--(); const_iterator operator++(int) const; const_iterator operator--(int) const; const_buffer operator*() const; private: friend class chunk_encoded_buffers; const_iterator(chunk_encoded_buffers const& buffers, bool past_the_end); }; //------------------------------------------------------------------------------ template chunk_encoded_buffers::chunk_encoded_buffers ( Buffers const& buffers, bool final_chunk) : buffers_(buffers) { auto const size = boost::asio::buffer_size(buffers); data_[data_.size() - 2] = '\r'; data_[data_.size() - 1] = '\n'; auto pos = to_hex(data_.begin(), data_.end() - 2, size); head_ = const_buffer(&*pos, std::distance(pos, data_.end())); if (size > 0 && final_chunk) tail_ = const_buffer("\r\n0\r\n\r\n", 7); else tail_ = const_buffer("\r\n", 2); } template template std::enable_if_t::value, OutIter> chunk_encoded_buffers::to_hex( OutIter const first, OutIter const last, Unsigned n) { assert(first != last); OutIter iter = last; if(n == 0) { *--iter = '0'; return iter; } while(n) { assert(iter != first); *--iter = "0123456789abcdef"[n&0xf]; n>>=4; } return iter; } template chunk_encoded_buffers::const_iterator::const_iterator() : buffers_(nullptr) , where_(Where::end) { } template bool chunk_encoded_buffers::const_iterator::operator==( const_iterator const& other) const { return buffers_ == other.buffers_ && where_ == other.where_ && iter_ == other.iter_; } template bool chunk_encoded_buffers::const_iterator::operator!=( const_iterator const& other) const { return buffers_ != other.buffers_ || where_ != other.where_ || iter_ != other.iter_; } template auto chunk_encoded_buffers::const_iterator::operator++() -> const_iterator& { assert(buffers_); assert(where_ != Where::end); if (where_ == Where::head) where_ = Where::input; else if (iter_ != buffers_->buffers_.end()) ++iter_; else where_ = Where::end; return *this; } template auto chunk_encoded_buffers::const_iterator::operator--() -> const_iterator& { assert(buffers_); assert(where_ != Where::head); if (where_ == Where::end) where_ = Where::input; else if (iter_ != buffers_->buffers_.begin()) --iter_; else where_ = Where::head; return *this; } template auto chunk_encoded_buffers::const_iterator::operator++(int) const -> const_iterator { auto iter = *this; ++iter; return iter; } template auto chunk_encoded_buffers::const_iterator::operator--(int) const -> const_iterator { auto iter = *this; --iter; return iter; } template auto chunk_encoded_buffers::const_iterator::operator*() const -> const_buffer { assert(buffers_); assert(where_ != Where::end); if (where_ == Where::head) return buffers_->head_; if (iter_ != buffers_->buffers_.end()) return *iter_; return buffers_->tail_; } template chunk_encoded_buffers::const_iterator::const_iterator( chunk_encoded_buffers const& buffers, bool past_the_end) : buffers_(&buffers) , where_(past_the_end ? Where::end : Where::head) , iter_(past_the_end ? buffers_->buffers_.end() : buffers_->buffers_.begin()) { } } // detail /** Returns a chunk-encoded BufferSequence. See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 @param buffers The input buffer sequence. @param final_chunk `true` If this should include a final-chunk. @return A chunk-encoded ConstBufferSequence representing the input. */ template #if GENERATING_DOCS implementation_defined #else detail::chunk_encoded_buffers #endif chunk_encode(ConstBufferSequence const& buffers, bool final_chunk = false) { return detail::chunk_encoded_buffers< ConstBufferSequence>{buffers, final_chunk}; } /// Returns a chunked encoding final chunk. inline #if GENERATING_DOCS implementation_defined #else boost::asio::const_buffers_1 #endif chunk_encode_final() { return boost::asio::const_buffers_1( "0\r\n\r\n", 5); } } // http } // beast #endif