Make chunk_encode public:

fix #154, fix #156

This adds public interfaces for transforming buffer
sequences into their chunk-encoded equivalents. The
transformations are O(1) in space and time.
This commit is contained in:
Vinnie Falco
2016-11-07 16:57:41 -05:00
parent 50bc9a58cd
commit f98ec17121
6 changed files with 68 additions and 34 deletions

View File

@@ -2,6 +2,8 @@
HTTP HTTP
* Make chunk_encode public
WebSocket WebSocket
* Optimize utf8 validation * Optimize utf8 validation

View File

@@ -59,6 +59,8 @@
<member><link linkend="beast.ref.http__async_parse">async_parse</link></member> <member><link linkend="beast.ref.http__async_parse">async_parse</link></member>
<member><link linkend="beast.ref.http__async_read">async_read</link></member> <member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_write">async_write</link></member> <member><link linkend="beast.ref.http__async_write">async_write</link></member>
<member><link linkend="beast.ref.http__chunk_encode">chunk_encode</link></member>
<member><link linkend="beast.ref.http__chunk_encode_final">chunk_encode_final</link></member>
<member><link linkend="beast.ref.http__is_keep_alive">is_keep_alive</link></member> <member><link linkend="beast.ref.http__is_keep_alive">is_keep_alive</link></member>
<member><link linkend="beast.ref.http__is_upgrade">is_upgrade</link></member> <member><link linkend="beast.ref.http__is_upgrade">is_upgrade</link></member>
<member><link linkend="beast.ref.http__parse">parse</link></member> <member><link linkend="beast.ref.http__parse">parse</link></member>

View File

@@ -11,6 +11,7 @@
#include <beast/http/basic_headers.hpp> #include <beast/http/basic_headers.hpp>
#include <beast/http/basic_parser_v1.hpp> #include <beast/http/basic_parser_v1.hpp>
#include <beast/http/body_type.hpp> #include <beast/http/body_type.hpp>
#include <beast/http/chunk_encode.hpp>
#include <beast/http/empty_body.hpp> #include <beast/http/empty_body.hpp>
#include <beast/http/headers.hpp> #include <beast/http/headers.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>

View File

@@ -11,7 +11,6 @@
#include <beast/core/buffer_cat.hpp> #include <beast/core/buffer_cat.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/logic/tribool.hpp>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cstddef> #include <cstddef>
@@ -20,7 +19,6 @@
namespace beast { namespace beast {
namespace http { namespace http {
namespace detail {
class chunk_encode_text class chunk_encode_text
{ {
@@ -29,6 +27,14 @@ class chunk_encode_text
// Storage for the longest hex string we might need, plus delimiters. // Storage for the longest hex string we might need, plus delimiters.
std::array<char, 2 * sizeof(std::size_t) + 2> buf_; std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
template<class = void>
void
copy(chunk_encode_text const& other);
template<class = void>
void
setup(std::size_t n);
template<class OutIter> template<class OutIter>
static static
OutIter OutIter
@@ -54,22 +60,13 @@ public:
chunk_encode_text(chunk_encode_text const& other) chunk_encode_text(chunk_encode_text const& other)
{ {
auto const n = copy(other);
boost::asio::buffer_size(other.cb_);
buf_ = other.buf_;
cb_ = boost::asio::const_buffer(
&buf_[buf_.size() - n], n);
} }
explicit explicit
chunk_encode_text(std::size_t n) chunk_encode_text(std::size_t n)
{ {
buf_[buf_.size() - 2] = '\r'; setup(n);
buf_[buf_.size() - 1] = '\n';
auto it = to_hex(buf_.end() - 2, n);
cb_ = boost::asio::const_buffer{&*it,
static_cast<std::size_t>(
std::distance(it, buf_.end()))};
} }
const_iterator const_iterator
@@ -84,12 +81,38 @@ public:
return begin() + 1; return begin() + 1;
} }
}; };
template<class>
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<class>
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::size_t>(
std::distance(it, buf_.end()))};
}
/** Returns a chunk-encoded ConstBufferSequence. /** Returns a chunk-encoded ConstBufferSequence.
This returns a buffer sequence representing the This returns a buffer sequence representing the
first chunk of a chunked transfer coded body. first chunk of a chunked transfer coded body.
@param fin `true` if this is the last chunk.
@param buffers The input buffer sequence. @param buffers The input buffer sequence.
@return A chunk-encoded ConstBufferSequence representing the input. @return A chunk-encoded ConstBufferSequence representing the input.
@@ -103,16 +126,20 @@ implementation_defined
beast::detail::buffer_cat_helper<boost::asio::const_buffer, beast::detail::buffer_cat_helper<boost::asio::const_buffer,
chunk_encode_text, ConstBufferSequence, boost::asio::const_buffers_1> chunk_encode_text, ConstBufferSequence, boost::asio::const_buffers_1>
#endif #endif
chunk_encode(ConstBufferSequence const& buffers) chunk_encode(bool fin, ConstBufferSequence const& buffers)
{ {
using boost::asio::buffer_size; using boost::asio::buffer_size;
return buffer_cat( return buffer_cat(
chunk_encode_text{buffer_size(buffers)}, chunk_encode_text{buffer_size(buffers)},
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 <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
*/
inline inline
#if GENERATING_DOCS #if GENERATING_DOCS
implementation_defined implementation_defined
@@ -121,11 +148,9 @@ boost::asio::const_buffers_1
#endif #endif
chunk_encode_final() chunk_encode_final()
{ {
return boost::asio::const_buffers_1( return boost::asio::const_buffers_1{"0\r\n\r\n", 5};
"0\r\n\r\n", 5);
} }
} // detail
} // http } // http
} // beast } // beast

