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