//------------------------------------------------------------------------------ /* This file is part of Beast: https://github.com/vinniefalco/Beast Copyright 2013, Vinnie Falco Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef BEAST_HTTP_CHUNK_ENCODE_H_INCLUDED #define BEAST_HTTP_CHUNK_ENCODE_H_INCLUDED #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() : where_(Where::end) , buffers_(nullptr) { } 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::begin); 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()) { } } /** Returns a chunk-encoded BufferSequence. See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 @tparam Buffers A type meeting the requirements of BufferSequence. @param buffers The input buffer sequence. @param final_chunk `true` If this should include a final-chunk. @return A chunk-encoded ConstBufferSeqeunce representing the input. */ /** @{ */ template detail::chunk_encoded_buffers chunk_encode (Buffers const& buffers, bool final_chunk = false) { return detail::chunk_encoded_buffers< Buffers>(buffers, final_chunk); } // Returns a chunked encoding final chunk. inline boost::asio::const_buffers_1 chunk_encode_final() { return boost::asio::const_buffers_1( "0\r\n\r\n", 5); } /** @} */ } // http } // beast #endif