Skip to content

Commit 9f34f0b

Browse files
committed
body_read_stream implementation
1 parent daf37da commit 9f34f0b

File tree

5 files changed

+652
-1
lines changed

5 files changed

+652
-1
lines changed

include/boost/beast2.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef BOOST_BEAST2_HPP
1111
#define BOOST_BEAST2_HPP
1212

13+
#include <boost/beast2/body_read_stream.hpp>
1314
#include <boost/beast2/read.hpp>
1415
#include <boost/beast2/write.hpp>
1516

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
//
2+
// Copyright (c) 2025 Mungo Gill
3+
//
4+
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
//
7+
// Official repository: https://github.com/cppalliance/beast2
8+
//
9+
10+
#ifndef BOOST_HTTP_IO_BODY_READ_STREAM_HPP
11+
#define BOOST_HTTP_IO_BODY_READ_STREAM_HPP
12+
13+
#include <boost/asio/async_result.hpp>
14+
#include <boost/http_proto/parser.hpp>
15+
#include <boost/system/error_code.hpp>
16+
17+
namespace boost {
18+
namespace beast2 {
19+
20+
/** A body reader for HTTP/1 messages.
21+
22+
This type meets the requirements of asio's
23+
AsyncReadStream, and is constructed with a reference to an
24+
underlying AsyncReadStream.
25+
26+
Any call to `async_read_some` initially triggers reads
27+
from the underlying stream until all of the HTTP headers
28+
and at least one byte of the body
29+
have been read and processed. Thereafter, each subsequent
30+
call to `async_read_some` processes at least one byte of
31+
the body, triggering, where required, calls to the underlying
32+
stream's `async_read_some` method. The resulting body
33+
data is stored in the referenced MutableBufferSequence.
34+
35+
All processing depends on a http_io::parser object owned
36+
by the caller and referenced in the construction of this
37+
object.
38+
39+
@see
40+
@ref http_proto::parser.
41+
*/
42+
template<class AsyncReadStream>
43+
class body_read_stream {
44+
45+
public:
46+
47+
/** The type of the executor associated with the stream.
48+
49+
This will be the type of executor used to invoke completion
50+
handlers which do not have an explicit associated executor.
51+
*/
52+
typedef typename AsyncReadStream::executor_type executor_type;
53+
54+
/** Get the executor associated with the object.
55+
56+
This function may be used to obtain the executor object that the
57+
stream uses to dispatch completion handlers without an assocaited
58+
executor.
59+
60+
@return A copy of the executor that stream will use to dispatch
61+
handlers.
62+
*/
63+
executor_type get_executor() {
64+
return stream_.get_executor();
65+
}
66+
67+
/** Constructor
68+
69+
This constructor creates the stream by forwarding all arguments
70+
to the underlying socket. The socket then needs to be open and
71+
connected or accepted before data can be sent or received on it.
72+
73+
@param us The underlying stream from which the HTTP message is read.
74+
This object's executor is initialized to that of the
75+
underlying stream.
76+
77+
@param pr A http_proto::parser object which will perform the parsing
78+
of the HTTP message and extraction of the body. This must
79+
be initialized by the caller and ownership of the parser is
80+
retained by the caller, which must guarantee that it remains
81+
valid until the handler is called.
82+
*/
83+
explicit
84+
body_read_stream(
85+
AsyncReadStream& us,
86+
http_proto::parser& pr);
87+
88+
/** Read some data asynchronously.
89+
90+
This function is used to asynchronously read data from the stream.
91+
92+
This call always returns immediately. The asynchronous operation
93+
will continue until one of the following conditions is true:
94+
95+
@li The HTTP headers are read in full from the underlying stream
96+
and one or more bytes of the body are read from the stream and
97+
stored in the buffer `mb`.
98+
99+
@li An error occurs.
100+
101+
The algorithm, known as a <em>composed asynchronous operation</em>,
102+
is implemented in terms of calls to the underlying stream's `async_read_some`
103+
function. The program must ensure that no other calls to
104+
`async_read_some` are performed until this operation completes.
105+
106+
@param mb The buffers into which the body data will be read. If the size
107+
of the buffers is zero bytes, the operation always completes immediately
108+
with no error.
109+
Although the buffers object may be copied as necessary, ownership of the
110+
underlying memory blocks is retained by the caller, which must guarantee
111+
that they remain valid until the handler is called.
112+
Where the mb buffer is not of sufficient size to hold the read data, the
113+
remainder may be read by subsequent calls to this function.
114+
115+
@param handler The completion handler to invoke when the operation
116+
completes. The implementation takes ownership of the handler by
117+
performing a decay-copy. The equivalent function signature of
118+
the handler must be:
119+
`void handler(error_code error, std::size_t bytes_transferred)`
120+
If the handler has an associated immediate executor,
121+
an immediate completion will be dispatched to it.
122+
Otherwise, the handler will not be invoked from within
123+
this function. Invocation of the handler will be performed
124+
by dispatching to the immediate executor. If no
125+
immediate executor is specified, this is equivalent
126+
to using `net::post`.
127+
128+
@note The `async_read_some` operation may not receive all of the requested
129+
number of bytes. Consider using the function `net::async_read` if you need
130+
to ensure that the requested amount of data is read before the asynchronous
131+
operation completes.
132+
133+
@par Per-Operation Cancellation
134+
135+
This asynchronous operation supports cancellation for the following
136+
net::cancellation_type values:
137+
138+
@li @c net::cancellation_type::terminal
139+
@li @c net::cancellation_type::partial
140+
@li @c net::cancellation_type::total
141+
142+
if they are also supported by the underlying stream's @c async_read_some
143+
operation.
144+
*/
145+
template<
146+
class MutableBufferSequence,
147+
BOOST_ASIO_COMPLETION_TOKEN_FOR(
148+
void(system::error_code, std::size_t)) CompletionToken>
149+
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
150+
void(system::error_code, std::size_t))
151+
async_read_some(
152+
MutableBufferSequence mb,
153+
CompletionToken&& handler);
154+
155+
private:
156+
AsyncReadStream& stream_;
157+
http_proto::parser& pr_;
158+
};
159+
160+
} // beast2
161+
} // boost
162+
163+
#include <boost/beast2/impl/body_read_stream.hpp>
164+
165+
#endif
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//
2+
// Copyright (c) 2025 Mungo Gill
3+
//
4+
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
//
7+
// Official repository: https://github.com/cppalliance/beast2
8+
//
9+
10+
#ifndef BOOST_HTTP_IO_IMPL_BODY_READ_STREAM_HPP
11+
#define BOOST_HTTP_IO_IMPL_BODY_READ_STREAM_HPP
12+
13+
#include <boost/beast2/detail/config.hpp>
14+
#include <boost/beast2/read.hpp>
15+
16+
#include <boost/asio/buffer.hpp>
17+
#include <boost/asio/buffers_iterator.hpp>
18+
#include <boost/asio/coroutine.hpp>
19+
#include <boost/core/ignore_unused.hpp>
20+
21+
#include <iostream>
22+
23+
namespace boost {
24+
namespace beast2 {
25+
26+
namespace detail {
27+
28+
template <class MutableBufferSequence, class AsyncReadStream>
29+
class body_read_stream_op : public asio::coroutine {
30+
31+
AsyncReadStream& stream_;
32+
MutableBufferSequence mb_;
33+
http_proto::parser& pr_;
34+
35+
public:
36+
37+
body_read_stream_op(
38+
AsyncReadStream& s,
39+
MutableBufferSequence&& mb,
40+
http_proto::parser& pr) noexcept
41+
: stream_(s)
42+
, mb_(std::move(mb))
43+
, pr_(pr)
44+
{
45+
}
46+
47+
template<class Self>
48+
void
49+
operator()(
50+
Self& self,
51+
system::error_code ec = {},
52+
std::size_t bytes_transferred = 0)
53+
{
54+
boost::ignore_unused(bytes_transferred);
55+
56+
BOOST_ASIO_CORO_REENTER(*this)
57+
{
58+
self.reset_cancellation_state(
59+
asio::enable_total_cancellation());
60+
61+
if (!pr_.got_header())
62+
{
63+
BOOST_ASIO_CORO_YIELD
64+
{
65+
BOOST_ASIO_HANDLER_LOCATION((
66+
__FILE__, __LINE__,
67+
"async_read_header"));
68+
beast2::async_read_header<
69+
AsyncReadStream,
70+
Self > (
71+
stream_,
72+
pr_,
73+
std::move(self));
74+
}
75+
if (ec.failed()) goto upcall;
76+
}
77+
78+
if (!!self.cancelled())
79+
{
80+
ec = asio::error::operation_aborted;
81+
goto upcall;
82+
}
83+
84+
BOOST_ASIO_CORO_YIELD
85+
{
86+
BOOST_ASIO_HANDLER_LOCATION((
87+
__FILE__, __LINE__,
88+
"async_read_some"));
89+
beast2::async_read_some(
90+
stream_,
91+
pr_,
92+
std::move(self));
93+
}
94+
95+
upcall:
96+
std::size_t n = 0;
97+
98+
if (!ec.failed())
99+
{
100+
auto source_buf = pr_.pull_body();
101+
102+
n = boost::asio::buffer_copy(mb_, source_buf);
103+
104+
pr_.consume_body(n);
105+
106+
ec = (n != 0) ? system::error_code{} : asio::stream_errc::eof;
107+
}
108+
109+
self.complete(ec, n);
110+
}
111+
}
112+
};
113+
114+
} // detail
115+
116+
//------------------------------------------------
117+
118+
// TODO: copy in Beast's stream traits to check if AsyncReadStream
119+
// is an AsyncReadStream, and also static_assert that body_read_stream is too.
120+
121+
122+
123+
template<class AsyncReadStream>
124+
body_read_stream<AsyncReadStream>::body_read_stream(
125+
AsyncReadStream& und_stream
126+
, http_proto::parser& pr)
127+
:
128+
stream_(und_stream)
129+
, pr_(pr)
130+
{
131+
}
132+
133+
134+
template<class AsyncReadStream>
135+
template<
136+
class MutableBufferSequence,
137+
BOOST_ASIO_COMPLETION_TOKEN_FOR(
138+
void(system::error_code, std::size_t)) CompletionToken>
139+
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
140+
void(system::error_code, std::size_t))
141+
body_read_stream<AsyncReadStream>::async_read_some(
142+
MutableBufferSequence mb
143+
, CompletionToken&& token)
144+
{
145+
return asio::async_compose<
146+
CompletionToken,
147+
void(system::error_code, std::size_t)>(
148+
detail::body_read_stream_op<
149+
MutableBufferSequence, AsyncReadStream>{stream_, std::move(mb), pr_},
150+
token,
151+
stream_
152+
);
153+
}
154+
155+
} // beast2
156+
} // boost
157+
158+
#endif

src/asio_io_context.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include <boost/beast2/server/call_mf.hpp>
1313
#include <boost/asio/executor_work_guard.hpp>
1414
#include <boost/asio/signal_set.hpp>
15-
#include <boost/asio/strand.hpp>
1615
#include <thread>
1716
#include <vector>
1817

0 commit comments

Comments
 (0)