1//
2// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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/boostorg/beast
8//
9
10// Test that header file is self-contained.
11#include <boost/beast/http/read.hpp>
12
13#include "test_parser.hpp"
14
15#include <boost/beast/core/ostream.hpp>
16#include <boost/beast/core/flat_static_buffer.hpp>
17#include <boost/beast/http/fields.hpp>
18#include <boost/beast/http/dynamic_body.hpp>
19#include <boost/beast/http/parser.hpp>
20#include <boost/beast/http/string_body.hpp>
21#include <boost/beast/_experimental/test/stream.hpp>
22#include <boost/beast/_experimental/unit_test/suite.hpp>
23#include <boost/beast/test/yield_to.hpp>
24#include <boost/asio/io_context.hpp>
25#include <boost/asio/ip/tcp.hpp>
26#include <boost/asio/strand.hpp>
27#include <boost/asio/write.hpp>
28#include <boost/asio/bind_cancellation_slot.hpp>
29#include <boost/asio/error.hpp>
30#include <boost/asio/io_context.hpp>
31#include <boost/asio/connect_pipe.hpp>
32#include <boost/asio/readable_pipe.hpp>
33#include <boost/asio/writable_pipe.hpp>
34#include <atomic>
35
36#if BOOST_ASIO_HAS_CO_AWAIT
37#include <boost/asio/use_awaitable.hpp>
38#endif
39
40namespace boost {
41namespace beast {
42namespace http {
43
44class read_test
45 : public beast::unit_test::suite
46 , public test::enable_yield_to
47{
48public:
49 template<bool isRequest>
50 void
51 failMatrix(char const* s, yield_context do_yield)
52 {
53 static std::size_t constexpr limit = 100;
54 std::size_t n;
55 auto const len = strlen(s: s);
56 for(n = 0; n < limit; ++n)
57 {
58 multi_buffer b;
59 b.commit(n: net::buffer_copy(
60 target: b.prepare(n: len), source: net::buffer(data: s, size_in_bytes: len)));
61 test::fail_count fc(n);
62 test::stream ts{ioc_, fc};
63 test_parser<isRequest> p(fc);
64 error_code ec = test::error::test_failure;
65 ts.close_remote();
66 read(ts, b, p, ec);
67 if(! ec)
68 break;
69 }
70 BEAST_EXPECT(n < limit);
71 for(n = 0; n < limit; ++n)
72 {
73 static std::size_t constexpr pre = 10;
74 multi_buffer b;
75 b.commit(n: net::buffer_copy(
76 target: b.prepare(n: pre), source: net::buffer(data: s, size_in_bytes: pre)));
77 test::fail_count fc(n);
78 test::stream ts{ioc_, fc,
79 std::string(s + pre, len - pre)};
80 test_parser<isRequest> p(fc);
81 error_code ec = test::error::test_failure;
82 ts.close_remote();
83 read(ts, b, p, ec);
84 if(! ec)
85 break;
86 }
87 BEAST_EXPECT(n < limit);
88 for(n = 0; n < limit; ++n)
89 {
90 multi_buffer b;
91 b.commit(n: net::buffer_copy(
92 target: b.prepare(n: len), source: net::buffer(data: s, size_in_bytes: len)));
93 test::fail_count fc(n);
94 test::stream ts{ioc_, fc};
95 test_parser<isRequest> p(fc);
96 error_code ec = test::error::test_failure;
97 ts.close_remote();
98 async_read(ts, b, p, do_yield[ec]);
99 if(! ec)
100 break;
101 }
102 BEAST_EXPECT(n < limit);
103 for(n = 0; n < limit; ++n)
104 {
105 multi_buffer b;
106 b.commit(n: net::buffer_copy(
107 target: b.prepare(n: len), source: net::buffer(data: s, size_in_bytes: len)));
108 test::fail_count fc(n);
109 test::stream ts{ioc_, fc};
110 test_parser<isRequest> p(fc);
111 error_code ec = test::error::test_failure;
112 ts.close_remote();
113 async_read_header(ts, b, p, do_yield[ec]);
114 if(! ec)
115 break;
116 }
117 BEAST_EXPECT(n < limit);
118 for(n = 0; n < limit; ++n)
119 {
120 static std::size_t constexpr pre = 10;
121 multi_buffer b;
122 b.commit(n: net::buffer_copy(
123 target: b.prepare(n: pre), source: net::buffer(data: s, size_in_bytes: pre)));
124 test::fail_count fc(n);
125 test::stream ts(ioc_, fc,
126 std::string{s + pre, len - pre});
127 test_parser<isRequest> p(fc);
128 error_code ec = test::error::test_failure;
129 ts.close_remote();
130 async_read(ts, b, p, do_yield[ec]);
131 if(! ec)
132 break;
133 }
134 BEAST_EXPECT(n < limit);
135 }
136
137 void testThrow()
138 {
139 try
140 {
141 multi_buffer b;
142 test::stream c{ioc_, "GET / X"};
143 c.close_remote();
144 request_parser<dynamic_body> p;
145 read(stream&: c, buffer&: b, parser&: p);
146 fail();
147 }
148 catch(std::exception const&)
149 {
150 pass();
151 }
152 }
153
154 void
155 testBufferOverflow()
156 {
157 {
158 test::stream c{ioc_};
159 ostream(buffer&: c.buffer()) <<
160 "GET / HTTP/1.1\r\n"
161 "Host: localhost\r\n"
162 "User-Agent: test\r\n"
163 "Transfer-Encoding: chunked\r\n"
164 "\r\n"
165 "10\r\n"
166 "****************\r\n"
167 "0\r\n\r\n";
168 flat_static_buffer<1024> b;
169 request<string_body> req;
170 try
171 {
172 read(stream&: c, buffer&: b, msg&: req);
173 pass();
174 }
175 catch(std::exception const& e)
176 {
177 fail(reason: e.what(), __FILE__, __LINE__);
178 }
179 }
180 {
181 test::stream c{ioc_};
182 ostream(buffer&: c.buffer()) <<
183 "GET / HTTP/1.1\r\n"
184 "Host: localhost\r\n"
185 "User-Agent: test\r\n"
186 "Transfer-Encoding: chunked\r\n"
187 "\r\n"
188 "10\r\n"
189 "****************\r\n"
190 "0\r\n\r\n";
191 error_code ec = test::error::test_failure;
192 flat_static_buffer<10> b;
193 request<string_body> req;
194 read(stream&: c, buffer&: b, msg&: req, ec);
195 BEAST_EXPECTS(ec == error::buffer_overflow,
196 ec.message());
197 }
198 }
199
200 void testFailures(yield_context do_yield)
201 {
202 char const* req[] = {
203 "GET / HTTP/1.0\r\n"
204 "Host: localhost\r\n"
205 "User-Agent: test\r\n"
206 "Empty:\r\n"
207 "\r\n"
208 ,
209 "GET / HTTP/1.1\r\n"
210 "Host: localhost\r\n"
211 "User-Agent: test\r\n"
212 "Content-Length: 2\r\n"
213 "\r\n"
214 "**"
215 ,
216 "GET / HTTP/1.1\r\n"
217 "Host: localhost\r\n"
218 "User-Agent: test\r\n"
219 "Transfer-Encoding: chunked\r\n"
220 "\r\n"
221 "10\r\n"
222 "****************\r\n"
223 "0\r\n\r\n"
224 ,
225 nullptr
226 };
227
228 char const* res[] = {
229 "HTTP/1.0 200 OK\r\n"
230 "Server: test\r\n"
231 "\r\n"
232 ,
233 "HTTP/1.0 200 OK\r\n"
234 "Server: test\r\n"
235 "\r\n"
236 "***"
237 ,
238 "HTTP/1.1 200 OK\r\n"
239 "Server: test\r\n"
240 "Content-Length: 3\r\n"
241 "\r\n"
242 "***"
243 ,
244 "HTTP/1.1 200 OK\r\n"
245 "Server: test\r\n"
246 "Transfer-Encoding: chunked\r\n"
247 "\r\n"
248 "10\r\n"
249 "****************\r\n"
250 "0\r\n\r\n"
251 ,
252 nullptr
253 };
254 for(std::size_t i = 0; req[i]; ++i)
255 failMatrix<true>(req[i], do_yield);
256 for(std::size_t i = 0; res[i]; ++i)
257 failMatrix<false>(res[i], do_yield);
258 }
259
260 void testRead(yield_context do_yield)
261 {
262 static std::size_t constexpr limit = 100;
263 std::size_t n;
264
265 for(n = 0; n < limit; ++n)
266 {
267 test::fail_count fc{n};
268 test::stream c{ioc_, fc,
269 "GET / HTTP/1.1\r\n"
270 "Host: localhost\r\n"
271 "User-Agent: test\r\n"
272 "Content-Length: 0\r\n"
273 "\r\n"
274 };
275 request<dynamic_body> m;
276 try
277 {
278 multi_buffer b;
279 read(stream&: c, buffer&: b, msg&: m);
280 break;
281 }
282 catch(std::exception const&)
283 {
284 }
285 }
286 BEAST_EXPECT(n < limit);
287
288 for(n = 0; n < limit; ++n)
289 {
290 test::fail_count fc{n};
291 test::stream ts{ioc_, fc,
292 "GET / HTTP/1.1\r\n"
293 "Host: localhost\r\n"
294 "User-Agent: test\r\n"
295 "Content-Length: 0\r\n"
296 "\r\n"
297 };
298 request<dynamic_body> m;
299 error_code ec = test::error::test_failure;
300 multi_buffer b;
301 read(stream&: ts, buffer&: b, msg&: m, ec);
302 if(! ec)
303 break;
304 }
305 BEAST_EXPECT(n < limit);
306
307 for(n = 0; n < limit; ++n)
308 {
309 test::fail_count fc{n};
310 test::stream c{ioc_, fc,
311 "GET / HTTP/1.1\r\n"
312 "Host: localhost\r\n"
313 "User-Agent: test\r\n"
314 "Content-Length: 0\r\n"
315 "\r\n"
316 };
317 request<dynamic_body> m;
318 error_code ec = test::error::test_failure;
319 multi_buffer b;
320 async_read(c, b, m, do_yield[ec]);
321 if(! ec)
322 break;
323 }
324 BEAST_EXPECT(n < limit);
325
326 for(n = 0; n < limit; ++n)
327 {
328 test::fail_count fc{n};
329 test::stream c{ioc_, fc,
330 "GET / HTTP/1.1\r\n"
331 "Host: localhost\r\n"
332 "User-Agent: test\r\n"
333 "Content-Length: 0\r\n"
334 "\r\n"
335 };
336 request_parser<dynamic_body> m;
337 error_code ec = test::error::test_failure;
338 multi_buffer b;
339 async_read_some(c, b, m, do_yield[ec]);
340 if(! ec)
341 break;
342 }
343 BEAST_EXPECT(n < limit);
344 }
345
346 void
347 testEof(yield_context do_yield)
348 {
349 {
350 multi_buffer b;
351 test::stream ts{ioc_};
352 request_parser<dynamic_body> p;
353 error_code ec;
354 ts.close_remote();
355 read(stream&: ts, buffer&: b, parser&: p, ec);
356 BEAST_EXPECT(ec == http::error::end_of_stream);
357 }
358 {
359 multi_buffer b;
360 test::stream ts{ioc_};
361 request_parser<dynamic_body> p;
362 error_code ec;
363 ts.close_remote();
364 async_read(ts, b, p, do_yield[ec]);
365 BEAST_EXPECT(ec == http::error::end_of_stream);
366 }
367 }
368
369 // Ensure completion handlers are not leaked
370 struct handler
371 {
372 static std::atomic<std::size_t>&
373 count() { static std::atomic<std::size_t> n; return n; }
374 handler() { ++count(); }
375 ~handler() { --count(); }
376 handler(handler const&) { ++count(); }
377 void operator()(error_code const&, std::size_t) const {}
378 };
379
380 void
381 testIoService()
382 {
383 {
384 // Make sure handlers are not destroyed
385 // after calling io_context::stop
386 net::io_context ioc;
387 test::stream ts{ioc,
388 "GET / HTTP/1.1\r\n\r\n"};
389 ts.close(); // make sure the read completes
390 BEAST_EXPECT(handler::count() == 0);
391 multi_buffer b;
392 request<dynamic_body> m;
393 async_read(stream&: ts, buffer&: b, msg&: m, handler: handler{});
394 BEAST_EXPECT(handler::count() > 0);
395 ioc.stop();
396 BEAST_EXPECT(handler::count() > 0);
397 ioc.restart();
398 BEAST_EXPECT(handler::count() > 0);
399 ioc.run_one();
400 BEAST_EXPECT(handler::count() == 0);
401 }
402 {
403 // Make sure uninvoked handlers are
404 // destroyed when calling ~io_context
405 {
406 net::io_context ioc;
407 test::stream ts{ioc,
408 "GET / HTTP/1.1\r\n\r\n"};
409 BEAST_EXPECT(handler::count() == 0);
410 multi_buffer b;
411 request<dynamic_body> m;
412 async_read(stream&: ts, buffer&: b, msg&: m, handler: handler{});
413 BEAST_EXPECT(handler::count() > 0);
414 }
415 BEAST_EXPECT(handler::count() == 0);
416 }
417 }
418
419 // https://github.com/boostorg/beast/issues/430
420 void
421 testRegression430()
422 {
423 test::stream ts{ioc_};
424 ts.read_size(n: 1);
425 ostream(buffer&: ts.buffer()) <<
426 "HTTP/1.1 200 OK\r\n"
427 "Transfer-Encoding: chunked\r\n"
428 "Content-Type: application/octet-stream\r\n"
429 "\r\n"
430 "4\r\nabcd\r\n"
431 "0\r\n\r\n";
432 error_code ec;
433 flat_buffer fb;
434 response_parser<dynamic_body> p;
435 read(stream&: ts, buffer&: fb, parser&: p, ec);
436 BEAST_EXPECTS(! ec, ec.message());
437 }
438
439 //--------------------------------------------------------------------------
440
441 template<class Parser, class Pred>
442 void
443 readgrind(string_view s, Pred&& pred)
444 {
445 for(std::size_t n = 1; n < s.size() - 1; ++n)
446 {
447 Parser p;
448 error_code ec = test::error::test_failure;
449 flat_buffer b;
450 test::stream ts{ioc_};
451 ostream(buffer&: ts.buffer()) << s;
452 ts.read_size(n);
453 read(ts, b, p, ec);
454 if(! BEAST_EXPECTS(! ec, ec.message()))
455 continue;
456 pred(p);
457 }
458 }
459
460 void
461 testReadGrind()
462 {
463 readgrind<test_parser<false>>(
464 s: "HTTP/1.1 200 OK\r\n"
465 "Transfer-Encoding: chunked\r\n"
466 "Content-Type: application/octet-stream\r\n"
467 "\r\n"
468 "4\r\nabcd\r\n"
469 "0\r\n\r\n"
470 ,pred: [&](test_parser<false> const& p)
471 {
472 BEAST_EXPECT(p.body == "abcd");
473 });
474 readgrind<test_parser<false>>(
475 s: "HTTP/1.1 200 OK\r\n"
476 "Server: test\r\n"
477 "Expect: Expires, MD5-Fingerprint\r\n"
478 "Transfer-Encoding: chunked\r\n"
479 "\r\n"
480 "5\r\n"
481 "*****\r\n"
482 "2;a;b=1;c=\"2\"\r\n"
483 "--\r\n"
484 "0;d;e=3;f=\"4\"\r\n"
485 "Expires: never\r\n"
486 "MD5-Fingerprint: -\r\n"
487 "\r\n"
488 ,pred: [&](test_parser<false> const& p)
489 {
490 BEAST_EXPECT(p.body == "*****--");
491 });
492 }
493
494 struct copyable_handler
495 {
496 template<class... Args>
497 void
498 operator()(Args&&...) const
499 {
500 }
501 };
502
503 void
504 testAsioHandlerInvoke()
505 {
506 using strand = net::strand<
507 net::io_context::executor_type>;
508
509 // make sure things compile, also can set a
510 // breakpoint in asio_handler_invoke to make sure
511 // it is instantiated.
512 {
513 net::io_context ioc;
514 strand s{ioc.get_executor()};
515 test::stream ts{ioc};
516 flat_buffer b;
517 request_parser<dynamic_body> p;
518 async_read_some(stream&: ts, buffer&: b, parser&: p,
519 handler: net::bind_executor(
520 ex: s, t: copyable_handler{}));
521 }
522 {
523 net::io_context ioc;
524 strand s{ioc.get_executor()};
525 test::stream ts{ioc};
526 flat_buffer b;
527 request_parser<dynamic_body> p;
528 async_read(stream&: ts, buffer&: b, parser&: p,
529 handler: net::bind_executor(
530 ex: s, t: copyable_handler{}));
531 }
532 {
533 net::io_context ioc;
534 strand s{ioc.get_executor()};
535 test::stream ts{ioc};
536 flat_buffer b;
537 request<dynamic_body> m;
538 async_read(stream&: ts, buffer&: b, msg&: m,
539 handler: net::bind_executor(
540 ex: s, t: copyable_handler{}));
541 }
542 }
543
544#if BOOST_ASIO_HAS_CO_AWAIT
545 void testAwaitableCompiles(
546 test::stream& stream,
547 flat_buffer& dynbuf,
548 parser<true, string_body>& request_parser,
549 request<http::string_body>& request,
550 parser<false, string_body>& response_parser,
551 response<http::string_body>& response)
552 {
553 static_assert(std::is_same_v<
554 net::awaitable<std::size_t>, decltype(
555 http::async_read(stream, dynbuf, request, net::use_awaitable))>);
556
557 static_assert(std::is_same_v<
558 net::awaitable<std::size_t>, decltype(
559 http::async_read(stream, dynbuf, request_parser, net::use_awaitable))>);
560
561 static_assert(std::is_same_v<
562 net::awaitable<std::size_t>, decltype(
563 http::async_read(stream, dynbuf, response, net::use_awaitable))>);
564
565 static_assert(std::is_same_v<
566 net::awaitable<std::size_t>, decltype(
567 http::async_read(stream, dynbuf, response_parser, net::use_awaitable))>);
568
569 static_assert(std::is_same_v<
570 net::awaitable<std::size_t>, decltype(
571 http::async_read_some(stream, dynbuf, request_parser, net::use_awaitable))>);
572
573 static_assert(std::is_same_v<
574 net::awaitable<std::size_t>, decltype(
575 http::async_read_some(stream, dynbuf, response_parser, net::use_awaitable))>);
576
577 static_assert(std::is_same_v<
578 net::awaitable<std::size_t>, decltype(
579 http::async_read_header(stream, dynbuf, request_parser, net::use_awaitable))>);
580
581 static_assert(std::is_same_v<
582 net::awaitable<std::size_t>, decltype(
583 http::async_read_header(stream, dynbuf, response_parser, net::use_awaitable))>);
584 }
585#endif
586
587 void testReadSomeHeader(net::yield_context yield)
588 {
589 std::string hdr =
590 "GET /foo HTTP/1.1" "\r\n"
591 "Connection: Keep-Alive" "\r\n"
592 "Content-Length: 6"
593 "\r\n"
594 "\r\n";
595 std::string body =
596 "Hello!";
597
598 {
599 // bytes_transferred returns length of header
600 request_parser<string_body> p;
601 test::stream s(ioc_);
602
603 s.append(string_view(hdr));
604 s.append(string_view(body));
605 flat_buffer fb;
606 error_code ec;
607 auto bt = async_read_header(s, fb, p, yield[ec]);
608 BEAST_EXPECTS(!ec, ec.message());
609 BEAST_EXPECT(bt == hdr.size());
610
611 // next read should be zero-size, success
612 bt = async_read_header(s, fb, p, yield[ec]);
613 BEAST_EXPECTS(!ec, ec.message());
614 BEAST_EXPECTS(bt == 0, std::to_string(0));
615 }
616
617 {
618 // incomplete header consumes all parsable header bytes
619 request_parser<string_body> p;
620 test::stream s(ioc_);
621
622 s.append(hdr.substr(pos: 0, n: hdr.size() - 1));
623 s.close();
624 flat_buffer fb;
625 error_code ec;
626 auto bt = async_read_header(s, fb, p, yield[ec]);
627 BEAST_EXPECTS(ec == error::partial_message, ec.message());
628 BEAST_EXPECTS(bt + fb.size() == hdr.size() - 1,
629 std::to_string(bt + fb.size()) +
630 " expected " +
631 std::to_string(hdr.size() - 1));
632 }
633
634 {
635 // read consumes and reports correct number of bytes
636 request_parser<string_body> p;
637 test::stream s(ioc_);
638
639 s.append(hdr);
640 s.append(body);
641 s.append(hdr);
642 s.append(body);
643 s.append(hdr);
644 s.append(body);
645
646 flat_buffer fb;
647 error_code ec;
648 auto bt = async_read_header(s, fb, p, yield[ec]);
649 BEAST_EXPECTS("ec", ec.message());
650 BEAST_EXPECT(bt == hdr.size());
651 auto bt2 = async_read_some(s, fb, p, yield[ec]);
652 BEAST_EXPECTS(!ec, ec.message());
653 BEAST_EXPECT(bt2 == body.size());
654 BEAST_EXPECTS(fb.size() / 2 == hdr.size() + body.size(),
655 std::to_string(fb.size() / 2) + " != " + std::to_string(hdr.size() + body.size()));
656
657 request_parser<string_body> p2;
658 bt = async_read(s, fb, p2, yield[ec]);
659 BEAST_EXPECTS(!ec, ec.message());
660 BEAST_EXPECTS(bt == hdr.size() + body.size(),
661 std::to_string(bt) +
662 " expected " +
663 std::to_string(hdr.size() + body.size()));
664 BEAST_EXPECTS(fb.size() == hdr.size() + body.size(),
665 std::to_string(fb.size()) + " != " + std::to_string(hdr.size() + body.size()));
666
667
668 }
669 }
670
671 void testReadSomeHeader()
672 {
673 net::io_context ioc;
674
675 std::string hdr =
676 "GET /foo HTTP/1.1" "\r\n"
677 "Connection: Keep-Alive" "\r\n"
678 "Content-Length: 6"
679 "\r\n"
680 "\r\n";
681 std::string body =
682 "Hello!";
683
684 {
685 // bytes_transferred returns length of header
686 request_parser<string_body> p;
687 test::stream s(ioc);
688 s.append(s: string_view(hdr));
689 s.append(s: string_view(body));
690 flat_buffer fb;
691 error_code ec;
692 auto bt = read_header(stream&: s, buffer&: fb, parser&: p, ec);
693 BEAST_EXPECTS(!ec, ec.message());
694 BEAST_EXPECT(bt == hdr.size());
695
696 // next read should be zero-size, success
697 bt = read_header(stream&: s, buffer&: fb, parser&: p, ec);
698 BEAST_EXPECTS(!ec, ec.message());
699 BEAST_EXPECTS(bt == 0, std::to_string(0));
700 }
701
702 {
703 // bytes_transferred returns length of header
704 request_parser<string_body> p;
705 test::stream s(ioc);
706 s.append(s: string_view(hdr));
707 s.append(s: string_view(body));
708 std::string res ;
709 auto fb = asio::dynamic_buffer(data&: res);
710 error_code ec;
711 auto bt = read_header(stream&: s, buffer&: fb, parser&: p, ec);
712 BEAST_EXPECTS(!ec, ec.message());
713 BEAST_EXPECT(bt == hdr.size());
714
715 // next read should be zero-size, success
716 bt = read_header(stream&: s, buffer&: fb, parser&: p, ec);
717 BEAST_EXPECTS(!ec, ec.message());
718 BEAST_EXPECTS(bt == 0, std::to_string(0));
719 }
720
721 {
722 // incomplete header consumes all parsable header bytes
723 request_parser<string_body> p;
724 test::stream s(ioc);
725
726 s.append(s: hdr.substr(pos: 0, n: hdr.size() - 1));
727 s.close();
728 flat_buffer fb;
729 error_code ec;
730 auto bt = read_header(stream&: s, buffer&: fb, parser&: p, ec);
731 BEAST_EXPECTS(ec == error::partial_message, ec.message());
732 BEAST_EXPECTS(bt + fb.size() == hdr.size() - 1,
733 std::to_string(bt + fb.size()) +
734 " expected " +
735 std::to_string(hdr.size() - 1));
736 }
737 }
738
739 void
740 testCancellation(yield_context do_yield)
741 {
742 // this is tested on a pipe
743 // because the test::stream doesn't implement cancellation
744 {
745 response<string_body> m;
746 error_code ec;
747 net::writable_pipe ts{ioc_};
748 net::readable_pipe tr{ioc_};
749 net::connect_pipe(read_end&: tr, write_end&: ts);
750 net::cancellation_signal cl;
751 net::post(ioc_, [&]{cl.emit(type: net::cancellation_type::all);});
752 net::steady_timer timeout(ioc_, std::chrono::seconds(5));
753 timeout.async_wait(
754 token: [&](error_code ec)
755 {
756 BEAST_EXPECT(ec == net::error::operation_aborted);
757 if (!ec) // this means the cancel failed!
758 ts.close();
759 });
760 multi_buffer b;
761 async_read(tr, b, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
762 timeout.cancel();
763 BEAST_EXPECT(ec == net::error::operation_aborted);
764 }
765 {
766 response<string_body> m;
767 error_code ec;
768 net::writable_pipe ts{ioc_};
769 net::readable_pipe tr{ioc_};
770 net::connect_pipe(read_end&: tr, write_end&: ts);
771 net::cancellation_signal cl;
772 net::post(ioc_, [&]{cl.emit(type: net::cancellation_type::all);});
773 net::steady_timer timeout(ioc_, std::chrono::seconds(5));
774 timeout.async_wait(
775 token: [&](error_code ec)
776 {
777 // using BEAST_EXPECT HERE is a race condition, since the test suite might
778 BEAST_EXPECT(ec == net::error::operation_aborted);
779 if (!ec) // this means the cancel failed!
780 ts.close();
781 });
782 multi_buffer b;
783 async_read(tr, b, m, net::bind_cancellation_slot(cl.slot(), do_yield[ec]));
784 timeout.cancel();
785 BEAST_EXPECT(ec == net::error::operation_aborted);
786 }
787 // the timer handler may be invoked after the test suite is complete if we don't post.
788 asio::post(ioc_, do_yield);
789 }
790 void
791 run() override
792 {
793 testThrow();
794 testBufferOverflow();
795
796 yield_to([&](yield_context yield)
797 {
798 testFailures(yield);
799 });
800 yield_to([&](yield_context yield)
801 {
802 testRead(yield);
803 });
804 yield_to([&](yield_context yield)
805 {
806 testEof(yield);
807 });
808
809 testIoService();
810 testRegression430();
811 testReadGrind();
812 testAsioHandlerInvoke();
813#if BOOST_ASIO_HAS_CO_AWAIT
814 boost::ignore_unused(&read_test::testAwaitableCompiles);
815#endif
816 yield_to([&](yield_context yield)
817 {
818 testReadSomeHeader(yield);
819 });
820 testReadSomeHeader();
821 yield_to(
822 [&](yield_context yield)
823 {
824 testCancellation(yield);
825 });
826 }
827
828
829};
830
831BEAST_DEFINE_TESTSUITE(beast,http,read);
832
833} // http
834} // beast
835} // boost
836

source code of boost/libs/beast/test/beast/http/read.cpp