Skip to content

Commit

Permalink
stream prepare has nullary overload
Browse files Browse the repository at this point in the history
  • Loading branch information
cmazakas committed Apr 9, 2024
1 parent b5cbccc commit d502b88
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 70 deletions.
18 changes: 14 additions & 4 deletions include/boost/http_proto/serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,25 +247,35 @@ class BOOST_SYMBOL_VISIBLE
// trailer-section
// CRLF

static
constexpr
std::size_t
crlf_len_ = 2;

// chunk = chunk-size [ chunk-ext ] CRLF
// chunk-data CRLF
static
constexpr
std::size_t
chunk_header_len_ = 16 + 2; // chunk-size + CRLF
chunk_header_len_ =
16 + // 16 hex digits => 64 bit number
crlf_len_;

// last-chunk = 1*("0") [ chunk-ext ] CRLF
static
constexpr
std::size_t
last_chunk_len_ = 1 + 2 + 2; // chunked-body termination requires an extra CRLF
last_chunk_len_ =
1 + // "0"
crlf_len_ +
crlf_len_; // chunked-body termination requires an extra CRLF

static
constexpr
std::size_t
chunked_overhead_ =
chunk_header_len_ +
2 + // CRLF
crlf_len_ + // closing chunk data
last_chunk_len_;

detail::workspace ws_;
Expand Down Expand Up @@ -315,7 +325,7 @@ struct serializer::stream

BOOST_HTTP_PROTO_DECL
buffers_type
prepare(std::size_t n) const;
prepare() const;

BOOST_HTTP_PROTO_DECL
void
Expand Down
27 changes: 21 additions & 6 deletions src/serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,14 +560,29 @@ size() const
auto
serializer::
stream::
prepare(
std::size_t n) const ->
buffers_type
prepare() const ->
buffers_type
{
auto n = sr_->tmp0_.capacity();
if( sr_->is_chunked_ )
{
// for chunked encoding, we want to unconditionally
// reserve space for the complete chunk and the
// last-chunk
// this enables users to call:
//
// stream.commit(n); stream.close();
//
// without needing to worry about draining the
// serializer via `consume()` calls
if( n < chunked_overhead_ + 1 )
detail::throw_length_error();

n -= chunked_overhead_;
return buffers::sans_prefix(
sr_->tmp0_.prepare(n + chunk_header_len_),
sr_->tmp0_.prepare(chunk_header_len_ + n),
chunk_header_len_);
}

return sr_->tmp0_.prepare(n);
}
Expand Down Expand Up @@ -603,10 +618,10 @@ stream::
close() const
{
// Precondition violation
if(! sr_->more_)
if(! sr_->more_ )
detail::throw_logic_error();

if (sr_->is_chunked_)
if( sr_->is_chunked_ )
write_last_chunk(sr_->tmp0_);

sr_->more_ = false;
Expand Down
171 changes: 111 additions & 60 deletions test/unit/serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,26 +255,34 @@ struct serializer_test
f(s);
};

struct check_stream_opts
{
std::size_t sr_capacity = 1024;
};

template <class F>
void
check_stream(
core::string_view headers,
core::string_view body,
check_stream_opts const& opts,
F f)
{
auto sr_capacity = opts.sr_capacity;

response res(headers);

serializer sr(1024);
serializer sr(sr_capacity);
auto stream = sr.start_stream(res);

std::vector<char> s; // stores complete output
std::size_t const chunk_data_size = 100;

auto prepare_chunk = [&]
{
auto mbs = stream.prepare(chunk_data_size);
auto mbs = stream.prepare();

auto bs = buffers::buffer_size(mbs);
BOOST_TEST_EQ(bs, chunk_data_size);
BOOST_TEST_GT(bs, 0);

if( bs > body.size() )
bs = body.size();
Expand All @@ -300,8 +308,7 @@ struct serializer_test
while( buf.size() > 0 )
{
auto num_copied =
buffers::buffer_copy(
out_buf, buf);
buffers::buffer_copy(out_buf, buf);

buf += num_copied;

Expand Down Expand Up @@ -454,55 +461,100 @@ struct serializer_test
check_chunked_body(s, "");
});

check_stream(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"\r\n",
std::string(0, '*'),
[](core::string_view s){
core::string_view expected_header =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"\r\n";
BOOST_TEST(s.starts_with(expected_header));
s.remove_prefix(expected_header.size());
BOOST_TEST_EQ(s, std::string(0, '*'));
});
// empty stream
{
check_stream_opts opts;
check_stream(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"\r\n",
std::string(0, '*'),
opts,
[](core::string_view s)
{
core::string_view expected_header =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"\r\n";
BOOST_TEST(s.starts_with(expected_header));
s.remove_prefix(expected_header.size());
BOOST_TEST(s.empty());
});
}

check_stream(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 2048\r\n"
"\r\n",
std::string(2048, '*'),
[](core::string_view s){
core::string_view expected_header =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 2048\r\n"
"\r\n";
BOOST_TEST(s.starts_with(expected_header));
s.remove_prefix(expected_header.size());
BOOST_TEST_EQ(s, std::string(2048, '*'));
});
// empty stream, chunked
{
check_stream_opts opts;
check_stream(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n",
std::string(0, '*'),
opts,
[](core::string_view s)
{
core::string_view expected_header =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n";
BOOST_TEST(s.starts_with(expected_header));
s.remove_prefix(expected_header.size());
check_chunked_body(s, "");
});
}

check_stream(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n",
std::string(2048, '*'),
[](core::string_view s){
core::string_view expected_header =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n";
BOOST_TEST(s.starts_with(expected_header));
s.remove_prefix(expected_header.size());
check_chunked_body(
s, std::string(2048, '*'));
});
// stream
{
check_stream_opts opts;
check_stream(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 13370\r\n"
"\r\n",
std::string(13370, '*'),
opts,
[](core::string_view s){
core::string_view expected_header =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 13370\r\n"
"\r\n";

BOOST_TEST(
s.starts_with(expected_header));

s.remove_prefix(expected_header.size());
BOOST_TEST_EQ(
s, std::string(13370, '*'));
});
}

// stream, chunked
{
check_stream_opts opts;
check_stream(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n",
std::string(13370, '*'),
opts,
[](core::string_view s)
{
core::string_view expected_header =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n";
BOOST_TEST(s.starts_with(
expected_header));
s.remove_prefix(expected_header.size());
check_chunked_body(
s, std::string(13370, '*'));
});
}
}

void
Expand Down Expand Up @@ -631,8 +683,8 @@ struct serializer_test

serializer sr;
auto stream = sr.start_stream(res);
auto mbs = stream.prepare(0);
BOOST_TEST_EQ(
auto mbs = stream.prepare();
BOOST_TEST_GT(
buffers::buffer_size(mbs), 0);
BOOST_TEST_THROWS(
stream.commit(0), std::logic_error);
Expand Down Expand Up @@ -686,14 +738,13 @@ struct serializer_test
std::string chunk(chunk_size, 'a');
while( num_written < 2048 )
{
auto mbs =
stream.prepare(chunk_size);

auto n = buffers::buffer_copy(
mbs,
stream.prepare(),
buffers::const_buffer(
chunk.data(), chunk.size()));
chunk.data(),
chunk.size()));

BOOST_TEST_GT(n, 0);
stream.commit(n);
num_written += n;
}
Expand Down

0 comments on commit d502b88

Please sign in to comment.