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