]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/beast/http/read.cpp
import new upstream nautilus stable release 14.2.8
[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/strand.hpp>
26 #include <boost/asio/ip/tcp.hpp>
27 #include <atomic>
28
29 namespace boost {
30 namespace beast {
31 namespace http {
32
33 class read_test
34 : public beast::unit_test::suite
35 , public test::enable_yield_to
36 {
37 public:
38 template<bool isRequest>
39 void
40 failMatrix(char const* s, yield_context do_yield)
41 {
42 static std::size_t constexpr limit = 100;
43 std::size_t n;
44 auto const len = strlen(s);
45 for(n = 0; n < limit; ++n)
46 {
47 multi_buffer b;
48 b.commit(net::buffer_copy(
49 b.prepare(len), net::buffer(s, len)));
50 test::fail_count fc(n);
51 test::stream ts{ioc_, fc};
52 test_parser<isRequest> p(fc);
53 error_code ec = test::error::test_failure;
54 ts.close_remote();
55 read(ts, b, p, ec);
56 if(! ec)
57 break;
58 }
59 BEAST_EXPECT(n < limit);
60 for(n = 0; n < limit; ++n)
61 {
62 static std::size_t constexpr pre = 10;
63 multi_buffer b;
64 b.commit(net::buffer_copy(
65 b.prepare(pre), net::buffer(s, pre)));
66 test::fail_count fc(n);
67 test::stream ts{ioc_, fc,
68 std::string(s + pre, len - pre)};
69 test_parser<isRequest> p(fc);
70 error_code ec = test::error::test_failure;
71 ts.close_remote();
72 read(ts, b, p, ec);
73 if(! ec)
74 break;
75 }
76 BEAST_EXPECT(n < limit);
77 for(n = 0; n < limit; ++n)
78 {
79 multi_buffer b;
80 b.commit(net::buffer_copy(
81 b.prepare(len), net::buffer(s, len)));
82 test::fail_count fc(n);
83 test::stream ts{ioc_, fc};
84 test_parser<isRequest> p(fc);
85 error_code ec = test::error::test_failure;
86 ts.close_remote();
87 async_read(ts, b, p, do_yield[ec]);
88 if(! ec)
89 break;
90 }
91 BEAST_EXPECT(n < limit);
92 for(n = 0; n < limit; ++n)
93 {
94 multi_buffer b;
95 b.commit(net::buffer_copy(
96 b.prepare(len), net::buffer(s, len)));
97 test::fail_count fc(n);
98 test::stream ts{ioc_, fc};
99 test_parser<isRequest> p(fc);
100 error_code ec = test::error::test_failure;
101 ts.close_remote();
102 async_read_header(ts, b, p, do_yield[ec]);
103 if(! ec)
104 break;
105 }
106 BEAST_EXPECT(n < limit);
107 for(n = 0; n < limit; ++n)
108 {
109 static std::size_t constexpr pre = 10;
110 multi_buffer b;
111 b.commit(net::buffer_copy(
112 b.prepare(pre), net::buffer(s, pre)));
113 test::fail_count fc(n);
114 test::stream ts(ioc_, fc,
115 std::string{s + pre, len - pre});
116 test_parser<isRequest> p(fc);
117 error_code ec = test::error::test_failure;
118 ts.close_remote();
119 async_read(ts, b, p, do_yield[ec]);
120 if(! ec)
121 break;
122 }
123 BEAST_EXPECT(n < limit);
124 }
125
126 void testThrow()
127 {
128 try
129 {
130 multi_buffer b;
131 test::stream c{ioc_, "GET / X"};
132 c.close_remote();
133 request_parser<dynamic_body> p;
134 read(c, b, p);
135 fail();
136 }
137 catch(std::exception const&)
138 {
139 pass();
140 }
141 }
142
143 void
144 testBufferOverflow()
145 {
146 {
147 test::stream c{ioc_};
148 ostream(c.buffer()) <<
149 "GET / HTTP/1.1\r\n"
150 "Host: localhost\r\n"
151 "User-Agent: test\r\n"
152 "Transfer-Encoding: chunked\r\n"
153 "\r\n"
154 "10\r\n"
155 "****************\r\n"
156 "0\r\n\r\n";
157 flat_static_buffer<1024> b;
158 request<string_body> req;
159 try
160 {
161 read(c, b, req);
162 pass();
163 }
164 catch(std::exception const& e)
165 {
166 fail(e.what(), __FILE__, __LINE__);
167 }
168 }
169 {
170 test::stream c{ioc_};
171 ostream(c.buffer()) <<
172 "GET / HTTP/1.1\r\n"
173 "Host: localhost\r\n"
174 "User-Agent: test\r\n"
175 "Transfer-Encoding: chunked\r\n"
176 "\r\n"
177 "10\r\n"
178 "****************\r\n"
179 "0\r\n\r\n";
180 error_code ec = test::error::test_failure;
181 flat_static_buffer<10> b;
182 request<string_body> req;
183 read(c, b, req, ec);
184 BEAST_EXPECTS(ec == error::buffer_overflow,
185 ec.message());
186 }
187 }
188
189 void testFailures(yield_context do_yield)
190 {
191 char const* req[] = {
192 "GET / HTTP/1.0\r\n"
193 "Host: localhost\r\n"
194 "User-Agent: test\r\n"
195 "Empty:\r\n"
196 "\r\n"
197 ,
198 "GET / HTTP/1.1\r\n"
199 "Host: localhost\r\n"
200 "User-Agent: test\r\n"
201 "Content-Length: 2\r\n"
202 "\r\n"
203 "**"
204 ,
205 "GET / HTTP/1.1\r\n"
206 "Host: localhost\r\n"
207 "User-Agent: test\r\n"
208 "Transfer-Encoding: chunked\r\n"
209 "\r\n"
210 "10\r\n"
211 "****************\r\n"
212 "0\r\n\r\n"
213 ,
214 nullptr
215 };
216
217 char const* res[] = {
218 "HTTP/1.0 200 OK\r\n"
219 "Server: test\r\n"
220 "\r\n"
221 ,
222 "HTTP/1.0 200 OK\r\n"
223 "Server: test\r\n"
224 "\r\n"
225 "***"
226 ,
227 "HTTP/1.1 200 OK\r\n"
228 "Server: test\r\n"
229 "Content-Length: 3\r\n"
230 "\r\n"
231 "***"
232 ,
233 "HTTP/1.1 200 OK\r\n"
234 "Server: test\r\n"
235 "Transfer-Encoding: chunked\r\n"
236 "\r\n"
237 "10\r\n"
238 "****************\r\n"
239 "0\r\n\r\n"
240 ,
241 nullptr
242 };
243 for(std::size_t i = 0; req[i]; ++i)
244 failMatrix<true>(req[i], do_yield);
245 for(std::size_t i = 0; res[i]; ++i)
246 failMatrix<false>(res[i], do_yield);
247 }
248
249 void testRead(yield_context do_yield)
250 {
251 static std::size_t constexpr limit = 100;
252 std::size_t n;
253
254 for(n = 0; n < limit; ++n)
255 {
256 test::fail_count fc{n};
257 test::stream c{ioc_, fc,
258 "GET / HTTP/1.1\r\n"
259 "Host: localhost\r\n"
260 "User-Agent: test\r\n"
261 "Content-Length: 0\r\n"
262 "\r\n"
263 };
264 request<dynamic_body> m;
265 try
266 {
267 multi_buffer b;
268 read(c, b, m);
269 break;
270 }
271 catch(std::exception const&)
272 {
273 }
274 }
275 BEAST_EXPECT(n < limit);
276
277 for(n = 0; n < limit; ++n)
278 {
279 test::fail_count fc{n};
280 test::stream ts{ioc_, fc,
281 "GET / HTTP/1.1\r\n"
282 "Host: localhost\r\n"
283 "User-Agent: test\r\n"
284 "Content-Length: 0\r\n"
285 "\r\n"
286 };
287 request<dynamic_body> m;
288 error_code ec = test::error::test_failure;
289 multi_buffer b;
290 read(ts, b, m, ec);
291 if(! ec)
292 break;
293 }
294 BEAST_EXPECT(n < limit);
295
296 for(n = 0; n < limit; ++n)
297 {
298 test::fail_count fc{n};
299 test::stream c{ioc_, fc,
300 "GET / HTTP/1.1\r\n"
301 "Host: localhost\r\n"
302 "User-Agent: test\r\n"
303 "Content-Length: 0\r\n"
304 "\r\n"
305 };
306 request<dynamic_body> m;
307 error_code ec = test::error::test_failure;
308 multi_buffer b;
309 async_read(c, b, m, do_yield[ec]);
310 if(! ec)
311 break;
312 }
313 BEAST_EXPECT(n < limit);
314
315 for(n = 0; n < limit; ++n)
316 {
317 test::fail_count fc{n};
318 test::stream c{ioc_, fc,
319 "GET / HTTP/1.1\r\n"
320 "Host: localhost\r\n"
321 "User-Agent: test\r\n"
322 "Content-Length: 0\r\n"
323 "\r\n"
324 };
325 request_parser<dynamic_body> m;
326 error_code ec = test::error::test_failure;
327 multi_buffer b;
328 async_read_some(c, b, m, do_yield[ec]);
329 if(! ec)
330 break;
331 }
332 BEAST_EXPECT(n < limit);
333 }
334
335 void
336 testEof(yield_context do_yield)
337 {
338 {
339 multi_buffer b;
340 test::stream ts{ioc_};
341 request_parser<dynamic_body> p;
342 error_code ec;
343 ts.close_remote();
344 read(ts, b, p, ec);
345 BEAST_EXPECT(ec == http::error::end_of_stream);
346 }
347 {
348 multi_buffer b;
349 test::stream ts{ioc_};
350 request_parser<dynamic_body> p;
351 error_code ec;
352 ts.close_remote();
353 async_read(ts, b, p, do_yield[ec]);
354 BEAST_EXPECT(ec == http::error::end_of_stream);
355 }
356 }
357
358 // Ensure completion handlers are not leaked
359 struct handler
360 {
361 static std::atomic<std::size_t>&
362 count() { static std::atomic<std::size_t> n; return n; }
363 handler() { ++count(); }
364 ~handler() { --count(); }
365 handler(handler const&) { ++count(); }
366 void operator()(error_code const&, std::size_t) const {}
367 };
368
369 void
370 testIoService()
371 {
372 {
373 // Make sure handlers are not destroyed
374 // after calling io_context::stop
375 net::io_context ioc;
376 test::stream ts{ioc,
377 "GET / HTTP/1.1\r\n\r\n"};
378 BEAST_EXPECT(handler::count() == 0);
379 multi_buffer b;
380 request<dynamic_body> m;
381 async_read(ts, b, m, handler{});
382 BEAST_EXPECT(handler::count() > 0);
383 ioc.stop();
384 BEAST_EXPECT(handler::count() > 0);
385 ioc.restart();
386 BEAST_EXPECT(handler::count() > 0);
387 ioc.run_one();
388 BEAST_EXPECT(handler::count() == 0);
389 }
390 {
391 // Make sure uninvoked handlers are
392 // destroyed when calling ~io_context
393 {
394 net::io_context ioc;
395 test::stream ts{ioc,
396 "GET / HTTP/1.1\r\n\r\n"};
397 BEAST_EXPECT(handler::count() == 0);
398 multi_buffer b;
399 request<dynamic_body> m;
400 async_read(ts, b, m, handler{});
401 BEAST_EXPECT(handler::count() > 0);
402 }
403 BEAST_EXPECT(handler::count() == 0);
404 }
405 }
406
407 // https://github.com/boostorg/beast/issues/430
408 void
409 testRegression430()
410 {
411 test::stream ts{ioc_};
412 ts.read_size(1);
413 ostream(ts.buffer()) <<
414 "HTTP/1.1 200 OK\r\n"
415 "Transfer-Encoding: chunked\r\n"
416 "Content-Type: application/octet-stream\r\n"
417 "\r\n"
418 "4\r\nabcd\r\n"
419 "0\r\n\r\n";
420 error_code ec;
421 flat_buffer fb;
422 response_parser<dynamic_body> p;
423 read(ts, fb, p, ec);
424 BEAST_EXPECTS(! ec, ec.message());
425 }
426
427 //--------------------------------------------------------------------------
428
429 template<class Parser, class Pred>
430 void
431 readgrind(string_view s, Pred&& pred)
432 {
433 for(std::size_t n = 1; n < s.size() - 1; ++n)
434 {
435 Parser p;
436 error_code ec = test::error::test_failure;
437 flat_buffer b;
438 test::stream ts{ioc_};
439 ostream(ts.buffer()) << s;
440 ts.read_size(n);
441 read(ts, b, p, ec);
442 if(! BEAST_EXPECTS(! ec, ec.message()))
443 continue;
444 pred(p);
445 }
446 }
447
448 void
449 testReadGrind()
450 {
451 readgrind<test_parser<false>>(
452 "HTTP/1.1 200 OK\r\n"
453 "Transfer-Encoding: chunked\r\n"
454 "Content-Type: application/octet-stream\r\n"
455 "\r\n"
456 "4\r\nabcd\r\n"
457 "0\r\n\r\n"
458 ,[&](test_parser<false> const& p)
459 {
460 BEAST_EXPECT(p.body == "abcd");
461 });
462 readgrind<test_parser<false>>(
463 "HTTP/1.1 200 OK\r\n"
464 "Server: test\r\n"
465 "Expect: Expires, MD5-Fingerprint\r\n"
466 "Transfer-Encoding: chunked\r\n"
467 "\r\n"
468 "5\r\n"
469 "*****\r\n"
470 "2;a;b=1;c=\"2\"\r\n"
471 "--\r\n"
472 "0;d;e=3;f=\"4\"\r\n"
473 "Expires: never\r\n"
474 "MD5-Fingerprint: -\r\n"
475 "\r\n"
476 ,[&](test_parser<false> const& p)
477 {
478 BEAST_EXPECT(p.body == "*****--");
479 });
480 }
481
482 struct copyable_handler
483 {
484 template<class... Args>
485 void
486 operator()(Args&&...) const
487 {
488 }
489 };
490
491 void
492 testAsioHandlerInvoke()
493 {
494 using strand = net::strand<
495 net::io_context::executor_type>;
496
497 // make sure things compile, also can set a
498 // breakpoint in asio_handler_invoke to make sure
499 // it is instantiated.
500 {
501 net::io_context ioc;
502 strand s{ioc.get_executor()};
503 test::stream ts{ioc};
504 flat_buffer b;
505 request_parser<dynamic_body> p;
506 async_read_some(ts, b, p,
507 net::bind_executor(
508 s, copyable_handler{}));
509 }
510 {
511 net::io_context ioc;
512 strand s{ioc.get_executor()};
513 test::stream ts{ioc};
514 flat_buffer b;
515 request_parser<dynamic_body> p;
516 async_read(ts, b, p,
517 net::bind_executor(
518 s, copyable_handler{}));
519 }
520 {
521 net::io_context ioc;
522 strand s{ioc.get_executor()};
523 test::stream ts{ioc};
524 flat_buffer b;
525 request<dynamic_body> m;
526 async_read(ts, b, m,
527 net::bind_executor(
528 s, copyable_handler{}));
529 }
530 }
531
532 void
533 run() override
534 {
535 testThrow();
536 testBufferOverflow();
537
538 yield_to([&](yield_context yield)
539 {
540 testFailures(yield);
541 });
542 yield_to([&](yield_context yield)
543 {
544 testRead(yield);
545 });
546 yield_to([&](yield_context yield)
547 {
548 testEof(yield);
549 });
550
551 testIoService();
552 testRegression430();
553 testReadGrind();
554 testAsioHandlerInvoke();
555 }
556 };
557
558 BEAST_DEFINE_TESTSUITE(beast,http,read);
559
560 } // http
561 } // beast
562 } // boost