diff --git a/CHANGELOG.md b/CHANGELOG.md index d6798571..e4795b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ HTTP +* Make chunk_encode public + WebSocket * Optimize utf8 validation diff --git a/doc/quickref.xml b/doc/quickref.xml index 8811e848..839000cd 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -59,6 +59,8 @@ async_parse async_read async_write + chunk_encode + chunk_encode_final is_keep_alive is_upgrade parse diff --git a/include/beast/http.hpp b/include/beast/http.hpp index 516dc69b..02cb4b0d 100644 --- a/include/beast/http.hpp +++ b/include/beast/http.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/include/beast/http/detail/chunk_encode.hpp b/include/beast/http/chunk_encode.hpp similarity index 67% rename from include/beast/http/detail/chunk_encode.hpp rename to include/beast/http/chunk_encode.hpp index 3c03dca1..51749316 100644 --- a/include/beast/http/detail/chunk_encode.hpp +++ b/include/beast/http/chunk_encode.hpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -20,7 +19,6 @@ namespace beast { namespace http { -namespace detail { class chunk_encode_text { @@ -29,6 +27,14 @@ class chunk_encode_text // Storage for the longest hex string we might need, plus delimiters. std::array buf_; + template + void + copy(chunk_encode_text const& other); + + template + void + setup(std::size_t n); + template static OutIter @@ -54,22 +60,13 @@ public: chunk_encode_text(chunk_encode_text const& other) { - auto const n = - boost::asio::buffer_size(other.cb_); - buf_ = other.buf_; - cb_ = boost::asio::const_buffer( - &buf_[buf_.size() - n], n); + copy(other); } explicit chunk_encode_text(std::size_t n) { - buf_[buf_.size() - 2] = '\r'; - buf_[buf_.size() - 1] = '\n'; - auto it = to_hex(buf_.end() - 2, n); - cb_ = boost::asio::const_buffer{&*it, - static_cast( - std::distance(it, buf_.end()))}; + setup(n); } const_iterator @@ -84,12 +81,38 @@ public: return begin() + 1; } }; +template +void +chunk_encode_text:: +copy(chunk_encode_text const& other) +{ + auto const n = + boost::asio::buffer_size(other.cb_); + buf_ = other.buf_; + cb_ = boost::asio::const_buffer( + &buf_[buf_.size() - n], n); +} + +template +void +chunk_encode_text:: +setup(std::size_t n) +{ + buf_[buf_.size() - 2] = '\r'; + buf_[buf_.size() - 1] = '\n'; + auto it = to_hex(buf_.end() - 2, n); + cb_ = boost::asio::const_buffer{&*it, + static_cast( + std::distance(it, buf_.end()))}; +} /** Returns a chunk-encoded ConstBufferSequence. This returns a buffer sequence representing the first chunk of a chunked transfer coded body. + @param fin `true` if this is the last chunk. + @param buffers The input buffer sequence. @return A chunk-encoded ConstBufferSequence representing the input. @@ -103,16 +126,20 @@ implementation_defined beast::detail::buffer_cat_helper #endif -chunk_encode(ConstBufferSequence const& buffers) +chunk_encode(bool fin, ConstBufferSequence const& buffers) { using boost::asio::buffer_size; return buffer_cat( chunk_encode_text{buffer_size(buffers)}, buffers, - boost::asio::const_buffers_1{"\r\n", 2}); + fin ? boost::asio::const_buffers_1{"\r\n0\r\n\r\n", 7} + : boost::asio::const_buffers_1{"\r\n", 2}); } -/// Returns a chunked encoding final chunk. +/** Returns a chunked encoding final chunk. + + @see rfc7230 section 4.1.3 +*/ inline #if GENERATING_DOCS implementation_defined @@ -121,11 +148,9 @@ boost::asio::const_buffers_1 #endif chunk_encode_final() { - return boost::asio::const_buffers_1( - "0\r\n\r\n", 5); + return boost::asio::const_buffers_1{"0\r\n\r\n", 5}; } -} // detail } // http } // beast diff --git a/include/beast/http/impl/write.ipp b/include/beast/http/impl/write.ipp index de443d7f..11e7bc4b 100644 --- a/include/beast/http/impl/write.ipp +++ b/include/beast/http/impl/write.ipp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -191,7 +191,7 @@ class write_op if(d.wp.chunked) boost::asio::async_write(d.s, buffer_cat(d.wp.sb.data(), - detail::chunk_encode(buffers)), + chunk_encode(false, buffers)), std::move(self_)); else boost::asio::async_write(d.s, @@ -218,7 +218,7 @@ class write_op // write body if(d.wp.chunked) boost::asio::async_write(d.s, - detail::chunk_encode(buffers), + chunk_encode(false, buffers), std::move(self_)); else boost::asio::async_write(d.s, @@ -243,7 +243,7 @@ public: d.resume = { [sp]() mutable { - write_op self(std::move(sp)); + write_op self{std::move(sp)}; self.d_->cont = false; auto& ios = self.d_->s.get_io_service(); ios.dispatch(bind_handler(std::move(self), @@ -383,7 +383,7 @@ operator()(error_code ec, std::size_t, bool again) // write final chunk d.state = 5; boost::asio::async_write(d.s, - detail::chunk_encode_final(), std::move(*this)); + chunk_encode_final(), std::move(*this)); return; case 5: @@ -425,7 +425,7 @@ public: // write headers and body if(chunked_) boost::asio::write(stream_, buffer_cat( - sb_.data(), detail::chunk_encode(buffers)), ec_); + sb_.data(), chunk_encode(false, buffers)), ec_); else boost::asio::write(stream_, buffer_cat( sb_.data(), buffers), ec_); @@ -454,7 +454,7 @@ public: // write body if(chunked_) boost::asio::write(stream_, - detail::chunk_encode(buffers), ec_); + chunk_encode(false, buffers), ec_); else boost::asio::write(stream_, buffers, ec_); } @@ -563,7 +563,7 @@ write(SyncWriteStream& stream, // final body chunk with the final chunk delimiter. // // write final chunk - boost::asio::write(stream, detail::chunk_encode_final(), ec); + boost::asio::write(stream, chunk_encode_final(), ec); if(ec) return; } diff --git a/test/http/chunk_encode.cpp b/test/http/chunk_encode.cpp index f44e3eca..cff31cbd 100644 --- a/test/http/chunk_encode.cpp +++ b/test/http/chunk_encode.cpp @@ -5,13 +5,14 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include +// Test that header file is self-contained. +#include + #include #include namespace beast { namespace http { -namespace detail { class chunk_encode_test : public beast::unit_test::suite { @@ -35,8 +36,8 @@ public: { using boost::asio::buffer; if(! fc.s.empty()) - s.append(to_string( - chunk_encode(buffer(fc.s.data(), fc.s.size())))); + s.append(to_string(chunk_encode( + false, buffer(fc.s.data(), fc.s.size())))); s.append(to_string(chunk_encode_final())); } @@ -45,8 +46,8 @@ public: encode1(std::string& s, std::string const& piece) { using boost::asio::buffer; - s.append(to_string( - chunk_encode(buffer(piece.data(), piece.size())))); + s.append(to_string(chunk_encode( + false, buffer(piece.data(), piece.size())))); } static @@ -122,11 +123,14 @@ public: "****\r\n" "0\r\n\r\n", "****", final_chunk{}); + + BEAST_EXPECT(to_string(chunk_encode(true, + boost::asio::buffer("****", 4))) == + "4\r\n****\r\n0\r\n\r\n"); } }; BEAST_DEFINE_TESTSUITE(chunk_encode,http,beast); -} // detail } // http } // beast