Skip to content

Commit 7b71549

Browse files
committed
body_read_stream implementation
1 parent 83dc972 commit 7b71549

File tree

5 files changed

+640
-1
lines changed

5 files changed

+640
-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 us_.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& us_;
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: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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& us_;
32+
MutableBufferSequence mb_;
33+
http_proto::parser& pr_;
34+
std::size_t bb_ = 0;
35+
36+
public:
37+
38+
body_read_stream_op(
39+
AsyncReadStream& s,
40+
MutableBufferSequence&& mb,
41+
http_proto::parser& pr) noexcept
42+
: us_(s)
43+
, mb_(std::move(mb))
44+
, pr_(pr)
45+
{
46+
}
47+
48+
template<class Self>
49+
void
50+
operator()(
51+
Self& self,
52+
system::error_code ec = {},
53+
std::size_t bytes_transferred = 0)
54+
{
55+
boost::ignore_unused(bytes_transferred);
56+
57+
BOOST_ASIO_CORO_REENTER(*this)
58+
{
59+
self.reset_cancellation_state(
60+
asio::enable_total_cancellation());
61+
62+
if (!pr_.got_header())
63+
{
64+
BOOST_ASIO_CORO_YIELD
65+
{
66+
BOOST_ASIO_HANDLER_LOCATION((
67+
__FILE__, __LINE__,
68+
"async_read_header"));
69+
beast2::async_read_header<
70+
AsyncReadStream,
71+
Self > (
72+
us_,
73+
pr_,
74+
std::move(self));
75+
}
76+
if (ec.failed()) goto upcall;
77+
}
78+
79+
BOOST_ASIO_CORO_YIELD
80+
{
81+
BOOST_ASIO_HANDLER_LOCATION((
82+
__FILE__, __LINE__,
83+
"async_read_some"));
84+
beast2::async_read_some(
85+
us_,
86+
pr_,
87+
std::move(self));
88+
}
89+
90+
upcall:
91+
std::size_t n = 0;
92+
93+
if (!ec.failed())
94+
{
95+
auto source_buf = pr_.pull_body();
96+
97+
n = boost::asio::buffer_copy(mb_, source_buf);
98+
99+
pr_.consume_body(n);
100+
101+
ec = (n != 0) ? system::error_code{} : asio::stream_errc::eof;
102+
}
103+
104+
self.complete(ec, n);
105+
}
106+
}
107+
};
108+
109+
} // detail
110+
111+
//------------------------------------------------
112+
113+
// TODO: copy in Beast's stream traits to check if AsyncReadStream
114+
// is an AsyncReadStream, and also static_assert that body_read_stream is too.
115+
116+
117+
118+
template<class AsyncReadStream>
119+
body_read_stream<AsyncReadStream>::body_read_stream(
120+
AsyncReadStream& und_stream
121+
, http_proto::parser& pr)
122+
:
123+
us_(und_stream)
124+
, pr_(pr)
125+
{
126+
}
127+
128+
129+
template<class AsyncReadStream>
130+
template<
131+
class MutableBufferSequence,
132+
BOOST_ASIO_COMPLETION_TOKEN_FOR(
133+
void(system::error_code, std::size_t)) CompletionToken>
134+
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
135+
void(system::error_code, std::size_t))
136+
body_read_stream<AsyncReadStream>::async_read_some(
137+
MutableBufferSequence mb
138+
, CompletionToken&& token)
139+
{
140+
return asio::async_compose<
141+
CompletionToken,
142+
void(system::error_code, std::size_t)>(
143+
detail::body_read_stream_op<
144+
MutableBufferSequence, AsyncReadStream>{us_, std::move(mb), pr_},
145+
token,
146+
us_
147+
);
148+
}
149+
150+
} // beast2
151+
} // boost
152+
153+
#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)