]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/beast/http/read.cpp
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 // Test that header file is self-contained.
11 #include <boost/beast/http/read.hpp>
13 #include "test_parser.hpp"
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>
30 #if BOOST_ASIO_HAS_CO_AWAIT
31 #include <boost/asio/use_awaitable.hpp>
39 : public beast::unit_test::suite
40 , public test::enable_yield_to
43 template<bool isRequest
>
45 failMatrix(char const* s
, yield_context do_yield
)
47 static std::size_t constexpr limit
= 100;
49 auto const len
= strlen(s
);
50 for(n
= 0; n
< limit
; ++n
)
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
;
64 BEAST_EXPECT(n
< limit
);
65 for(n
= 0; n
< limit
; ++n
)
67 static std::size_t constexpr pre
= 10;
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
;
81 BEAST_EXPECT(n
< limit
);
82 for(n
= 0; n
< limit
; ++n
)
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
;
92 async_read(ts
, b
, p
, do_yield
[ec
]);
96 BEAST_EXPECT(n
< limit
);
97 for(n
= 0; n
< limit
; ++n
)
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
;
107 async_read_header(ts
, b
, p
, do_yield
[ec
]);
111 BEAST_EXPECT(n
< limit
);
112 for(n
= 0; n
< limit
; ++n
)
114 static std::size_t constexpr pre
= 10;
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
;
124 async_read(ts
, b
, p
, do_yield
[ec
]);
128 BEAST_EXPECT(n
< limit
);
136 test::stream c
{ioc_
, "GET / X"};
138 request_parser
<dynamic_body
> p
;
142 catch(std::exception
const&)
152 test::stream c
{ioc_
};
153 ostream(c
.buffer()) <<
155 "Host: localhost\r\n"
156 "User-Agent: test\r\n"
157 "Transfer-Encoding: chunked\r\n"
160 "****************\r\n"
162 flat_static_buffer
<1024> b
;
163 request
<string_body
> req
;
169 catch(std::exception
const& e
)
171 fail(e
.what(), __FILE__
, __LINE__
);
175 test::stream c
{ioc_
};
176 ostream(c
.buffer()) <<
178 "Host: localhost\r\n"
179 "User-Agent: test\r\n"
180 "Transfer-Encoding: chunked\r\n"
183 "****************\r\n"
185 error_code ec
= test::error::test_failure
;
186 flat_static_buffer
<10> b
;
187 request
<string_body
> req
;
189 BEAST_EXPECTS(ec
== error::buffer_overflow
,
194 void testFailures(yield_context do_yield
)
196 char const* req
[] = {
198 "Host: localhost\r\n"
199 "User-Agent: test\r\n"
204 "Host: localhost\r\n"
205 "User-Agent: test\r\n"
206 "Content-Length: 2\r\n"
211 "Host: localhost\r\n"
212 "User-Agent: test\r\n"
213 "Transfer-Encoding: chunked\r\n"
216 "****************\r\n"
222 char const* res
[] = {
223 "HTTP/1.0 200 OK\r\n"
227 "HTTP/1.0 200 OK\r\n"
232 "HTTP/1.1 200 OK\r\n"
234 "Content-Length: 3\r\n"
238 "HTTP/1.1 200 OK\r\n"
240 "Transfer-Encoding: chunked\r\n"
243 "****************\r\n"
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
);
254 void testRead(yield_context do_yield
)
256 static std::size_t constexpr limit
= 100;
259 for(n
= 0; n
< limit
; ++n
)
261 test::fail_count fc
{n
};
262 test::stream c
{ioc_
, fc
,
264 "Host: localhost\r\n"
265 "User-Agent: test\r\n"
266 "Content-Length: 0\r\n"
269 request
<dynamic_body
> m
;
276 catch(std::exception
const&)
280 BEAST_EXPECT(n
< limit
);
282 for(n
= 0; n
< limit
; ++n
)
284 test::fail_count fc
{n
};
285 test::stream ts
{ioc_
, fc
,
287 "Host: localhost\r\n"
288 "User-Agent: test\r\n"
289 "Content-Length: 0\r\n"
292 request
<dynamic_body
> m
;
293 error_code ec
= test::error::test_failure
;
299 BEAST_EXPECT(n
< limit
);
301 for(n
= 0; n
< limit
; ++n
)
303 test::fail_count fc
{n
};
304 test::stream c
{ioc_
, fc
,
306 "Host: localhost\r\n"
307 "User-Agent: test\r\n"
308 "Content-Length: 0\r\n"
311 request
<dynamic_body
> m
;
312 error_code ec
= test::error::test_failure
;
314 async_read(c
, b
, m
, do_yield
[ec
]);
318 BEAST_EXPECT(n
< limit
);
320 for(n
= 0; n
< limit
; ++n
)
322 test::fail_count fc
{n
};
323 test::stream c
{ioc_
, fc
,
325 "Host: localhost\r\n"
326 "User-Agent: test\r\n"
327 "Content-Length: 0\r\n"
330 request_parser
<dynamic_body
> m
;
331 error_code ec
= test::error::test_failure
;
333 async_read_some(c
, b
, m
, do_yield
[ec
]);
337 BEAST_EXPECT(n
< limit
);
341 testEof(yield_context do_yield
)
345 test::stream ts
{ioc_
};
346 request_parser
<dynamic_body
> p
;
350 BEAST_EXPECT(ec
== http::error::end_of_stream
);
354 test::stream ts
{ioc_
};
355 request_parser
<dynamic_body
> p
;
358 async_read(ts
, b
, p
, do_yield
[ec
]);
359 BEAST_EXPECT(ec
== http::error::end_of_stream
);
363 // Ensure completion handlers are not leaked
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 {}
378 // Make sure handlers are not destroyed
379 // after calling io_context::stop
382 "GET / HTTP/1.1\r\n\r\n"};
383 BEAST_EXPECT(handler::count() == 0);
385 request
<dynamic_body
> m
;
386 async_read(ts
, b
, m
, handler
{});
387 BEAST_EXPECT(handler::count() > 0);
389 BEAST_EXPECT(handler::count() > 0);
391 BEAST_EXPECT(handler::count() > 0);
393 BEAST_EXPECT(handler::count() == 0);
396 // Make sure uninvoked handlers are
397 // destroyed when calling ~io_context
401 "GET / HTTP/1.1\r\n\r\n"};
402 BEAST_EXPECT(handler::count() == 0);
404 request
<dynamic_body
> m
;
405 async_read(ts
, b
, m
, handler
{});
406 BEAST_EXPECT(handler::count() > 0);
408 BEAST_EXPECT(handler::count() == 0);
412 // https://github.com/boostorg/beast/issues/430
416 test::stream ts
{ioc_
};
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"
427 response_parser
<dynamic_body
> p
;
429 BEAST_EXPECTS(! ec
, ec
.message());
432 //--------------------------------------------------------------------------
434 template<class Parser
, class Pred
>
436 readgrind(string_view s
, Pred
&& pred
)
438 for(std::size_t n
= 1; n
< s
.size() - 1; ++n
)
441 error_code ec
= test::error::test_failure
;
443 test::stream ts
{ioc_
};
444 ostream(ts
.buffer()) << s
;
447 if(! BEAST_EXPECTS(! ec
, ec
.message()))
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"
463 ,[&](test_parser
<false> const& p
)
465 BEAST_EXPECT(p
.body
== "abcd");
467 readgrind
<test_parser
<false>>(
468 "HTTP/1.1 200 OK\r\n"
470 "Expect: Expires, MD5-Fingerprint\r\n"
471 "Transfer-Encoding: chunked\r\n"
475 "2;a;b=1;c=\"2\"\r\n"
477 "0;d;e=3;f=\"4\"\r\n"
479 "MD5-Fingerprint: -\r\n"
481 ,[&](test_parser
<false> const& p
)
483 BEAST_EXPECT(p
.body
== "*****--");
487 struct copyable_handler
489 template<class... Args
>
491 operator()(Args
&&...) const
497 testAsioHandlerInvoke()
499 using strand
= net::strand
<
500 net::io_context::executor_type
>;
502 // make sure things compile, also can set a
503 // breakpoint in asio_handler_invoke to make sure
504 // it is instantiated.
507 strand s
{ioc
.get_executor()};
508 test::stream ts
{ioc
};
510 request_parser
<dynamic_body
> p
;
511 async_read_some(ts
, b
, p
,
513 s
, copyable_handler
{}));
517 strand s
{ioc
.get_executor()};
518 test::stream ts
{ioc
};
520 request_parser
<dynamic_body
> p
;
523 s
, copyable_handler
{}));
527 strand s
{ioc
.get_executor()};
528 test::stream ts
{ioc
};
530 request
<dynamic_body
> m
;
533 s
, copyable_handler
{}));
537 #if BOOST_ASIO_HAS_CO_AWAIT
538 void testAwaitableCompiles(
539 test::stream
& stream
,
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
)
546 static_assert(std::is_same_v
<
547 net::awaitable
<std::size_t>, decltype(
548 http::async_read(stream
, dynbuf
, request
, net::use_awaitable
))>);
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
))>);
554 static_assert(std::is_same_v
<
555 net::awaitable
<std::size_t>, decltype(
556 http::async_read(stream
, dynbuf
, response
, net::use_awaitable
))>);
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
))>);
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
))>);
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
))>);
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
))>);
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
))>);
580 void testReadSomeHeader(net::yield_context yield
)
583 "GET /foo HTTP/1.1" "\r\n"
584 "Connection: Keep-Alive" "\r\n"
592 // bytes_transferred returns length of header
593 request_parser
<string_body
> p
;
594 test::stream
s(ioc_
);
596 s
.append(string_view(hdr
));
597 s
.append(string_view(body
));
600 auto bt
= async_read_header(s
, fb
, p
, yield
[ec
]);
601 BEAST_EXPECTS(!ec
, ec
.message());
602 BEAST_EXPECT(bt
== hdr
.size());
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));
611 // incomplete header consumes all parsable header bytes
612 request_parser
<string_body
> p
;
613 test::stream
s(ioc_
);
615 s
.append(hdr
.substr(0, hdr
.size() - 1));
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()) +
624 std::to_string(hdr
.size() - 1));
628 // read consumes and reports correct number of bytes
629 request_parser
<string_body
> p
;
630 test::stream
s(ioc_
);
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()));
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(),
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()));
664 void testReadSomeHeader()
669 "GET /foo HTTP/1.1" "\r\n"
670 "Connection: Keep-Alive" "\r\n"
678 // bytes_transferred returns length of header
679 request_parser
<string_body
> p
;
681 s
.append(string_view(hdr
));
682 s
.append(string_view(body
));
685 auto bt
= read_header(s
, fb
, p
, ec
);
686 BEAST_EXPECTS(!ec
, ec
.message());
687 BEAST_EXPECT(bt
== hdr
.size());
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));
696 // incomplete header consumes all parsable header bytes
697 request_parser
<string_body
> p
;
700 s
.append(hdr
.substr(0, hdr
.size() - 1));
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()) +
709 std::to_string(hdr
.size() - 1));
718 testBufferOverflow();
720 yield_to([&](yield_context yield
)
724 yield_to([&](yield_context yield
)
728 yield_to([&](yield_context yield
)
736 testAsioHandlerInvoke();
737 #if BOOST_ASIO_HAS_CO_AWAIT
738 boost::ignore_unused(&read_test::testAwaitableCompiles
);
740 yield_to([&](yield_context yield
)
742 testReadSomeHeader(yield
);
744 testReadSomeHeader();
750 BEAST_DEFINE_TESTSUITE(beast
,http
,read
);