mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 04:47:29 +02:00
Add http::message_generator
This commit is contained in:
@ -16,6 +16,11 @@ The generator will be asked to produce buffers. The consuming code will signal
|
||||
how much of the data has been consumed, and repeatedly query for buffers until
|
||||
no more data is available, or the generator indicates an error condition.
|
||||
|
||||
In this way, serializers can be adapted as [*BuffersGenerator], for example
|
||||
[link beast.ref.boost__beast__http__message_generator
|
||||
`http::message_generator`] which provides a type-erased interface for a variety
|
||||
of concrete http message types.
|
||||
|
||||
Overloads of [link beast.ref.boost__beast__write `write`] and [link
|
||||
beast.ref.boost__beast__async_write `async_write`] operations are provided as
|
||||
free functions. These operations will consume the output of a
|
||||
@ -33,6 +38,7 @@ In this table:
|
||||
|
||||
* `G` denotes a type meeting the requirements of [*BuffersGenerator].
|
||||
* `g` denotes a value of type `G`.
|
||||
* `c` denotes a possibly-const value of type `G`.
|
||||
* `n` is a value of type `std::size_t`.
|
||||
* `ec` is a value of type [link beast.ref.boost__beast__error_code `error_code&`].
|
||||
|
||||
@ -46,7 +52,7 @@ In this table:
|
||||
This is the type of buffer returned by `g.prepare(ec)`.
|
||||
]
|
||||
][
|
||||
[`g.is_done()`]
|
||||
[`c.is_done()`]
|
||||
[`bool`]
|
||||
[
|
||||
Called to ask the generator for its completion status.
|
||||
@ -56,8 +62,6 @@ In this table:
|
||||
|
||||
[*Note:] The result of invoking `prepare` on `g` once it has completed
|
||||
is unspecified.
|
||||
|
||||
This member function does not alter the state of the generator.
|
||||
]
|
||||
][
|
||||
[`g.prepare(ec)`]
|
||||
@ -106,7 +110,7 @@ In this table:
|
||||
]
|
||||
][
|
||||
[`is_buffers_generator<G>`]
|
||||
[`std::true_type`]
|
||||
[`std::bool_constant`]
|
||||
[
|
||||
An alias for `std::true_type` for `G`, otherwise an alias
|
||||
for `std::false_type`.
|
||||
@ -121,7 +125,7 @@ In this table:
|
||||
{
|
||||
using const_buffers_type = net::const_buffer;
|
||||
|
||||
bool is_done();
|
||||
bool is_done() const;
|
||||
const_buffers_type prepare( error_code& ec );
|
||||
void consume( std::size_t n );
|
||||
};
|
||||
@ -132,6 +136,8 @@ In this table:
|
||||
|
||||
[heading Models]
|
||||
|
||||
* [link beast.ref.boost__beast__http__message_generator `http::message_generator`]
|
||||
|
||||
[heading Algorithms]
|
||||
|
||||
* [link beast.ref.boost__beast__async_write `async_write`]
|
||||
|
@ -205,6 +205,7 @@
|
||||
<member><link linkend="beast.ref.boost__beast__http__file_body">file_body</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__http__header">header</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__http__message">message</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__http__message_generator">message_generator</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__http__parser">parser</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__http__request">request</link></member>
|
||||
<member><link linkend="beast.ref.boost__beast__http__request_header">request_header</link></member>
|
||||
|
@ -11,11 +11,9 @@
|
||||
#define BOOST_BEAST_CORE_BUFFERS_GENERATOR_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
@ -63,7 +61,7 @@ struct is_buffers_generator<
|
||||
by a caller-provided BuffersGenerator to a stream. The call
|
||||
will block until one of the following conditions is true:
|
||||
|
||||
@li The generator returns an empty buffers sequence.
|
||||
@li A call to the generator's `is_done` returns `false`.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
@ -85,8 +83,7 @@ template<
|
||||
class SyncWriteStream,
|
||||
class BuffersGenerator
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
,
|
||||
typename std::enable_if<is_buffers_generator<
|
||||
, typename std::enable_if<is_buffers_generator<
|
||||
typename std::decay<BuffersGenerator>::
|
||||
type>::value>::type* = nullptr
|
||||
#endif
|
||||
@ -103,7 +100,7 @@ write(
|
||||
by a caller-provided BuffersGenerator to a stream. The call
|
||||
will block until one of the following conditions is true:
|
||||
|
||||
@li The generator returns an empty buffers sequence.
|
||||
@li A call to the generator's `is_done` returns `false`.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
@ -125,8 +122,7 @@ template<
|
||||
class SyncWriteStream,
|
||||
class BuffersGenerator
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
,
|
||||
typename std::enable_if<is_buffers_generator<
|
||||
, typename std::enable_if<is_buffers_generator<
|
||||
typename std::decay<BuffersGenerator>::
|
||||
type>::value>::type* = nullptr
|
||||
#endif
|
||||
@ -145,7 +141,7 @@ write(
|
||||
operation will continue until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li The generator returns an empty buffers sequence.
|
||||
@li A call to the generator's `is_done` returns `false`.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
@ -182,8 +178,7 @@ template<
|
||||
class BuffersGenerator,
|
||||
class CompletionToken
|
||||
#if !BOOST_BEAST_DOXYGEN
|
||||
,
|
||||
typename std::enable_if<is_buffers_generator<
|
||||
, typename std::enable_if<is_buffers_generator<
|
||||
BuffersGenerator>::value>::type* = nullptr
|
||||
#endif
|
||||
>
|
||||
@ -191,16 +186,15 @@ auto
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
BuffersGenerator generator,
|
||||
CompletionToken&& token) //
|
||||
-> typename net::async_result<
|
||||
typename std::decay<
|
||||
CompletionToken>::type,
|
||||
void(error_code, std::size_t)>::
|
||||
return_type;
|
||||
CompletionToken&& token) ->
|
||||
typename net::async_result<
|
||||
typename std::decay<
|
||||
CompletionToken>::type,
|
||||
void(error_code, std::size_t)>::return_type;
|
||||
|
||||
} // namespace beast
|
||||
} // namespace boost
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <include/boost/beast/core/impl/buffers_generator.hpp>
|
||||
#include <boost/beast/core/impl/buffers_generator.hpp>
|
||||
|
||||
#endif
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/beast/http/fields.hpp>
|
||||
#include <boost/beast/http/file_body.hpp>
|
||||
#include <boost/beast/http/message_generator.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
|
104
include/boost/beast/http/impl/message_generator.hpp
Normal file
104
include/boost/beast/http/impl/message_generator.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright (c) 2022 Seth Heeren (sgheeren 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_GENERATOR_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_MESSAGE_GENERATOR_HPP
|
||||
|
||||
#include <boost/beast/http/message_generator.hpp>
|
||||
#include <boost/smart_ptr/make_unique.hpp>
|
||||
#include <boost/beast/core/buffers_generator.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
message_generator::message_generator(
|
||||
http::message<isRequest, Body, Fields>&& m)
|
||||
: impl_(boost::make_unique<
|
||||
generator_impl<isRequest, Body, Fields>>(
|
||||
std::move(m)))
|
||||
{
|
||||
}
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
struct message_generator::generator_impl
|
||||
: message_generator::impl_base
|
||||
{
|
||||
explicit generator_impl(
|
||||
http::message<isRequest, Body, Fields>&& m)
|
||||
: m_(std::move(m))
|
||||
, sr_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
is_done() override
|
||||
{
|
||||
return sr_.is_done();
|
||||
}
|
||||
|
||||
const_buffers_type
|
||||
prepare(error_code& ec) override
|
||||
{
|
||||
sr_.next(ec, visit{*this});
|
||||
return current_;
|
||||
}
|
||||
|
||||
void
|
||||
consume(std::size_t n) override
|
||||
{
|
||||
sr_.consume((std::min)(n, beast::buffer_bytes(current_)));
|
||||
}
|
||||
|
||||
bool
|
||||
keep_alive() const noexcept override
|
||||
{
|
||||
return m_.keep_alive();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr unsigned max_fixed_bufs = 12;
|
||||
|
||||
http::message<isRequest, Body, Fields> m_;
|
||||
http::serializer<isRequest, Body, Fields> sr_;
|
||||
|
||||
std::array<net::const_buffer, max_fixed_bufs> bs_;
|
||||
const_buffers_type current_ = bs_; // subspan
|
||||
|
||||
struct visit
|
||||
{
|
||||
generator_impl& self_;
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code&, ConstBufferSequence const& buffers)
|
||||
{
|
||||
auto& s = self_.bs_;
|
||||
auto& cur = self_.current_;
|
||||
|
||||
auto it = net::buffer_sequence_begin(buffers);
|
||||
|
||||
std::size_t n =
|
||||
std::distance(it, net::buffer_sequence_end(buffers));
|
||||
|
||||
n = (std::min)(s.size(), n);
|
||||
|
||||
cur = { s.data(), n };
|
||||
std::copy_n(it, n, cur.begin());
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace beast
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
99
include/boost/beast/http/message_generator.hpp
Normal file
99
include/boost/beast/http/message_generator.hpp
Normal file
@ -0,0 +1,99 @@
|
||||
//
|
||||
// Copyright (c) 2022 Seth Heeren (sgheeren 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
#ifndef BOOST_BEAST_HTTP_MESSAGE_GENERATOR_HPP
|
||||
#define BOOST_BEAST_HTTP_MESSAGE_GENERATOR_HPP
|
||||
|
||||
#include <boost/beast/core/span.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Type-erased buffers generator for @ref http::message
|
||||
|
||||
Implements the BuffersGenerator concept for any concrete instance of the
|
||||
@ref http::message template.
|
||||
|
||||
@ref http::message_generator takes ownership of a message on construction,
|
||||
erasing the concrete type from the interface.
|
||||
|
||||
This makes it practical for use in server applications to implement request
|
||||
handling:
|
||||
|
||||
@code
|
||||
template <class Body, class Fields>
|
||||
http::message_generator handle_request(
|
||||
string_view doc_root,
|
||||
http::request<Body, Fields>&& request);
|
||||
@endcode
|
||||
|
||||
The @ref beast::write and @ref beast::async_write operations are provided
|
||||
for BuffersGenerator. The @ref http::message::keep_alive property is made
|
||||
available for use after writing the message.
|
||||
*/
|
||||
class message_generator
|
||||
{
|
||||
public:
|
||||
using const_buffers_type = span<net::const_buffer>;
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
message_generator(http::message<isRequest, Body, Fields>&&);
|
||||
|
||||
/// @ref BuffersGenerator
|
||||
bool is_done() const {
|
||||
return impl_->is_done();
|
||||
}
|
||||
|
||||
/// @ref BuffersGenerator
|
||||
const_buffers_type
|
||||
prepare(error_code& ec)
|
||||
{
|
||||
return impl_->prepare(ec);
|
||||
}
|
||||
|
||||
/// @ref BuffersGenerator
|
||||
void
|
||||
consume(std::size_t n)
|
||||
{
|
||||
impl_->consume(n);
|
||||
}
|
||||
|
||||
/// Returns the result of `m.keep_alive()` on the underlying message
|
||||
bool
|
||||
keep_alive() const noexcept
|
||||
{
|
||||
return impl_->keep_alive();
|
||||
}
|
||||
|
||||
private:
|
||||
struct impl_base
|
||||
{
|
||||
virtual ~impl_base() = default;
|
||||
virtual bool is_done() = 0;
|
||||
virtual const_buffers_type prepare(error_code& ec) = 0;
|
||||
virtual void consume(std::size_t n) = 0;
|
||||
virtual bool keep_alive() const noexcept = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<impl_base> impl_;
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
struct generator_impl;
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace beast
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/beast/http/impl/message_generator.hpp>
|
||||
|
||||
#endif
|
@ -27,7 +27,8 @@ namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct test_buffers_generator {
|
||||
struct test_buffers_generator
|
||||
{
|
||||
using underlying_buffer_sequence = std::array<net::const_buffer, 2>;
|
||||
using const_buffers_type = buffers_suffix<underlying_buffer_sequence>;
|
||||
std::size_t iterations_ = 5;
|
||||
@ -212,7 +213,8 @@ public:
|
||||
BEAST_EXPECT(30 == in.nwrite_bytes());
|
||||
BEAST_EXPECT(
|
||||
"abcde12345abcd1234abc123ab12a1" == in.str());
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(1 == out.nwrite());
|
||||
BEAST_EXPECT(10 == in.nwrite_bytes());
|
||||
@ -254,7 +256,8 @@ public:
|
||||
BEAST_EXPECT(30 == in.nwrite_bytes());
|
||||
BEAST_EXPECT(
|
||||
"abcde12345abcd1234abc123ab12a1" == in.str());
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(1 == out.nwrite());
|
||||
BEAST_EXPECT(10 == in.nwrite_bytes());
|
||||
|
@ -29,6 +29,7 @@ add_executable (tests-beast-http
|
||||
field_compiles.cpp
|
||||
fields.cpp
|
||||
file_body.cpp
|
||||
message_generator.cpp
|
||||
message.cpp
|
||||
parser.cpp
|
||||
read.cpp
|
||||
|
@ -19,6 +19,7 @@ local SOURCES =
|
||||
field_compiles.cpp
|
||||
fields.cpp
|
||||
file_body.cpp
|
||||
message_generator.cpp
|
||||
message.cpp
|
||||
parser.cpp
|
||||
read.cpp
|
||||
|
315
test/beast/http/message_generator.cpp
Normal file
315
test/beast/http/message_generator.cpp
Normal file
@ -0,0 +1,315 @@
|
||||
//
|
||||
// Copyright (c) 2022 Seth Heeren (sgheeren 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <boost/beast/http/message_generator.hpp>
|
||||
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/string_body.hpp>
|
||||
#include <boost/beast/http/write.hpp> //for operator<<
|
||||
|
||||
#include <boost/beast/core/buffers_to_string.hpp>
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/core/buffers_generator.hpp> // for is_buffers_generator and [async_]write overloads
|
||||
#include <boost/beast/_experimental/test/stream.hpp>
|
||||
|
||||
#include <boost/beast/_experimental/unit_test/suite.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Degenerate body generator to trigger dynamic generator buffer
|
||||
// allocation
|
||||
//
|
||||
// Warning: magic numbers ahead
|
||||
//
|
||||
// Arbitrarily decided on 65 buffers of which two are "some" and "body", the
|
||||
// other bufs are chosen by the "seed fragment"
|
||||
struct fragmented_test_body
|
||||
{
|
||||
using value_type = net::const_buffer;
|
||||
|
||||
static std::uint64_t
|
||||
size(net::const_buffer fragment)
|
||||
{
|
||||
return 8 + 63 * fragment.size();
|
||||
}
|
||||
|
||||
struct writer
|
||||
{
|
||||
using const_buffers_type = std::array<net::const_buffer, 65>;
|
||||
|
||||
bool done_ = false;
|
||||
net::const_buffer seed_fragment_;
|
||||
|
||||
template<typename Fields>
|
||||
writer(Fields const&, net::const_buffer fragment)
|
||||
: seed_fragment_(fragment)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(beast::error_code&) const
|
||||
{
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(beast::error_code&)
|
||||
{
|
||||
const_buffers_type cb;
|
||||
std::fill(begin(cb), end(cb), seed_fragment_);
|
||||
cb.at(7) = { "some", 4 };
|
||||
cb.at(27) = { "body", 4 };
|
||||
return std::make_pair(cb, exchange(done_, true));
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<>
|
||||
struct is_body_sized<detail::fragmented_test_body>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
class message_generator_test : public beast::unit_test::suite
|
||||
{
|
||||
static_assert(
|
||||
is_buffers_generator<message_generator>::value,
|
||||
"buffers_generator not modeled");
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_constructible<
|
||||
message_generator,
|
||||
message<true, string_body>&&>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
std::is_constructible<
|
||||
message_generator,
|
||||
message<false, string_body>&&>::value);
|
||||
|
||||
// only rvalue refs
|
||||
BOOST_STATIC_ASSERT(
|
||||
not std::is_constructible<
|
||||
message_generator,
|
||||
message<true, string_body>&>::value);
|
||||
BOOST_STATIC_ASSERT(
|
||||
not std::is_constructible<
|
||||
message_generator,
|
||||
message<true, string_body> const&>::value);
|
||||
|
||||
static request<string_body> make_get() {
|
||||
return request<string_body>{
|
||||
verb::get, "/path/query?1", 11,
|
||||
"Serializable but ignored on GET"
|
||||
};
|
||||
}
|
||||
|
||||
static http::response<detail::fragmented_test_body>
|
||||
make_fragmented_body_response(net::const_buffer seed_fragment)
|
||||
{
|
||||
http::response<detail::fragmented_test_body> msg(
|
||||
http::status::ok, 11, seed_fragment);
|
||||
msg.prepare_payload();
|
||||
|
||||
BEAST_EXPECT(msg.has_content_length());
|
||||
BEAST_EXPECT(
|
||||
msg.at(http::field::content_length) ==
|
||||
std::to_string(8 + 63 * seed_fragment.size()));
|
||||
return msg;
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
testGenerate()
|
||||
{
|
||||
message_generator gen(make_get());
|
||||
error_code ec;
|
||||
|
||||
std::string received;
|
||||
|
||||
while(! gen.is_done())
|
||||
{
|
||||
message_generator::const_buffers_type b = gen.prepare(ec);
|
||||
BEAST_EXPECT(! ec);
|
||||
received += buffers_to_string(b);
|
||||
|
||||
gen.consume(buffer_bytes(b));
|
||||
}
|
||||
|
||||
BEAST_EXPECT(received ==
|
||||
"GET /path/query?1 HTTP/1.1\r\n\r\n"
|
||||
"Serializable but ignored on GET");
|
||||
}
|
||||
|
||||
void
|
||||
testGenerateSlowConsumer()
|
||||
{
|
||||
message_generator gen(make_get());
|
||||
error_code ec;
|
||||
|
||||
std::vector<std::string> received;
|
||||
|
||||
while(! gen.is_done())
|
||||
{
|
||||
message_generator::const_buffers_type b = gen.prepare(ec);
|
||||
BEAST_EXPECT(! ec);
|
||||
received.push_back(buffers_to_string(b).substr(0, 3));
|
||||
|
||||
gen.consume(3); // allowed > buffer_bytes(b)
|
||||
}
|
||||
|
||||
BEAST_EXPECT(
|
||||
(received ==
|
||||
std::vector<std::string>{
|
||||
"GET", " /p", "ath", "/qu", "ery",
|
||||
"?1 ", "HTT", "P/1", ".1\r", "\n\r\n",
|
||||
"Ser", "ial", "iza", "ble", " bu",
|
||||
"t i", "gno", "red", " on", " GE",
|
||||
"T",
|
||||
}));
|
||||
}
|
||||
|
||||
void
|
||||
testAsyncWrite()
|
||||
{
|
||||
net::io_context ioc;
|
||||
test::stream out(ioc), in(ioc);
|
||||
{
|
||||
test::connect(out, in);
|
||||
|
||||
message_generator gen(make_get());
|
||||
|
||||
beast::async_write(out, std::move(gen),
|
||||
[&](error_code ec, size_t total) {
|
||||
BEAST_EXPECT(total == 61);
|
||||
BEAST_EXPECT(!ec.failed());
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
BEAST_EXPECT(61 == in.nwrite_bytes());
|
||||
BEAST_EXPECT(in.str() ==
|
||||
"GET /path/query?1 HTTP/1.1\r\n\r\n"
|
||||
"Serializable but ignored on GET");
|
||||
}
|
||||
|
||||
void
|
||||
testWrite()
|
||||
{
|
||||
net::io_context ioc;
|
||||
test::stream out(ioc), in(ioc);
|
||||
test::connect(out, in);
|
||||
|
||||
{
|
||||
message_generator gen(make_get());
|
||||
|
||||
error_code ec;
|
||||
std::size_t total = beast::write(out, gen, ec);
|
||||
|
||||
BEAST_EXPECT(total == 61);
|
||||
BEAST_EXPECT(!ec.failed());
|
||||
|
||||
BEAST_EXPECT(61 == in.nwrite_bytes());
|
||||
BEAST_EXPECT(in.str() ==
|
||||
"GET /path/query?1 HTTP/1.1\r\n\r\n"
|
||||
"Serializable but ignored on GET");
|
||||
}
|
||||
|
||||
in.clear();
|
||||
|
||||
{
|
||||
// rvalue accepted
|
||||
std::size_t total =
|
||||
beast::write(out, message_generator{ make_get() });
|
||||
BEAST_EXPECT(total == 61);
|
||||
BEAST_EXPECT(in.str() ==
|
||||
"GET /path/query?1 HTTP/1.1\r\n\r\n"
|
||||
"Serializable but ignored on GET");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testFragmentedBody()
|
||||
{
|
||||
net::io_context ioc;
|
||||
test::stream out(ioc), in(ioc);
|
||||
test::connect(out, in);
|
||||
|
||||
{
|
||||
message_generator gen(make_fragmented_body_response(net::buffer("", 0)));
|
||||
|
||||
error_code ec;
|
||||
std::size_t total = beast::write(out, gen, ec);
|
||||
|
||||
BEAST_EXPECT(total == 46);
|
||||
BEAST_EXPECT(! ec.failed());
|
||||
|
||||
BEAST_EXPECT(
|
||||
in.str() ==
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: 8\r\n\r\nsomebody");
|
||||
}
|
||||
|
||||
in.clear();
|
||||
|
||||
{
|
||||
message_generator gen(make_fragmented_body_response(net::buffer("x", 1)));
|
||||
|
||||
error_code ec;
|
||||
std::size_t total = beast::write(out, gen, ec);
|
||||
|
||||
BEAST_EXPECT(total == 47 + 63);
|
||||
BEAST_EXPECT(! ec.failed());
|
||||
|
||||
BEAST_EXPECT(
|
||||
in.str() ==
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: 71\r\n\r\nxxxxxxxsomexxxxxxxxxxxxxxxxxxxbodyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testKeepAlive()
|
||||
{
|
||||
auto request = [](int version)
|
||||
{
|
||||
return http::request<http::string_body>(
|
||||
http::verb::post, "/", version);
|
||||
};
|
||||
BEAST_EXPECT(
|
||||
! http::message_generator(request(10)).keep_alive());
|
||||
BEAST_EXPECT(
|
||||
http::message_generator(request(11)).keep_alive());
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testGenerate();
|
||||
testGenerateSlowConsumer();
|
||||
testAsyncWrite();
|
||||
testWrite();
|
||||
testFragmentedBody();
|
||||
testKeepAlive();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(beast,http,message_generator);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
Reference in New Issue
Block a user