View File

@@ -10,7 +10,7 @@
#include <beast/http/concepts.hpp> #include <beast/http/concepts.hpp>
#include <beast/http/resume_context.hpp> #include <beast/http/resume_context.hpp>
#include <beast/http/detail/chunk_encode.hpp> #include <beast/http/chunk_encode.hpp>
#include <beast/core/buffer_cat.hpp> #include <beast/core/buffer_cat.hpp>
#include <beast/core/bind_handler.hpp> #include <beast/core/bind_handler.hpp>
#include <beast/core/buffer_concepts.hpp> #include <beast/core/buffer_concepts.hpp>
@@ -191,7 +191,7 @@ class write_op
if(d.wp.chunked) if(d.wp.chunked)
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
buffer_cat(d.wp.sb.data(), buffer_cat(d.wp.sb.data(),
detail::chunk_encode(buffers)), chunk_encode(false, buffers)),
std::move(self_)); std::move(self_));
else else
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
@@ -218,7 +218,7 @@ class write_op
// write body // write body
if(d.wp.chunked) if(d.wp.chunked)
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
detail::chunk_encode(buffers), chunk_encode(false, buffers),
std::move(self_)); std::move(self_));
else else
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
@@ -243,7 +243,7 @@ public:
d.resume = { d.resume = {
[sp]() mutable [sp]() mutable
{ {
write_op self(std::move(sp)); write_op self{std::move(sp)};
self.d_->cont = false; self.d_->cont = false;
auto& ios = self.d_->s.get_io_service(); auto& ios = self.d_->s.get_io_service();
ios.dispatch(bind_handler(std::move(self), ios.dispatch(bind_handler(std::move(self),
@@ -383,7 +383,7 @@ operator()(error_code ec, std::size_t, bool again)
// write final chunk // write final chunk
d.state = 5; d.state = 5;
boost::asio::async_write(d.s, boost::asio::async_write(d.s,
detail::chunk_encode_final(), std::move(*this)); chunk_encode_final(), std::move(*this));
return; return;
case 5: case 5:
@@ -425,7 +425,7 @@ public:
// write headers and body // write headers and body
if(chunked_) if(chunked_)
boost::asio::write(stream_, buffer_cat( boost::asio::write(stream_, buffer_cat(
sb_.data(), detail::chunk_encode(buffers)), ec_); sb_.data(), chunk_encode(false, buffers)), ec_);
else else
boost::asio::write(stream_, buffer_cat( boost::asio::write(stream_, buffer_cat(
sb_.data(), buffers), ec_); sb_.data(), buffers), ec_);
@@ -454,7 +454,7 @@ public:
// write body // write body
if(chunked_) if(chunked_)
boost::asio::write(stream_, boost::asio::write(stream_,
detail::chunk_encode(buffers), ec_); chunk_encode(false, buffers), ec_);
else else
boost::asio::write(stream_, buffers, ec_); boost::asio::write(stream_, buffers, ec_);
} }
@@ -563,7 +563,7 @@ write(SyncWriteStream& stream,
// final body chunk with the final chunk delimiter. // final body chunk with the final chunk delimiter.
// //
// write final chunk // write final chunk
boost::asio::write(stream, detail::chunk_encode_final(), ec); boost::asio::write(stream, chunk_encode_final(), ec);
if(ec) if(ec)
return; return;
} }

View File

@@ -5,13 +5,14 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#include <beast/http/detail/chunk_encode.hpp> // Test that header file is self-contained.
#include <beast/http/chunk_encode.hpp>
#include <beast/core/to_string.hpp> #include <beast/core/to_string.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
namespace detail {
class chunk_encode_test : public beast::unit_test::suite class chunk_encode_test : public beast::unit_test::suite
{ {
@@ -35,8 +36,8 @@ public:
{ {
using boost::asio::buffer; using boost::asio::buffer;
if(! fc.s.empty()) if(! fc.s.empty())
s.append(to_string( s.append(to_string(chunk_encode(
chunk_encode(buffer(fc.s.data(), fc.s.size())))); false, buffer(fc.s.data(), fc.s.size()))));
s.append(to_string(chunk_encode_final())); s.append(to_string(chunk_encode_final()));
} }
@@ -45,8 +46,8 @@ public:
encode1(std::string& s, std::string const& piece) encode1(std::string& s, std::string const& piece)
{ {
using boost::asio::buffer; using boost::asio::buffer;
s.append(to_string( s.append(to_string(chunk_encode(
chunk_encode(buffer(piece.data(), piece.size())))); false, buffer(piece.data(), piece.size()))));
} }
static static
@@ -122,11 +123,14 @@ public:
"****\r\n" "****\r\n"
"0\r\n\r\n", "0\r\n\r\n",
"****", final_chunk{}); "****", 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); BEAST_DEFINE_TESTSUITE(chunk_encode,http,beast);
} // detail
} // http } // http
} // beast } // beast