]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/beast/http/read.cpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / beast / test / beast / http / read.cpp
1 //
2 // Copyright (c) 2016-2017 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/test/stream.hpp>
22 #include <boost/beast/test/yield_to.hpp>
23 #include <boost/beast/unit_test/suite.hpp>
24 #include <boost/asio/spawn.hpp>
25 #include <atomic>
26
27 namespace boost {
28 namespace beast {
29 namespace http {
30
31 class read_test
32 : public beast::unit_test::suite
33 , public test::enable_yield_to
34 {
35 public:
36 template<bool isRequest>
37 void
38 failMatrix(char const* s, yield_context do_yield)
39 {
40 using boost::asio::buffer;
41 using boost::asio::buffer_copy;
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(buffer_copy(
49 b.prepare(len), buffer(s, len)));
50 test::fail_counter fc(n);
51 test::stream ts{ioc_, fc};
52 test_parser<isRequest> p(fc);
53 error_code ec = test::error::fail_error;
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(buffer_copy(
65 b.prepare(pre), buffer(s, pre)));
66 test::fail_counter 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::fail_error;
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(buffer_copy(
81 b.prepare(len), buffer(s, len)));
82 test::fail_counter fc(n);
83 test::stream ts{ioc_, fc};
84 test_parser<isRequest> p(fc);
85 error_code ec = test::error::fail_error;
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(buffer_copy(
96 b.prepare(len), buffer(s, len)));
97 test::fail_counter fc(n);
98 test::stream ts{ioc_, fc};
99 test_parser<isRequest> p(fc);
100 error_code ec = test::error::fail_error;
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(buffer_copy(
112 b.prepare(pre), buffer(s, pre)));
113 test::fail_counter 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::fail_error;
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::fail_error;
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_counter 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_counter 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::fail_error;
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_counter 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::fail_error;
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_counter 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::fail_error;
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 boost::asio::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 boost::asio::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 using boost::asio::buffer;
434 for(std::size_t n = 1; n < s.size() - 1; ++n)
435 {
436 Parser p;
437 error_code ec = test::error::fail_error;
438 flat_buffer b;
439 test::stream ts{ioc_};
440 ostream(ts.buffer()) << s;
441 ts.read_size(n);
442 read(ts, b, p, ec);
443 if(! BEAST_EXPECTS(! ec, ec.message()))
444 continue;
445 pred(p);
446 }
447 }
448
449 void
450 testReadGrind()
451 {
452 readgrind<test_parser<false>>(
453 "HTTP/1.1 200 OK\r\n"
454 "Transfer-Encoding: chunked\r\n"
455 "Content-Type: application/octet-stream\r\n"
456 "\r\n"
457 "4\r\nabcd\r\n"
458 "0\r\n\r\n"
459 ,[&](test_parser<false> const& p)
460 {
461 BEAST_EXPECT(p.body == "abcd");
462 });
463 readgrind<test_parser<false>>(
464 "HTTP/1.1 200 OK\r\n"
465 "Server: test\r\n"
466 "Expect: Expires, MD5-Fingerprint\r\n"
467 "Transfer-Encoding: chunked\r\n"
468 "\r\n"
469 "5\r\n"
470 "*****\r\n"
471 "2;a;b=1;c=\"2\"\r\n"
472 "--\r\n"
473 "0;d;e=3;f=\"4\"\r\n"
474 "Expires: never\r\n"
475 "MD5-Fingerprint: -\r\n"
476 "\r\n"
477 ,[&](test_parser<false> const& p)
478 {
479 BEAST_EXPECT(p.body == "*****--");
480 });
481 }
482
483 void
484 run() override
485 {
486 testThrow();
487 testBufferOverflow();
488
489 yield_to([&](yield_context yield)
490 {
491 testFailures(yield);
492 });
493 yield_to([&](yield_context yield)
494 {
495 testRead(yield);
496 });
497 yield_to([&](yield_context yield)
498 {
499 testEof(yield);
500 });
501
502 testIoService();
503 testRegression430();
504 testReadGrind();
505 }
506 };
507
508 BEAST_DEFINE_TESTSUITE(beast,http,read);
509
510 } // http
511 } // beast
512 } // boost