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>
19 class write_test
: public websocket_test_suite
24 doTestWrite(Wrap
const& w
)
26 using boost::asio::buffer
;
28 permessage_deflate pmd
;
29 pmd
.client_enable
= false;
30 pmd
.server_enable
= false;
35 stream
<test::stream
> ws
{ioc_
};
36 ws
.next_layer().connect(es
.stream());
37 ws
.handshake("localhost", "/");
41 w
.write(ws
, sbuf(""));
42 fail("", __FILE__
, __LINE__
);
44 catch(system_error
const& se
)
47 se
.code() == boost::asio::error::operation_aborted
,
53 doTest(pmd
, [&](ws_type
& ws
)
55 ws
.auto_fragment(false);
57 std::string
const s
= "Hello, world!";
58 w
.write(ws
, buffer(s
));
61 BEAST_EXPECT(ws
.got_text());
62 BEAST_EXPECT(to_string(b
.data()) == s
);
66 doTest(pmd
, [&](ws_type
& ws
)
69 w
.write(ws
, boost::asio::null_buffers
{});
72 BEAST_EXPECT(ws
.got_text());
73 BEAST_EXPECT(b
.size() == 0);
77 doTest(pmd
, [&](ws_type
& ws
)
79 ws
.auto_fragment(false);
81 std::string
const s
= "Hello, world!";
82 w
.write_some(ws
, false, buffer(s
.data(), 5));
83 w
.write_some(ws
, true, buffer(s
.data() + 5, s
.size() - 5));
86 BEAST_EXPECT(ws
.got_text());
87 BEAST_EXPECT(to_string(b
.data()) == s
);
91 doTest(pmd
, [&](ws_type
& ws
)
93 std::string
const s
= "Hello";
94 std::size_t const chop
= 3;
95 BOOST_ASSERT(chop
< s
.size());
96 w
.write_some(ws
, false,
97 buffer(s
.data(), chop
));
98 w
.write_some(ws
, true, buffer(
99 s
.data() + chop
, s
.size() - chop
));
102 BEAST_EXPECT(to_string(b
.data()) == s
);
106 doTest(pmd
, [&](ws_type
& ws
)
108 ws
.auto_fragment(false);
109 std::string
const s
= "Hello";
110 w
.write(ws
, buffer(s
));
113 BEAST_EXPECT(to_string(b
.data()) == s
);
117 doTest(pmd
, [&](ws_type
& ws
)
119 ws
.auto_fragment(false);
120 ws
.write_buffer_size(16);
121 std::string
const s(32, '*');
122 w
.write(ws
, buffer(s
));
125 BEAST_EXPECT(to_string(b
.data()) == s
);
129 doTest(pmd
, [&](ws_type
& ws
)
131 ws
.auto_fragment(true);
132 std::string
const s(16384, '*');
133 w
.write(ws
, buffer(s
));
136 BEAST_EXPECT(to_string(b
.data()) == s
);
140 doStreamLoop([&](test::stream
& ts
)
142 echo_server es
{log
, kind::async_client
};
144 ws
.next_layer().connect(es
.stream());
147 es
.async_handshake();
149 ws
.auto_fragment(false);
150 std::string
const s
= "Hello";
151 w
.write(ws
, buffer(s
));
154 BEAST_EXPECT(to_string(b
.data()) == s
);
166 doStreamLoop([&](test::stream
& ts
)
168 echo_server es
{log
, kind::async_client
};
170 ws
.next_layer().connect(es
.stream());
173 es
.async_handshake();
175 ws
.auto_fragment(true);
176 std::string
const s(16384, '*');
177 w
.write(ws
, buffer(s
));
180 BEAST_EXPECT(to_string(b
.data()) == s
);
191 pmd
.client_enable
= true;
192 pmd
.server_enable
= true;
196 doTest(pmd
, [&](ws_type
& ws
)
198 auto const& s
= random_string();
200 w
.write(ws
, buffer(s
));
203 BEAST_EXPECT(to_string(b
.data()) == s
);
206 // deflate, continuation
207 doTest(pmd
, [&](ws_type
& ws
)
209 std::string
const s
= "Hello";
210 std::size_t const chop
= 3;
211 BOOST_ASSERT(chop
< s
.size());
212 // This call should produce no
213 // output due to compression latency.
214 w
.write_some(ws
, false,
215 buffer(s
.data(), chop
));
216 w
.write_some(ws
, true, buffer(
217 s
.data() + chop
, s
.size() - chop
));
220 BEAST_EXPECT(to_string(b
.data()) == s
);
223 // deflate, no context takeover
224 pmd
.client_no_context_takeover
= true;
225 doTest(pmd
, [&](ws_type
& ws
)
227 auto const& s
= random_string();
229 w
.write(ws
, buffer(s
));
232 BEAST_EXPECT(to_string(b
.data()) == s
);
239 using boost::asio::buffer
;
241 doTestWrite(SyncClient
{});
243 yield_to([&](yield_context yield
)
245 doTestWrite(AsyncClient
{yield
});
252 using boost::asio::buffer
;
255 doFailLoop([&](test::fail_counter
& fc
)
258 boost::asio::io_context ioc
;
259 stream
<test::stream
> ws
{ioc
, fc
};
260 ws
.next_layer().connect(es
.stream());
261 ws
.handshake("localhost", "/");
262 std::size_t count
= 0;
268 BOOST_THROW_EXCEPTION(
271 BEAST_EXPECT(ws
.wr_block_
);
272 BEAST_EXPECT(count
== 0);
273 ws
.async_write(sbuf("*"),
274 [&](error_code ec
, std::size_t n
)
278 BOOST_THROW_EXCEPTION(
280 BEAST_EXPECT(n
== 1);
282 BEAST_EXPECT(count
== 0);
284 BEAST_EXPECT(count
== 2);
288 doFailLoop([&](test::fail_counter
& fc
)
291 boost::asio::io_context ioc
;
292 stream
<test::stream
> ws
{ioc
, fc
};
293 ws
.next_layer().connect(es
.stream());
294 ws
.handshake("localhost", "/");
295 std::size_t count
= 0;
301 BOOST_THROW_EXCEPTION(
304 BEAST_EXPECT(ws
.wr_block_
);
305 BEAST_EXPECT(count
== 0);
306 ws
.async_write(sbuf("*"),
307 [&](error_code ec
, std::size_t)
310 if(ec
!= boost::asio::error::operation_aborted
)
311 BOOST_THROW_EXCEPTION(
314 BEAST_EXPECT(count
== 0);
316 BEAST_EXPECT(count
== 2);
319 // suspend on read ping + message
320 doFailLoop([&](test::fail_counter
& fc
)
323 boost::asio::io_context ioc
;
324 stream
<test::stream
> ws
{ioc
, fc
};
325 ws
.next_layer().connect(es
.stream());
326 ws
.handshake("localhost", "/");
327 // add a ping and message to the input
328 ws
.next_layer().append(string_view
{
329 "\x89\x00" "\x81\x01*", 5});
330 std::size_t count
= 0;
333 [&](error_code ec
, std::size_t)
337 BOOST_THROW_EXCEPTION(
340 while(! ws
.wr_block_
)
343 if(! BEAST_EXPECT(! ioc
.stopped()))
346 BEAST_EXPECT(count
== 0);
347 ws
.async_write(sbuf("*"),
348 [&](error_code ec
, std::size_t n
)
352 BOOST_THROW_EXCEPTION(
354 BEAST_EXPECT(n
== 1);
356 BEAST_EXPECT(count
== 0);
358 BEAST_EXPECT(count
== 2);
361 // suspend on ping: nomask, nofrag
362 doFailLoop([&](test::fail_counter
& fc
)
364 echo_server es
{log
, kind::async_client
};
365 boost::asio::io_context ioc
;
366 stream
<test::stream
> ws
{ioc
, fc
};
367 ws
.next_layer().connect(es
.stream());
368 es
.async_handshake();
370 std::size_t count
= 0;
371 std::string
const s(16384, '*');
372 ws
.auto_fragment(false);
373 ws
.async_write(buffer(s
),
374 [&](error_code ec
, std::size_t n
)
378 BOOST_THROW_EXCEPTION(
380 BEAST_EXPECT(n
== 16384);
382 BEAST_EXPECT(ws
.wr_block_
);
388 BOOST_THROW_EXCEPTION(
392 BEAST_EXPECT(count
== 2);
395 // suspend on ping: nomask, frag
396 doFailLoop([&](test::fail_counter
& fc
)
398 echo_server es
{log
, kind::async_client
};
399 boost::asio::io_context ioc
;
400 stream
<test::stream
> ws
{ioc
, fc
};
401 ws
.next_layer().connect(es
.stream());
402 es
.async_handshake();
404 std::size_t count
= 0;
405 std::string
const s(16384, '*');
406 ws
.auto_fragment(true);
407 ws
.async_write(buffer(s
),
408 [&](error_code ec
, std::size_t n
)
412 BOOST_THROW_EXCEPTION(
414 BEAST_EXPECT(n
== 16384);
416 BEAST_EXPECT(ws
.wr_block_
);
422 BOOST_THROW_EXCEPTION(
426 BEAST_EXPECT(count
== 2);
429 // suspend on ping: mask, nofrag
430 doFailLoop([&](test::fail_counter
& fc
)
434 boost::asio::io_context ioc
;
435 stream
<test::stream
> ws
{ioc
, fc
};
436 ws
.next_layer().connect(es
.stream());
437 ws
.handshake("localhost", "/");
438 std::size_t count
= 0;
439 std::string
const s(16384, '*');
440 ws
.auto_fragment(false);
441 ws
.async_write(buffer(s
),
442 [&](error_code ec
, std::size_t n
)
446 BOOST_THROW_EXCEPTION(
448 BEAST_EXPECT(n
== 16384);
450 BEAST_EXPECT(ws
.wr_block_
);
456 BOOST_THROW_EXCEPTION(
462 // suspend on ping: mask, frag
463 doFailLoop([&](test::fail_counter
& fc
)
467 boost::asio::io_context ioc
;
468 stream
<test::stream
> ws
{ioc
, fc
};
469 ws
.next_layer().connect(es
.stream());
470 ws
.handshake("localhost", "/");
471 std::size_t count
= 0;
472 std::string
const s(16384, '*');
473 ws
.auto_fragment(true);
474 ws
.async_write(buffer(s
),
475 [&](error_code ec
, std::size_t n
)
479 BOOST_THROW_EXCEPTION(
481 BEAST_EXPECT(n
== 16384);
483 BEAST_EXPECT(ws
.wr_block_
);
489 BOOST_THROW_EXCEPTION(
495 // suspend on ping: deflate
496 doFailLoop([&](test::fail_counter
& fc
)
498 echo_server es
{log
, kind::async
};
499 boost::asio::io_context ioc
;
500 stream
<test::stream
> ws
{ioc
, fc
};
502 permessage_deflate pmd
;
503 pmd
.client_enable
= true;
507 ws
.next_layer().connect(es
.stream());
508 ws
.handshake("localhost", "/");
509 std::size_t count
= 0;
510 auto const& s
= random_string();
512 ws
.async_write(buffer(s
),
513 [&](error_code ec
, std::size_t n
)
517 BOOST_THROW_EXCEPTION(
519 BEAST_EXPECT(n
== s
.size());
521 BEAST_EXPECT(ws
.wr_block_
);
527 BOOST_THROW_EXCEPTION(
535 testAsyncWriteFrame()
537 for(int i
= 0; i
< 2; ++i
)
541 echo_server es
{log
, i
==1 ?
542 kind::async
: kind::sync
};
543 boost::asio::io_context ioc
;
544 stream
<test::stream
> ws
{ioc
};
545 ws
.next_layer().connect(es
.stream());
548 ws
.handshake("localhost", "/", ec
);
549 if(! BEAST_EXPECTS(! ec
, ec
.message()))
551 ws
.async_write_some(false,
552 boost::asio::null_buffers
{},
553 [&](error_code
, std::size_t)
557 if(! BEAST_EXPECTS(! ec
, ec
.message()))
560 // Destruction of the io_context will cause destruction
561 // of the write_some_op without invoking the final handler.
569 https://github.com/boostorg/beast/issues/300
571 Write a message as two individual frames
576 for(int i
= 0; i
< 2; ++i
)
578 echo_server es
{log
, i
==1 ?
579 kind::async
: kind::sync
};
580 boost::asio::io_context ioc
;
581 stream
<test::stream
> ws
{ioc
};
582 ws
.next_layer().connect(es
.stream());
585 ws
.handshake("localhost", "/", ec
);
586 if(! BEAST_EXPECTS(! ec
, ec
.message()))
588 ws
.write_some(false, sbuf("u"));
589 ws
.write_some(true, sbuf("v"));
592 BEAST_EXPECTS(! ec
, ec
.message());
601 void operator()(error_code
) {}
605 stream
<test::stream
> ws
{ioc_
};
606 stream
<test::stream
>::write_some_op
<
607 boost::asio::const_buffer
,
608 handler
> op
{handler
{}, ws
, true,
609 boost::asio::const_buffer
{
611 using boost::asio::asio_handler_is_continuation
;
612 asio_handler_is_continuation(&op
);
620 testAsyncWriteFrame();
626 BEAST_DEFINE_TESTSUITE(beast
,websocket
,write
);