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