]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/beast/http/write.cpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / beast / test / beast / http / write.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/write.hpp>
12
13 #include <boost/beast/http/buffer_body.hpp>
14 #include <boost/beast/http/empty_body.hpp>
15 #include <boost/beast/http/fields.hpp>
16 #include <boost/beast/http/message.hpp>
17 #include <boost/beast/http/read.hpp>
18 #include <boost/beast/http/string_body.hpp>
19 #include <boost/beast/core/error.hpp>
20 #include <boost/beast/core/multi_buffer.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/error.hpp>
25 #include <sstream>
26 #include <string>
27
28 namespace boost {
29 namespace beast {
30 namespace http {
31
32 class write_test
33 : public beast::unit_test::suite
34 , public test::enable_yield_to
35 {
36 public:
37 struct unsized_body
38 {
39 using value_type = std::string;
40
41 class writer
42 {
43 value_type const& body_;
44
45 public:
46 using const_buffers_type =
47 boost::asio::const_buffer;
48
49 template<bool isRequest, class Fields>
50 explicit
51 writer(message<isRequest,
52 unsized_body, Fields> const& msg)
53 : body_(msg.body())
54 {
55 }
56
57 void
58 init(error_code& ec)
59 {
60 ec.assign(0, ec.category());
61 }
62
63 boost::optional<std::pair<const_buffers_type, bool>>
64 get(error_code& ec)
65 {
66 ec.assign(0, ec.category());
67 return {{const_buffers_type{
68 body_.data(), body_.size()}, false}};
69 }
70 };
71 };
72
73 template<
74 bool isSplit,
75 bool isFinalEmpty
76 >
77 struct test_body
78 {
79 struct value_type
80 {
81 std::string s;
82 bool mutable read = false;
83 };
84
85 class writer
86 {
87 int step_ = 0;
88 value_type const& body_;
89
90 public:
91 using const_buffers_type =
92 boost::asio::const_buffer;
93
94 template<bool isRequest, class Fields>
95 explicit
96 writer(message<isRequest,
97 test_body, Fields> const& msg)
98 : body_(msg.body())
99 {
100 }
101
102 void
103 init(error_code& ec)
104 {
105 ec.assign(0, ec.category());
106 }
107
108 boost::optional<std::pair<const_buffers_type, bool>>
109 get(error_code& ec)
110 {
111 ec.assign(0, ec.category());
112 body_.read = true;
113 return get(
114 std::integral_constant<bool, isSplit>{},
115 std::integral_constant<bool, isFinalEmpty>{});
116 }
117
118 private:
119 boost::optional<std::pair<const_buffers_type, bool>>
120 get(
121 std::false_type, // isSplit
122 std::false_type) // isFinalEmpty
123 {
124 using boost::asio::buffer;
125 if(body_.s.empty())
126 return boost::none;
127 return {{buffer(body_.s.data(), body_.s.size()), false}};
128 }
129
130 boost::optional<std::pair<const_buffers_type, bool>>
131 get(
132 std::false_type, // isSplit
133 std::true_type) // isFinalEmpty
134 {
135 using boost::asio::buffer;
136 if(body_.s.empty())
137 return boost::none;
138 switch(step_)
139 {
140 case 0:
141 step_ = 1;
142 return {{buffer(
143 body_.s.data(), body_.s.size()), true}};
144 default:
145 return boost::none;
146 }
147 }
148
149 boost::optional<std::pair<const_buffers_type, bool>>
150 get(
151 std::true_type, // isSplit
152 std::false_type) // isFinalEmpty
153 {
154 using boost::asio::buffer;
155 auto const n = (body_.s.size() + 1) / 2;
156 switch(step_)
157 {
158 case 0:
159 if(n == 0)
160 return boost::none;
161 step_ = 1;
162 return {{buffer(body_.s.data(), n),
163 body_.s.size() > 1}};
164 default:
165 return {{buffer(body_.s.data() + n,
166 body_.s.size() - n), false}};
167 }
168 }
169
170 boost::optional<std::pair<const_buffers_type, bool>>
171 get(
172 std::true_type, // isSplit
173 std::true_type) // isFinalEmpty
174 {
175 using boost::asio::buffer;
176 auto const n = (body_.s.size() + 1) / 2;
177 switch(step_)
178 {
179 case 0:
180 if(n == 0)
181 return boost::none;
182 step_ = body_.s.size() > 1 ? 1 : 2;
183 return {{buffer(body_.s.data(), n), true}};
184 case 1:
185 BOOST_ASSERT(body_.s.size() > 1);
186 step_ = 2;
187 return {{buffer(body_.s.data() + n,
188 body_.s.size() - n), true}};
189 default:
190 return boost::none;
191 }
192 }
193 };
194 };
195
196 struct fail_body
197 {
198 class writer;
199
200 class value_type
201 {
202 friend class writer;
203
204 std::string s_;
205 test::fail_counter& fc_;
206
207 public:
208 explicit
209 value_type(test::fail_counter& fc)
210 : fc_(fc)
211 {
212 }
213
214 value_type&
215 operator=(std::string s)
216 {
217 s_ = std::move(s);
218 return *this;
219 }
220 };
221
222 class writer
223 {
224 std::size_t n_ = 0;
225 value_type const& body_;
226
227 public:
228 using const_buffers_type =
229 boost::asio::const_buffer;
230
231 template<bool isRequest, class Fields>
232 explicit
233 writer(message<isRequest,
234 fail_body, Fields> const& msg)
235 : body_(msg.body())
236 {
237 }
238
239 void
240 init(error_code& ec)
241 {
242 body_.fc_.fail(ec);
243 }
244
245 boost::optional<std::pair<const_buffers_type, bool>>
246 get(error_code& ec)
247 {
248 if(body_.fc_.fail(ec))
249 return boost::none;
250 if(n_ >= body_.s_.size())
251 return boost::none;
252 return {{const_buffers_type{
253 body_.s_.data() + n_++, 1}, true}};
254 }
255 };
256 };
257
258 template<bool isRequest, class Body, class Fields>
259 static
260 std::string
261 to_string(message<isRequest, Body, Fields> const& m)
262 {
263 std::stringstream ss;
264 ss << m;
265 return ss.str();
266 }
267
268 template<class ConstBufferSequence>
269 static
270 std::string
271 to_string(ConstBufferSequence const& bs)
272 {
273 std::string s;
274 s.reserve(buffer_size(bs));
275 for(auto b : beast::detail::buffers_range(bs))
276 s.append(reinterpret_cast<char const*>(b),
277 b.size());
278 return s;
279 }
280
281 template<bool isRequest>
282 bool
283 equal_body(string_view sv, string_view body)
284 {
285 test::stream ts{ioc_, sv}, tr{ioc_};
286 ts.connect(tr);
287 message<isRequest, string_body, fields> m;
288 multi_buffer b;
289 ts.close_remote();
290 try
291 {
292 read(ts, b, m);
293 return m.body() == body;
294 }
295 catch(std::exception const& e)
296 {
297 log << "equal_body: " << e.what() << std::endl;
298 return false;
299 }
300 }
301
302 template<bool isRequest, class Body, class Fields>
303 std::string
304 str(message<isRequest, Body, Fields> const& m)
305 {
306 test::stream ts{ioc_}, tr{ioc_};
307 ts.connect(tr);
308 error_code ec;
309 write(ts, m, ec);
310 if(ec && ec != error::end_of_stream)
311 BOOST_THROW_EXCEPTION(system_error{ec});
312 return tr.str().to_string();
313 }
314
315 void
316 testAsyncWrite(yield_context do_yield)
317 {
318 {
319 response<string_body> m;
320 m.version(10);
321 m.result(status::ok);
322 m.set(field::server, "test");
323 m.set(field::content_length, "5");
324 m.body() = "*****";
325 error_code ec;
326 test::stream ts{ioc_}, tr{ioc_};
327 ts.connect(tr);
328 async_write(ts, m, do_yield[ec]);
329 BEAST_EXPECT(! m.keep_alive());
330 if(BEAST_EXPECTS(! ec, ec.message()))
331 BEAST_EXPECT(tr.str() ==
332 "HTTP/1.0 200 OK\r\n"
333 "Server: test\r\n"
334 "Content-Length: 5\r\n"
335 "\r\n"
336 "*****");
337 }
338 {
339 response<string_body> m;
340 m.version(11);
341 m.result(status::ok);
342 m.set(field::server, "test");
343 m.set(field::transfer_encoding, "chunked");
344 m.body() = "*****";
345 error_code ec;
346 test::stream ts{ioc_}, tr{ioc_};
347 ts.connect(tr);
348 async_write(ts, m, do_yield[ec]);
349 if(BEAST_EXPECTS(! ec, ec.message()))
350 BEAST_EXPECT(tr.str() ==
351 "HTTP/1.1 200 OK\r\n"
352 "Server: test\r\n"
353 "Transfer-Encoding: chunked\r\n"
354 "\r\n"
355 "5\r\n"
356 "*****\r\n"
357 "0\r\n\r\n");
358 }
359 }
360
361 void
362 testFailures(yield_context do_yield)
363 {
364 static std::size_t constexpr limit = 100;
365 std::size_t n;
366
367 for(n = 0; n < limit; ++n)
368 {
369 test::fail_counter fc(n);
370 test::stream ts{ioc_, fc}, tr{ioc_};
371 ts.connect(tr);
372 request<fail_body> m(verb::get, "/", 10, fc);
373 m.set(field::user_agent, "test");
374 m.set(field::connection, "keep-alive");
375 m.set(field::content_length, "5");
376 m.body() = "*****";
377 try
378 {
379 write(ts, m);
380 BEAST_EXPECT(tr.str() ==
381 "GET / HTTP/1.0\r\n"
382 "User-Agent: test\r\n"
383 "Connection: keep-alive\r\n"
384 "Content-Length: 5\r\n"
385 "\r\n"
386 "*****"
387 );
388 pass();
389 break;
390 }
391 catch(std::exception const&)
392 {
393 }
394 }
395 BEAST_EXPECT(n < limit);
396
397 for(n = 0; n < limit; ++n)
398 {
399 test::fail_counter fc(n);
400 test::stream ts{ioc_, fc}, tr{ioc_};
401 ts.connect(tr);
402 request<fail_body> m{verb::get, "/", 10, fc};
403 m.set(field::user_agent, "test");
404 m.set(field::transfer_encoding, "chunked");
405 m.body() = "*****";
406 error_code ec = test::error::fail_error;
407 write(ts, m, ec);
408 if(! ec)
409 {
410 BEAST_EXPECT(! m.keep_alive());
411 BEAST_EXPECT(tr.str() ==
412 "GET / HTTP/1.0\r\n"
413 "User-Agent: test\r\n"
414 "Transfer-Encoding: chunked\r\n"
415 "\r\n"
416 "1\r\n*\r\n"
417 "1\r\n*\r\n"
418 "1\r\n*\r\n"
419 "1\r\n*\r\n"
420 "1\r\n*\r\n"
421 "0\r\n\r\n"
422 );
423 break;
424 }
425 }
426 BEAST_EXPECT(n < limit);
427
428 for(n = 0; n < limit; ++n)
429 {
430 test::fail_counter fc(n);
431 test::stream ts{ioc_, fc}, tr{ioc_};
432 ts.connect(tr);
433 request<fail_body> m{verb::get, "/", 10, fc};
434 m.set(field::user_agent, "test");
435 m.set(field::transfer_encoding, "chunked");
436 m.body() = "*****";
437 error_code ec = test::error::fail_error;
438 async_write(ts, m, do_yield[ec]);
439 if(! ec)
440 {
441 BEAST_EXPECT(! m.keep_alive());
442 BEAST_EXPECT(tr.str() ==
443 "GET / HTTP/1.0\r\n"
444 "User-Agent: test\r\n"
445 "Transfer-Encoding: chunked\r\n"
446 "\r\n"
447 "1\r\n*\r\n"
448 "1\r\n*\r\n"
449 "1\r\n*\r\n"
450 "1\r\n*\r\n"
451 "1\r\n*\r\n"
452 "0\r\n\r\n"
453 );
454 break;
455 }
456 }
457 BEAST_EXPECT(n < limit);
458
459 for(n = 0; n < limit; ++n)
460 {
461 test::fail_counter fc(n);
462 test::stream ts{ioc_, fc}, tr{ioc_};
463 ts.connect(tr);
464 request<fail_body> m{verb::get, "/", 10, fc};
465 m.set(field::user_agent, "test");
466 m.set(field::connection, "keep-alive");
467 m.set(field::content_length, "5");
468 m.body() = "*****";
469 error_code ec = test::error::fail_error;
470 write(ts, m, ec);
471 if(! ec)
472 {
473 BEAST_EXPECT(tr.str() ==
474 "GET / HTTP/1.0\r\n"
475 "User-Agent: test\r\n"
476 "Connection: keep-alive\r\n"
477 "Content-Length: 5\r\n"
478 "\r\n"
479 "*****"
480 );
481 break;
482 }
483 }
484 BEAST_EXPECT(n < limit);
485
486 for(n = 0; n < limit; ++n)
487 {
488 test::fail_counter fc(n);
489 test::stream ts{ioc_, fc}, tr{ioc_};
490 ts.connect(tr);
491 request<fail_body> m{verb::get, "/", 10, fc};
492 m.set(field::user_agent, "test");
493 m.set(field::connection, "keep-alive");
494 m.set(field::content_length, "5");
495 m.body() = "*****";
496 error_code ec = test::error::fail_error;
497 async_write(ts, m, do_yield[ec]);
498 if(! ec)
499 {
500 BEAST_EXPECT(tr.str() ==
501 "GET / HTTP/1.0\r\n"
502 "User-Agent: test\r\n"
503 "Connection: keep-alive\r\n"
504 "Content-Length: 5\r\n"
505 "\r\n"
506 "*****"
507 );
508 break;
509 }
510 }
511 BEAST_EXPECT(n < limit);
512 }
513
514 void
515 testOutput()
516 {
517 // auto content-length HTTP/1.0
518 {
519 request<string_body> m;
520 m.method(verb::get);
521 m.target("/");
522 m.version(10);
523 m.set(field::user_agent, "test");
524 m.body() = "*";
525 m.prepare_payload();
526 BEAST_EXPECT(str(m) ==
527 "GET / HTTP/1.0\r\n"
528 "User-Agent: test\r\n"
529 "Content-Length: 1\r\n"
530 "\r\n"
531 "*"
532 );
533 }
534 // no content-length HTTP/1.0
535 {
536 request<unsized_body> m;
537 m.method(verb::get);
538 m.target("/");
539 m.version(10);
540 m.set(field::user_agent, "test");
541 m.body() = "*";
542 m.prepare_payload();
543 test::stream ts{ioc_}, tr{ioc_};
544 ts.connect(tr);
545 error_code ec;
546 write(ts, m, ec);
547 BEAST_EXPECT(! m.keep_alive());
548 BEAST_EXPECTS(! ec, ec.message());
549 BEAST_EXPECT(tr.str() ==
550 "GET / HTTP/1.0\r\n"
551 "User-Agent: test\r\n"
552 "\r\n"
553 "*"
554 );
555 }
556 // auto content-length HTTP/1.1
557 {
558 request<string_body> m;
559 m.method(verb::get);
560 m.target("/");
561 m.version(11);
562 m.set(field::user_agent, "test");
563 m.body() = "*";
564 m.prepare_payload();
565 BEAST_EXPECT(str(m) ==
566 "GET / HTTP/1.1\r\n"
567 "User-Agent: test\r\n"
568 "Content-Length: 1\r\n"
569 "\r\n"
570 "*"
571 );
572 }
573 // no content-length HTTP/1.1
574 {
575 request<unsized_body> m;
576 m.method(verb::get);
577 m.target("/");
578 m.version(11);
579 m.set(field::user_agent, "test");
580 m.body() = "*";
581 m.prepare_payload();
582 test::stream ts{ioc_}, tr{ioc_};
583 ts.connect(tr);
584 error_code ec;
585 write(ts, m, ec);
586 BEAST_EXPECT(tr.str() ==
587 "GET / HTTP/1.1\r\n"
588 "User-Agent: test\r\n"
589 "Transfer-Encoding: chunked\r\n"
590 "\r\n"
591 "1\r\n"
592 "*\r\n"
593 "0\r\n\r\n"
594 );
595 }
596 }
597
598 void test_std_ostream()
599 {
600 // Conversion to std::string via operator<<
601 {
602 request<string_body> m;
603 m.method(verb::get);
604 m.target("/");
605 m.version(11);
606 m.set(field::user_agent, "test");
607 m.body() = "*";
608 BEAST_EXPECT(to_string(m) ==
609 "GET / HTTP/1.1\r\nUser-Agent: test\r\n\r\n*");
610 }
611
612 // Output to std::ostream
613 {
614 request<string_body> m{verb::get, "/", 11};
615 std::stringstream ss;
616 ss << m;
617 BEAST_EXPECT(ss.str() ==
618 "GET / HTTP/1.1\r\n"
619 "\r\n");
620 }
621
622 // Output header to std::ostream
623 {
624 request<string_body> m{verb::get, "/", 11};
625 std::stringstream ss;
626 ss << m.base();
627 BEAST_EXPECT(ss.str() ==
628 "GET / HTTP/1.1\r\n"
629 "\r\n");
630 }
631 }
632
633 // Ensure completion handlers are not leaked
634 struct handler
635 {
636 static std::atomic<std::size_t>&
637 count() { static std::atomic<std::size_t> n; return n; }
638 handler() { ++count(); }
639 ~handler() { --count(); }
640 handler(handler const&) { ++count(); }
641 void operator()(error_code const&, std::size_t) const {}
642 };
643
644 void
645 testIoService()
646 {
647 {
648 // Make sure handlers are not destroyed
649 // after calling io_context::stop
650 boost::asio::io_context ioc;
651 test::stream ts{ioc};
652 BEAST_EXPECT(handler::count() == 0);
653 request<string_body> m;
654 m.method(verb::get);
655 m.version(11);
656 m.target("/");
657 m.set("Content-Length", 5);
658 m.body() = "*****";
659 async_write(ts, m, handler{});
660 BEAST_EXPECT(handler::count() > 0);
661 ioc.stop();
662 BEAST_EXPECT(handler::count() > 0);
663 ioc.restart();
664 BEAST_EXPECT(handler::count() > 0);
665 ioc.run_one();
666 BEAST_EXPECT(handler::count() == 0);
667 }
668 {
669 // Make sure uninvoked handlers are
670 // destroyed when calling ~io_context
671 {
672 boost::asio::io_context ioc;
673 test::stream ts{ioc}, tr{ioc};
674 ts.connect(tr);
675 BEAST_EXPECT(handler::count() == 0);
676 request<string_body> m;
677 m.method(verb::get);
678 m.version(11);
679 m.target("/");
680 m.set("Content-Length", 5);
681 m.body() = "*****";
682 async_write(ts, m, handler{});
683 BEAST_EXPECT(handler::count() > 0);
684 }
685 BEAST_EXPECT(handler::count() == 0);
686 }
687 }
688
689 template<
690 class Stream,
691 bool isRequest, class Body, class Fields>
692 void
693 do_write(
694 Stream& stream,
695 message<isRequest, Body, Fields> const& m,
696 error_code& ec)
697 {
698 serializer<isRequest, Body, Fields> sr{m};
699 for(;;)
700 {
701 write_some(stream, sr, ec);
702 if(ec)
703 return;
704 if(sr.is_done())
705 break;
706 }
707 }
708
709 template<
710 class Stream,
711 bool isRequest, class Body, class Fields>
712 void
713 do_async_write(
714 Stream& stream,
715 message<isRequest, Body, Fields> const& m,
716 error_code& ec,
717 yield_context yield)
718 {
719 serializer<isRequest, Body, Fields> sr{m};
720 for(;;)
721 {
722 async_write_some(stream, sr, yield[ec]);
723 if(ec)
724 return;
725 if(sr.is_done())
726 break;
727 }
728 }
729
730 template<class Body>
731 void
732 testWriteStream(boost::asio::yield_context yield)
733 {
734 test::stream ts{ioc_}, tr{ioc_};
735 ts.connect(tr);
736 ts.write_size(3);
737
738 response<Body> m0;
739 m0.version(11);
740 m0.result(status::ok);
741 m0.reason("OK");
742 m0.set(field::server, "test");
743 m0.body().s = "Hello, world!\n";
744
745 {
746 std::string const result =
747 "HTTP/1.1 200 OK\r\n"
748 "Server: test\r\n"
749 "\r\n"
750 "Hello, world!\n";
751 {
752 auto m = m0;
753 error_code ec;
754 do_write(ts, m, ec);
755 BEAST_EXPECT(tr.str() == result);
756 BEAST_EXPECT(equal_body<false>(
757 tr.str(), m.body().s));
758 tr.clear();
759 }
760 {
761 auto m = m0;
762 error_code ec;
763 do_async_write(ts, m, ec, yield);
764 BEAST_EXPECT(tr.str() == result);
765 BEAST_EXPECT(equal_body<false>(
766 tr.str(), m.body().s));
767 tr.clear();
768 }
769 {
770 auto m = m0;
771 error_code ec;
772 response_serializer<Body, fields> sr{m};
773 sr.split(true);
774 for(;;)
775 {
776 write_some(ts, sr);
777 if(sr.is_header_done())
778 break;
779 }
780 BEAST_EXPECT(! m.body().read);
781 tr.clear();
782 }
783 {
784 auto m = m0;
785 error_code ec;
786 response_serializer<Body, fields> sr{m};
787 sr.split(true);
788 for(;;)
789 {
790 async_write_some(ts, sr, yield);
791 if(sr.is_header_done())
792 break;
793 }
794 BEAST_EXPECT(! m.body().read);
795 tr.clear();
796 }
797 }
798 {
799 m0.set("Transfer-Encoding", "chunked");
800 {
801 auto m = m0;
802 error_code ec;
803 do_write(ts, m, ec);
804 BEAST_EXPECT(equal_body<false>(
805 tr.str(), m.body().s));
806 tr.clear();
807 }
808 {
809 auto m = m0;
810 error_code ec;
811 do_async_write(ts, m, ec, yield);
812 BEAST_EXPECT(equal_body<false>(
813 tr.str(), m.body().s));
814 tr.clear();
815 }
816 {
817 auto m = m0;
818 error_code ec;
819 response_serializer<Body, fields> sr{m};
820 sr.split(true);
821 for(;;)
822 {
823 write_some(ts, sr);
824 if(sr.is_header_done())
825 break;
826 }
827 BEAST_EXPECT(! m.body().read);
828 tr.clear();
829 }
830 {
831 auto m = m0;
832 error_code ec;
833 response_serializer<Body, fields> sr{m};
834 sr.split(true);
835 for(;;)
836 {
837 async_write_some(ts, sr, yield);
838 if(sr.is_header_done())
839 break;
840 }
841 BEAST_EXPECT(! m.body().read);
842 tr.clear();
843 }
844 }
845 }
846
847 void
848 testIssue655()
849 {
850 boost::asio::io_context ioc;
851 test::stream ts{ioc}, tr{ioc};
852 ts.connect(tr);
853 response<empty_body> res;
854 res.chunked(true);
855 response_serializer<empty_body> sr{res};
856 async_write_header(ts, sr,
857 [&](error_code const&, std::size_t)
858 {
859 });
860 ioc.run();
861 }
862
863 void
864 run() override
865 {
866 testIssue655();
867 yield_to(
868 [&](yield_context yield)
869 {
870 testAsyncWrite(yield);
871 testFailures(yield);
872 });
873 testOutput();
874 test_std_ostream();
875 testIoService();
876 yield_to(
877 [&](yield_context yield)
878 {
879 testWriteStream<test_body<false, false>>(yield);
880 testWriteStream<test_body<false, true>>(yield);
881 testWriteStream<test_body< true, false>>(yield);
882 testWriteStream<test_body< true, true>>(yield);
883 });
884 }
885 };
886
887 BEAST_DEFINE_TESTSUITE(beast,http,write);
888
889 } // http
890 } // beast
891 } // boost