Skip to content

Commit 0b9dd61

Browse files
committed
add unit tests for read operations
1 parent 980ceab commit 0b9dd61

File tree

5 files changed

+390
-79
lines changed

5 files changed

+390
-79
lines changed

include/boost/beast2/impl/read.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3+
// Copyright (c) 2025 Mohammad Nejati
34
//
45
// Distributed under the Boost Software License, Version 1.0. (See accompanying
56
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -62,7 +63,15 @@ class read_until_op
6263
{
6364
pr_.parse(ec);
6465
if(ec == http_proto::condition::need_more_input)
66+
{
67+
// specific to http_io::async_read_some
68+
if(total_bytes_ != 0 && condition_(pr_))
69+
{
70+
ec = {};
71+
goto upcall;
72+
}
6573
break;
74+
}
6675
if(ec.failed() || condition_(pr_))
6776
{
6877
if(total_bytes_ == 0)

include/boost/beast2/read.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3+
// Copyright (c) 2025 Mohammad Nejati
34
//
45
// Distributed under the Boost Software License, Version 1.0. (See accompanying
56
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

include/boost/beast2/test/impl/stream.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include <boost/asio/dispatch.hpp>
2020
#include <boost/asio/executor_work_guard.hpp>
2121
#include <boost/asio/post.hpp>
22-
#include <boost/make_shared.hpp>
2322

2423
#include <mutex>
2524

@@ -420,7 +419,7 @@ basic_stream(
420419
{
421420
in_->b.commit(asio::buffer_copy(
422421
in_->b.prepare(s.size()),
423-
asio::buffer(s.data(), s.size())));
422+
asio::const_buffer(s.data(), s.size())));
424423
}
425424

426425
template<class Executor>
@@ -433,7 +432,7 @@ basic_stream(
433432
{
434433
in_->b.commit(asio::buffer_copy(
435434
in_->b.prepare(s.size()),
436-
asio::buffer(s.data(), s.size())));
435+
asio::const_buffer(s.data(), s.size())));
437436
}
438437

439438
template<class Executor>
@@ -472,7 +471,7 @@ append(core::string_view s)
472471
std::lock_guard<std::mutex> lock{in_->m};
473472
in_->b.commit(asio::buffer_copy(
474473
in_->b.prepare(s.size()),
475-
asio::buffer(s.data(), s.size())));
474+
asio::const_buffer(s.data(), s.size())));
476475
}
477476

478477
template<class Executor>

test/unit/read.cpp

Lines changed: 178 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3+
// Copyright (c) 2025 Mohammad Nejati
34
//
45
// Distributed under the Boost Software License, Version 1.0. (See accompanying
56
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,99 +11,201 @@
1011
// Test that header file is self-contained.
1112
#include <boost/beast2/read.hpp>
1213

14+
#include <boost/beast2/test/stream.hpp>
15+
#include <boost/asio/bind_immediate_executor.hpp>
1316
#include <boost/asio/io_context.hpp>
14-
#include <boost/asio/post.hpp>
17+
#include <boost/buffers/copy.hpp>
18+
#include <boost/buffers/make_buffer.hpp>
19+
#include <boost/rts/context.hpp>
1520

16-
#include "test_suite.hpp"
21+
#include "test_helpers.hpp"
1722

