2 // Copyright (w) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 // Test that header file is self-contained.
11 #include <boost/beast/websocket/stream.hpp>
13 #include <boost/asio/io_service.hpp>
14 #include <boost/asio/strand.hpp>
22 class write_test
: public websocket_test_suite
25 template<bool deflateSupported
, class Wrap
>
27 doTestWrite(Wrap
const& w
)
29 using boost::asio::buffer
;
31 permessage_deflate pmd
;
32 pmd
.client_enable
= false;
33 pmd
.server_enable
= false;
38 stream
<test::stream
> ws
{ioc_
};
39 ws
.next_layer().connect(es
.stream());
40 ws
.handshake("localhost", "/");
44 w
.write(ws
, sbuf(""));
45 fail("", __FILE__
, __LINE__
);
47 catch(system_error
const& se
)
50 se
.code() == boost::asio::error::operation_aborted
,
56 doTest
<deflateSupported
>(pmd
,
57 [&](ws_type_t
<deflateSupported
>& ws
)
59 ws
.auto_fragment(false);
61 std::string
const s
= "Hello, world!";
62 w
.write(ws
, buffer(s
));
65 BEAST_EXPECT(ws
.got_text());
66 BEAST_EXPECT(to_string(b
.data()) == s
);
70 doTest
<deflateSupported
>(pmd
,
71 [&](ws_type_t
<deflateSupported
>& ws
)
74 w
.write(ws
, boost::asio::const_buffer
{});
77 BEAST_EXPECT(ws
.got_text());
78 BEAST_EXPECT(b
.size() == 0);
82 doTest
<deflateSupported
>(pmd
,
83 [&](ws_type_t
<deflateSupported
>& ws
)
85 ws
.auto_fragment(false);
87 std::string
const s
= "Hello, world!";
88 w
.write_some(ws
, false, buffer(s
.data(), 5));
89 w
.write_some(ws
, true, buffer(s
.data() + 5, s
.size() - 5));
92 BEAST_EXPECT(ws
.got_text());
93 BEAST_EXPECT(to_string(b
.data()) == s
);
97 doTest
<deflateSupported
>(pmd
,
98 [&](ws_type_t
<deflateSupported
>& ws
)
100 std::string
const s
= "Hello";
101 std::size_t const chop
= 3;
102 BOOST_ASSERT(chop
< s
.size());
103 w
.write_some(ws
, false,
104 buffer(s
.data(), chop
));
105 w
.write_some(ws
, true, buffer(
106 s
.data() + chop
, s
.size() - chop
));
109 BEAST_EXPECT(to_string(b
.data()) == s
);
113 doTest
<deflateSupported
>(pmd
,
114 [&](ws_type_t
<deflateSupported
>& ws
)
116 ws
.auto_fragment(false);
117 std::string
const s
= "Hello";
118 w
.write(ws
, buffer(s
));
121 BEAST_EXPECT(to_string(b
.data()) == s
);
125 doTest
<deflateSupported
>(pmd
,
126 [&](ws_type_t
<deflateSupported
>& ws
)
128 ws
.auto_fragment(false);
129 ws
.write_buffer_size(16);
130 std::string
const s(32, '*');
131 w
.write(ws
, buffer(s
));
134 BEAST_EXPECT(to_string(b
.data()) == s
);
138 doTest
<deflateSupported
>(pmd
,
139 [&](ws_type_t
<deflateSupported
>& ws
)
141 ws
.auto_fragment(true);
142 std::string
const s(16384, '*');
143 w
.write(ws
, buffer(s
));
146 BEAST_EXPECT(to_string(b
.data()) == s
);
150 doStreamLoop([&](test::stream
& ts
)
152 echo_server es
{log
, kind::async_client
};
153 ws_type_t
<deflateSupported
> ws
{ts
};
154 ws
.next_layer().connect(es
.stream());
157 es
.async_handshake();
159 ws
.auto_fragment(false);
160 std::string
const s
= "Hello";
161 w
.write(ws
, buffer(s
));
164 BEAST_EXPECT(to_string(b
.data()) == s
);
176 doStreamLoop([&](test::stream
& ts
)
178 echo_server es
{log
, kind::async_client
};
179 ws_type_t
<deflateSupported
> ws
{ts
};
180 ws
.next_layer().connect(es
.stream());
183 es
.async_handshake();
185 ws
.auto_fragment(true);
186 std::string
const s(16384, '*');
187 w
.write(ws
, buffer(s
));
190 BEAST_EXPECT(to_string(b
.data()) == s
);
204 doTestWriteDeflate(Wrap
const& w
)
206 using boost::asio::buffer
;
208 permessage_deflate pmd
;
209 pmd
.client_enable
= true;
210 pmd
.server_enable
= true;
214 doTest(pmd
, [&](ws_type
& ws
)
216 auto const& s
= random_string();
218 w
.write(ws
, buffer(s
));
221 BEAST_EXPECT(to_string(b
.data()) == s
);
224 // deflate, continuation
225 doTest(pmd
, [&](ws_type
& ws
)
227 std::string
const s
= "Hello";
228 std::size_t const chop
= 3;
229 BOOST_ASSERT(chop
< s
.size());
230 // This call should produce no
231 // output due to compression latency.
232 w
.write_some(ws
, false,
233 buffer(s
.data(), chop
));
234 w
.write_some(ws
, true, buffer(
235 s
.data() + chop
, s
.size() - chop
));
238 BEAST_EXPECT(to_string(b
.data()) == s
);
241 // deflate, no context takeover
242 pmd
.client_no_context_takeover
= true;
243 doTest(pmd
, [&](ws_type
& ws
)
245 auto const& s
= random_string();
247 w
.write(ws
, buffer(s
));
250 BEAST_EXPECT(to_string(b
.data()) == s
);
257 using boost::asio::buffer
;
259 doTestWrite
<false>(SyncClient
{});
260 doTestWrite
<true>(SyncClient
{});
261 doTestWriteDeflate(SyncClient
{});
263 yield_to([&](yield_context yield
)
265 doTestWrite
<false>(AsyncClient
{yield
});
266 doTestWrite
<true>(AsyncClient
{yield
});
267 doTestWriteDeflate(AsyncClient
{yield
});
274 using boost::asio::buffer
;
277 doFailLoop([&](test::fail_counter
& fc
)
280 boost::asio::io_context ioc
;
281 stream
<test::stream
> ws
{ioc
, fc
};
282 ws
.next_layer().connect(es
.stream());
283 ws
.handshake("localhost", "/");
284 std::size_t count
= 0;
290 BOOST_THROW_EXCEPTION(
293 BEAST_EXPECT(ws
.wr_block_
.is_locked());
294 BEAST_EXPECT(count
== 0);
295 ws
.async_write(sbuf("*"),
296 [&](error_code ec
, std::size_t n
)
300 BOOST_THROW_EXCEPTION(
302 BEAST_EXPECT(n
== 1);
304 BEAST_EXPECT(count
== 0);
306 BEAST_EXPECT(count
== 2);
310 doFailLoop([&](test::fail_counter
& fc
)
313 boost::asio::io_context ioc
;
314 stream
<test::stream
> ws
{ioc
, fc
};
315 ws
.next_layer().connect(es
.stream());
316 ws
.handshake("localhost", "/");
317 std::size_t count
= 0;
323 BOOST_THROW_EXCEPTION(
326 BEAST_EXPECT(ws
.wr_block_
.is_locked());
327 BEAST_EXPECT(count
== 0);
328 ws
.async_write(sbuf("*"),
329 [&](error_code ec
, std::size_t)
332 if(ec
!= boost::asio::error::operation_aborted
)
333 BOOST_THROW_EXCEPTION(
336 BEAST_EXPECT(count
== 0);
338 BEAST_EXPECT(count
== 2);
341 // suspend on read ping + message
342 doFailLoop([&](test::fail_counter
& fc
)
345 boost::asio::io_context ioc
;
346 stream
<test::stream
> ws
{ioc
, fc
};
347 ws
.next_layer().connect(es
.stream());
348 ws
.handshake("localhost", "/");
349 // add a ping and message to the input
350 ws
.next_layer().append(string_view
{
351 "\x89\x00" "\x81\x01*", 5});
352 std::size_t count
= 0;
355 [&](error_code ec
, std::size_t)
359 BOOST_THROW_EXCEPTION(
362 while(! ws
.wr_block_
.is_locked())
365 if(! BEAST_EXPECT(! ioc
.stopped()))
368 BEAST_EXPECT(count
== 0);
369 ws
.async_write(sbuf("*"),
370 [&](error_code ec
, std::size_t n
)
374 BOOST_THROW_EXCEPTION(
376 BEAST_EXPECT(n
== 1);
378 BEAST_EXPECT(count
== 0);
380 BEAST_EXPECT(count
== 2);
383 // suspend on ping: nomask, nofrag
384 doFailLoop([&](test::fail_counter
& fc
)
386 echo_server es
{log
, kind::async_client
};
387 boost::asio::io_context ioc
;
388 stream
<test::stream
> ws
{ioc
, fc
};
389 ws
.next_layer().connect(es
.stream());
390 es
.async_handshake();
392 std::size_t count
= 0;
393 std::string
const s(16384, '*');
394 ws
.auto_fragment(false);
395 ws
.async_write(buffer(s
),
396 [&](error_code ec
, std::size_t n
)
400 BOOST_THROW_EXCEPTION(
402 BEAST_EXPECT(n
== 16384);
404 BEAST_EXPECT(ws
.wr_block_
.is_locked());
410 BOOST_THROW_EXCEPTION(
414 BEAST_EXPECT(count
== 2);
417 // suspend on ping: nomask, frag
418 doFailLoop([&](test::fail_counter
& fc
)
420 echo_server es
{log
, kind::async_client
};
421 boost::asio::io_context ioc
;
422 stream
<test::stream
> ws
{ioc
, fc
};
423 ws
.next_layer().connect(es
.stream());
424 es
.async_handshake();
426 std::size_t count
= 0;
427 std::string
const s(16384, '*');
428 ws
.auto_fragment(true);
429 ws
.async_write(buffer(s
),
430 [&](error_code ec
, std::size_t n
)
434 BOOST_THROW_EXCEPTION(
436 BEAST_EXPECT(n
== 16384);
438 BEAST_EXPECT(ws
.wr_block_
.is_locked());
444 BOOST_THROW_EXCEPTION(
448 BEAST_EXPECT(count
== 2);
451 // suspend on ping: mask, nofrag
452 doFailLoop([&](test::fail_counter
& fc
)
456 boost::asio::io_context ioc
;
457 stream
<test::stream
> ws
{ioc
, fc
};
458 ws
.next_layer().connect(es
.stream());
459 ws
.handshake("localhost", "/");
460 std::size_t count
= 0;
461 std::string
const s(16384, '*');
462 ws
.auto_fragment(false);
463 ws
.async_write(buffer(s
),
464 [&](error_code ec
, std::size_t n
)
468 BOOST_THROW_EXCEPTION(
470 BEAST_EXPECT(n
== 16384);
472 BEAST_EXPECT(ws
.wr_block_
.is_locked());
478 BOOST_THROW_EXCEPTION(
484 // suspend on ping: mask, frag
485 doFailLoop([&](test::fail_counter
& fc
)
489 boost::asio::io_context ioc
;
490 stream
<test::stream
> ws
{ioc
, fc
};
491 ws
.next_layer().connect(es
.stream());
492 ws
.handshake("localhost", "/");
493 std::size_t count
= 0;
494 std::string
const s(16384, '*');
495 ws
.auto_fragment(true);
496 ws
.async_write(buffer(s
),
497 [&](error_code ec
, std::size_t n
)
501 BOOST_THROW_EXCEPTION(
503 BEAST_EXPECT(n
== 16384);
505 BEAST_EXPECT(ws
.wr_block_
.is_locked());
511 BOOST_THROW_EXCEPTION(
517 // suspend on ping: deflate
518 doFailLoop([&](test::fail_counter
& fc
)
520 echo_server es
{log
, kind::async
};
521 boost::asio::io_context ioc
;
522 stream
<test::stream
> ws
{ioc
, fc
};
524 permessage_deflate pmd
;
525 pmd
.client_enable
= true;
529 ws
.next_layer().connect(es
.stream());
530 ws
.handshake("localhost", "/");
531 std::size_t count
= 0;
532 auto const& s
= random_string();
534 ws
.async_write(buffer(s
),
535 [&](error_code ec
, std::size_t n
)
539 BOOST_THROW_EXCEPTION(
541 BEAST_EXPECT(n
== s
.size());
543 BEAST_EXPECT(ws
.wr_block_
.is_locked());
549 BOOST_THROW_EXCEPTION(
557 testAsyncWriteFrame()
559 for(int i
= 0; i
< 2; ++i
)
563 echo_server es
{log
, i
==1 ?
564 kind::async
: kind::sync
};
565 boost::asio::io_context ioc
;
566 stream
<test::stream
> ws
{ioc
};
567 ws
.next_layer().connect(es
.stream());
570 ws
.handshake("localhost", "/", ec
);
571 if(! BEAST_EXPECTS(! ec
, ec
.message()))
573 ws
.async_write_some(false,
574 boost::asio::const_buffer
{},
575 [&](error_code
, std::size_t)
579 if(! BEAST_EXPECTS(! ec
, ec
.message()))
582 // Destruction of the io_context will cause destruction
583 // of the write_some_op without invoking the final handler.
591 https://github.com/boostorg/beast/issues/300
593 Write a message as two individual frames
598 for(int i
= 0; i
< 2; ++i
)
600 echo_server es
{log
, i
==1 ?
601 kind::async
: kind::sync
};
602 boost::asio::io_context ioc
;
603 stream
<test::stream
> ws
{ioc
};
604 ws
.next_layer().connect(es
.stream());
607 ws
.handshake("localhost", "/", ec
);
608 if(! BEAST_EXPECTS(! ec
, ec
.message()))
610 ws
.write_some(false, sbuf("u"));
611 ws
.write_some(true, sbuf("v"));
614 BEAST_EXPECTS(! ec
, ec
.message());
623 void operator()(error_code
) {}
627 stream
<test::stream
> ws
{ioc_
};
628 stream
<test::stream
>::write_some_op
<
629 boost::asio::const_buffer
,
630 handler
> op
{handler
{}, ws
, true,
631 boost::asio::const_buffer
{
633 using boost::asio::asio_handler_is_continuation
;
634 asio_handler_is_continuation(&op
);
640 boost::asio::io_context ioc
;
641 stream
<test::stream
> ws
{ioc
};
643 true, boost::asio::const_buffer
{},
644 move_only_handler
{});
647 struct copyable_handler
649 template<class... Args
>
651 operator()(Args
&&...) const
657 testAsioHandlerInvoke()
659 // make sure things compile, also can set a
660 // breakpoint in asio_handler_invoke to make sure
661 // it is instantiated.
663 boost::asio::io_context ioc
;
664 boost::asio::io_service::strand s
{ioc
};
665 stream
<test::stream
> ws
{ioc
};
667 ws
.async_write(boost::asio::const_buffer
{},
668 s
.wrap(copyable_handler
{}));
677 testAsyncWriteFrame();
684 BEAST_DEFINE_TESTSUITE(beast
,websocket
,write
);