Skip to content

Commit d7fdade

Browse files
committed
body_read_stream implementation
1 parent daf37da commit d7fdade

File tree

5 files changed

+649
-1
lines changed

5 files changed

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