1823
namespace boost {
1924
namespace beast2 {
2025

21-
#if 0
22-
23-
auto read_some( Stream&, parser& );
24-
auto read_some( Stream&, parser&, DynamicBuffer& );
25-
auto read( Stream&, parser& );
26-
auto read( Stream&, parser&, DynamicBuffer& );
27-
28-
29-
//--------------------------------------------
30-
31-
read( s, p ); // read message
32-
33-
p.header(); // header
34-
p.body(); // decoded body
35-
36-
//--------------------------------------------
37-
38-
read_some( s, p ); // read header
39-
if( ! p.is_complete() )
40-
read( s, p ); // read body
41-
42-
p.header(); // header
43-
p.body(); // decoded body
44-
45-
//--------------------------------------------
46-
47-
read_some( s, p ); // read header
48-
read( s, p, b ); // read body into b
49-
50-
p.header(); // header
51-
b; // decoded body
52-
53-
//--------------------------------------------
54-
55-
read_some( s, p, b ); // read header, some body
56-
if( ! p.is_complete() )
57-
read( s, p, b ); // read body into b
58-
else
59-
// (avoid immediate completion)
60-
61-
p.header(); // header
62-
b; // decoded body
63-
64-
//--------------------------------------------
65-
66-
read_some( s, p ); // read header
67-
if( ! p.is_complete() )
68-
read( s, p, b ); // read body into b
69-
else if( ! p.body().empty() )
70-
p.append_body( b ); // not an I/O
71-
72-
p.header(); // header
73-
b; // decoded body
74-
75-
//--------------------------------------------
76-
77-
read( s, p, ec ); // read header, some body
78-
if( ec == error::buffer_full )
79-
ec = {};
80-
if( ! ec.failed() )
81-
{
82-
process( p,body() );
83-
p.discard_body();
84-
}
85-
86-
#endif
87-
8826
class read_test
8927
{
28+
core::string_view const msg =
29+
"HTTP/1.1 200 OK\r\n"
30+
"Content-Length: 3\r\n"
31+
"\r\n"
32+
"abc";
9033
public:
9134
void
92-
testRead()
35+
testAsyncReadSome()
9336
{
9437
boost::asio::io_context ioc;
95-
boost::asio::post(
96-
ioc.get_executor(),
97-
[]
38+
rts::context rts_ctx;
39+
http_proto::install_parser_service(rts_ctx, {});
40+
41+
// async_read_some completes when the parser reads
42+
// the header section of the message.
43+
{
44+
test::stream ts(ioc, msg);
45+
http_proto::response_parser pr(rts_ctx);
46+
pr.reset();
47+
pr.start();
48+
49+
// limit async_read_some for better coverage
50+
ts.read_size(1);
51+
52+
// header
53+
async_read_some(
54+
ts,
55+
pr,
56+
[&](system::error_code ec, std::size_t n)
57+
{
58+
BOOST_TEST(! ec.failed());
59+
BOOST_TEST_EQ(n, msg.size() - 3); // minus body
60+
});
61+
test::run(ioc);
62+
BOOST_TEST(pr.got_header());
63+
BOOST_TEST(! pr.is_complete());
64+
65+
// body
66+
for(auto i = 0; i < 3; i++)
9867
{
99-
});
68+
async_read_some(
69+
ts,
70+
pr,
71+
[&](system::error_code ec, std::size_t n)
72+
{
73+
BOOST_TEST(! ec.failed());
74+
BOOST_TEST_EQ(n, 1); // because of ts.read_size(1)
75+
});
76+
BOOST_TEST_EQ(test::run(ioc), 1);
77+
}
78+
BOOST_TEST(pr.is_complete());
79+
BOOST_TEST(pr.body() == "abc");
80+
}
81+
82+
// async_read_some reports stream errors
83+
{
84+
test::fail_count fc(11, asio::error::network_down);
85+
test::stream ts(ioc, fc, msg);
86+
http_proto::response_parser pr(rts_ctx);
87+
pr.reset();
88+
pr.start();
89+
90+
// limit async_read_some for better coverage
91+
ts.read_size(1);
92+
93+
bool invoked = false;
94+
async_read_some(
95+
ts,
96+
pr,
97+
[&](system::error_code ec, std::size_t n)
98+
{
99+
invoked = true;
100+
BOOST_TEST_EQ(ec, asio::error::network_down);
101+
BOOST_TEST_EQ(n, 10);
102+
});
103+
BOOST_TEST_EQ(test::run(ioc), 11);
104+
BOOST_TEST(invoked);
105+
}
106+
107+
// async_read_some reports parser errors
108+
{
109+
test::stream ts(ioc, msg);
110+
http_proto::response_parser pr(rts_ctx);
111+
pr.reset();
112+
pr.start();
113+
114+
// read header
115+
async_read_some(ts, pr, test::success_handler());
116+
test::run(ioc);
117+
118+
// read body
119+
pr.set_body_limit(2);
120+
async_read_some(
121+
ts,
122+
pr,
123+
test::fail_handler(http_proto::error::body_too_large));
124+
test::run(ioc);
125+
}
126+
}
127+
128+
void
129+
testAsyncReadHeader()
130+
{
131+
// currently, async_read_header and
132+
// async_read_some are identical
133+
}
134+
135+
void
136+
testAsyncRead()
137+
{
138+
boost::asio::io_context ioc;
139+
rts::context rts_ctx;
140+
http_proto::install_parser_service(rts_ctx, {});
141+
142+
// async_read completes when the parser reads
143+
// the entire message.
144+
{
145+
test::stream ts(ioc, msg);
146+
http_proto::response_parser pr(rts_ctx);
147+
pr.reset();
148+
pr.start();
149+
150+
// limit async_read_some for better coverage
151+
ts.read_size(1);
152+
153+
async_read(
154+
ts,
155+
pr,
156+
[&](system::error_code ec, std::size_t n)
157+
{
158+
BOOST_TEST(! ec.failed());
159+
BOOST_TEST_EQ(n, msg.size());
160+
});
161+
162+
test::run(ioc);
163+
164+
BOOST_TEST_EQ(ts.nread(), msg.size()); // because of ts.read_size(1)
165+
BOOST_TEST(pr.is_complete());
166+
BOOST_TEST(pr.body() == "abc");
167+
}
168+
169+
// async_read completes immediatly when
170+
// parser contains enough data
171+
{
172+
asio::post(
173+
ioc,
174+
[&]()
175+
{
176+
test::stream ts(ioc);
177+
http_proto::response_parser pr(rts_ctx);
178+
pr.reset();
179+
pr.start();
180+
181+
pr.commit(
182+
buffers::copy(
183+
pr.prepare(),
184+
buffers::const_buffer(
185+
msg.data(),
186+
msg.size())));
187+
188+
async_read(
189+
ts,
190+
pr,
191+
asio::bind_immediate_executor(
192+
ioc.get_executor(),
193+
test::success_handler()));
194+
195+
BOOST_TEST_EQ(ts.nread(), 0);
196+
BOOST_TEST(pr.is_complete());
197+
BOOST_TEST(pr.body() == "abc");
198+
});
199+
BOOST_TEST_EQ(test::run(ioc), 1);
200+
}
100201
}
101202

102203
void
103204
run()
104205
{
105-
testRead();
206+
testAsyncReadSome();
207+
testAsyncReadHeader();
208+
testAsyncRead();
106209
}
107210
};
108211

0 commit comments

Comments
 (0)