2 // Copyright (c) 2016-2019 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/beast/_experimental/test/stream.hpp>
14 #include <boost/beast/_experimental/test/tcp.hpp>
18 #include <boost/asio/io_context.hpp>
19 #include <boost/asio/strand.hpp>
21 #if BOOST_ASIO_HAS_CO_AWAIT
22 #include <boost/asio/use_awaitable.hpp>
29 class handshake_test
: public websocket_test_suite
34 doTestHandshake(Wrap
const& w
)
41 req_decorator(req_decorator
const&) = default;
44 req_decorator(bool& b
)
50 operator()(request_type
&) const
57 doStreamLoop([&](test::stream
& ts
)
61 ws
.next_layer().connect(es
.stream());
64 w
.handshake(ws
, "localhost", "/");
74 // handshake, response
75 doStreamLoop([&](test::stream
& ts
)
79 ws
.next_layer().connect(es
.stream());
83 w
.handshake(ws
, res
, "localhost", "/");
84 // VFALCO validate res?
94 // handshake, decorator
95 doStreamLoop([&](test::stream
& ts
)
99 ws
.next_layer().connect(es
.stream());
103 ws
.set_option(stream_base::decorator(
104 req_decorator
{called
}));
105 w
.handshake(ws
, "localhost", "/");
106 BEAST_EXPECT(called
);
116 // handshake, response, decorator
117 doStreamLoop([&](test::stream
& ts
)
121 ws
.next_layer().connect(es
.stream());
126 ws
.set_option(stream_base::decorator(
127 req_decorator
{called
}));
128 w
.handshake(ws
, res
, "localhost", "/");
129 // VFALCO validate res?
130 BEAST_EXPECT(called
);
144 doTestHandshake(SyncClient
{});
146 yield_to([&](yield_context yield
)
148 doTestHandshake(AsyncClient
{yield
});
152 [&](error e
, std::string
const& s
)
154 stream
<test::stream
> ws
{ioc_
};
155 auto tr
= connect(ws
.next_layer());
156 ws
.next_layer().append(s
);
160 ws
.handshake("localhost:80", "/");
163 catch(system_error
const& se
)
165 BEAST_EXPECTS(se
.code() == e
, se
.what());
169 check(error::bad_http_version
,
170 "HTTP/1.0 101 Switching Protocols\r\n"
172 "Upgrade: WebSocket\r\n"
173 "Connection: upgrade\r\n"
174 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
175 "Sec-WebSocket-Version: 13\r\n"
179 check(error::no_connection
,
180 "HTTP/1.1 101 Switching Protocols\r\n"
182 "Upgrade: WebSocket\r\n"
183 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
184 "Sec-WebSocket-Version: 13\r\n"
187 // no Connection upgrade
188 check(error::no_connection_upgrade
,
189 "HTTP/1.1 101 Switching Protocols\r\n"
191 "Upgrade: WebSocket\r\n"
192 "Connection: keep-alive\r\n"
193 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
194 "Sec-WebSocket-Version: 13\r\n"
198 check(error::no_upgrade
,
199 "HTTP/1.1 101 Switching Protocols\r\n"
201 "Connection: upgrade\r\n"
202 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
203 "Sec-WebSocket-Version: 13\r\n"
206 // no Upgrade websocket
207 check(error::no_upgrade_websocket
,
208 "HTTP/1.1 101 Switching Protocols\r\n"
210 "Upgrade: HTTP/2\r\n"
211 "Connection: upgrade\r\n"
212 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
213 "Sec-WebSocket-Version: 13\r\n"
216 // no Sec-WebSocket-Accept
217 check(error::no_sec_accept
,
218 "HTTP/1.1 101 Switching Protocols\r\n"
220 "Upgrade: WebSocket\r\n"
221 "Connection: upgrade\r\n"
222 "Sec-WebSocket-Version: 13\r\n"
225 // bad Sec-WebSocket-Accept
226 check(error::bad_sec_accept
,
227 "HTTP/1.1 101 Switching Protocols\r\n"
229 "Upgrade: WebSocket\r\n"
230 "Connection: upgrade\r\n"
231 "Sec-WebSocket-Accept: *\r\n"
232 "Sec-WebSocket-Version: 13\r\n"
236 check(error::upgrade_declined
,
237 "HTTP/1.1 200 OK\r\n"
239 "Upgrade: WebSocket\r\n"
240 "Connection: upgrade\r\n"
241 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
242 "Sec-WebSocket-Version: 13\r\n"
247 // Compression Extensions for WebSocket
249 // https://tools.ietf.org/html/rfc7692
254 detail::pmd_offer po
;
260 f
.set(http::field::sec_websocket_extensions
, s
);
261 po
= detail::pmd_offer();
262 detail::pmd_read(po
, f
);
263 BEAST_EXPECT(po
.accept
);
270 f
.set(http::field::sec_websocket_extensions
, s
);
271 po
= detail::pmd_offer();
272 detail::pmd_read(po
, f
);
273 BEAST_EXPECT(! po
.accept
);
276 // duplicate parameters
277 reject("permessage-deflate; server_max_window_bits=8; server_max_window_bits=8");
280 reject("permessage-deflate; server_max_window_bits");
281 reject("permessage-deflate; server_max_window_bits=");
284 reject("permessage-deflate; server_max_window_bits=-1");
285 reject("permessage-deflate; server_max_window_bits=7");
286 reject("permessage-deflate; server_max_window_bits=16");
287 reject("permessage-deflate; server_max_window_bits=999999999999999999999999");
288 reject("permessage-deflate; server_max_window_bits=9a");
290 // duplicate parameters
291 reject("permessage-deflate; client_max_window_bits=8; client_max_window_bits=8");
293 // optional value excluded
294 accept("permessage-deflate; client_max_window_bits");
295 BEAST_EXPECT(po
.client_max_window_bits
== -1);
296 accept("permessage-deflate; client_max_window_bits=");
297 BEAST_EXPECT(po
.client_max_window_bits
== -1);
300 reject("permessage-deflate; client_max_window_bits=-1");
301 reject("permessage-deflate; client_max_window_bits=7");
302 reject("permessage-deflate; client_max_window_bits=16");
303 reject("permessage-deflate; client_max_window_bits=999999999999999999999999");
305 // duplicate parameters
306 reject("permessage-deflate; server_no_context_takeover; server_no_context_takeover");
308 // valueless parameter
309 accept("permessage-deflate; server_no_context_takeover");
310 BEAST_EXPECT(po
.server_no_context_takeover
);
311 accept("permessage-deflate; server_no_context_takeover=");
312 BEAST_EXPECT(po
.server_no_context_takeover
);
315 reject("permessage-deflate; server_no_context_takeover=-1");
316 reject("permessage-deflate; server_no_context_takeover=x");
317 reject("permessage-deflate; server_no_context_takeover=\"yz\"");
318 reject("permessage-deflate; server_no_context_takeover=999999999999999999999999");
320 // duplicate parameters
321 reject("permessage-deflate; client_no_context_takeover; client_no_context_takeover");
323 // valueless parameter
324 accept("permessage-deflate; client_no_context_takeover");
325 BEAST_EXPECT(po
.client_no_context_takeover
);
326 accept("permessage-deflate; client_no_context_takeover=");
327 BEAST_EXPECT(po
.client_no_context_takeover
);
330 reject("permessage-deflate; client_no_context_takeover=-1");
331 reject("permessage-deflate; client_no_context_takeover=x");
332 reject("permessage-deflate; client_no_context_takeover=\"yz\"");
333 reject("permessage-deflate; client_no_context_takeover=999999999999999999999999");
335 // unknown extension parameter
336 reject("permessage-deflate; unknown");
337 reject("permessage-deflate; unknown=");
338 reject("permessage-deflate; unknown=1");
339 reject("permessage-deflate; unknown=x");
340 reject("permessage-deflate; unknown=\"xy\"");
346 detail::pmd_offer po
;
349 [&](string_view match
)
352 detail::pmd_write(f
, po
);
354 f
[http::field::sec_websocket_extensions
]
359 po
.server_max_window_bits
= 0;
360 po
.client_max_window_bits
= 0;
361 po
.server_no_context_takeover
= false;
362 po
.client_no_context_takeover
= false;
364 check("permessage-deflate");
366 po
.server_max_window_bits
= 10;
367 check("permessage-deflate; server_max_window_bits=10");
369 po
.server_max_window_bits
= -1;
370 check("permessage-deflate; server_max_window_bits");
372 po
.server_max_window_bits
= 0;
373 po
.client_max_window_bits
= 10;
374 check("permessage-deflate; client_max_window_bits=10");
376 po
.client_max_window_bits
= -1;
377 check("permessage-deflate; client_max_window_bits");
379 po
.client_max_window_bits
= 0;
380 po
.server_no_context_takeover
= true;
381 check("permessage-deflate; server_no_context_takeover");
383 po
.server_no_context_takeover
= false;
384 po
.client_no_context_takeover
= true;
385 check("permessage-deflate; client_no_context_takeover");
391 permessage_deflate pmd
;
397 detail::pmd_offer po
;
400 f
.set(http::field::sec_websocket_extensions
, offer
);
401 detail::pmd_read(po
, f
);
404 detail::pmd_offer config
;
405 detail::pmd_negotiate(f
, config
, po
, pmd
);
406 BEAST_EXPECT(! config
.accept
);
414 detail::pmd_offer po
;
417 f
.set(http::field::sec_websocket_extensions
, offer
);
418 detail::pmd_read(po
, f
);
421 detail::pmd_offer config
;
422 detail::pmd_negotiate(f
, config
, po
, pmd
);
424 f
[http::field::sec_websocket_extensions
];
425 BEAST_EXPECTS(got
== result
, got
);
427 detail::pmd_offer poc
;
428 detail::pmd_read(poc
, f
);
429 detail::pmd_normalize(poc
);
430 BEAST_EXPECT(poc
.accept
);
432 BEAST_EXPECT(config
.server_max_window_bits
!= 0);
433 BEAST_EXPECT(config
.client_max_window_bits
!= 0);
436 pmd
.server_enable
= true;
437 pmd
.server_max_window_bits
= 15;
438 pmd
.client_max_window_bits
= 15;
439 pmd
.server_no_context_takeover
= false;
440 pmd
.client_no_context_takeover
= false;
444 "permessage-deflate",
445 "permessage-deflate");
447 // non-default server_max_window_bits
449 "permessage-deflate; server_max_window_bits=14",
450 "permessage-deflate; server_max_window_bits=14");
452 // explicit default server_max_window_bits
454 "permessage-deflate; server_max_window_bits=15",
455 "permessage-deflate");
457 // minimum window size of 8 bits (a zlib bug)
459 "permessage-deflate; server_max_window_bits=8",
460 "permessage-deflate; server_max_window_bits=9");
462 // non-default server_max_window_bits setting
463 pmd
.server_max_window_bits
= 10;
465 "permessage-deflate",
466 "permessage-deflate; server_max_window_bits=10");
468 // clamped server_max_window_bits setting #1
469 pmd
.server_max_window_bits
= 10;
471 "permessage-deflate; server_max_window_bits=14",
472 "permessage-deflate; server_max_window_bits=10");
474 // clamped server_max_window_bits setting #2
475 pmd
.server_max_window_bits
=8;
477 "permessage-deflate; server_max_window_bits=14",
478 "permessage-deflate; server_max_window_bits=9");
480 pmd
.server_max_window_bits
= 15;
482 // present with no value
484 "permessage-deflate; client_max_window_bits",
485 "permessage-deflate");
487 // present with no value, non-default setting
488 pmd
.client_max_window_bits
= 10;
490 "permessage-deflate; client_max_window_bits",
491 "permessage-deflate; client_max_window_bits=10");
493 // absent, non-default setting
494 pmd
.client_max_window_bits
= 10;
496 "permessage-deflate");
503 stream
<test::stream
> ws
{ioc
};
504 ws
.async_handshake("", "", move_only_handler
{});
507 struct copyable_handler
509 template<class... Args
>
511 operator()(Args
&&...) const
519 using tcp
= net::ip::tcp
;
523 // success, no timeout
526 stream
<tcp::socket
> ws1(ioc
);
527 stream
<tcp::socket
> ws2(ioc
);
528 test::connect(ws1
.next_layer(), ws2
.next_layer());
530 ws1
.async_handshake("test", "/", test::success_handler());
531 ws2
.async_accept(test::success_handler());
532 test::run_for(ioc
, std::chrono::seconds(1));
536 stream
<test::stream
> ws1(ioc
);
537 stream
<test::stream
> ws2(ioc
);
538 test::connect(ws1
.next_layer(), ws2
.next_layer());
540 ws1
.async_handshake("test", "/", test::success_handler());
541 ws2
.async_accept(test::success_handler());
542 test::run_for(ioc
, std::chrono::seconds(1));
545 // success, timeout enabled
548 stream
<tcp::socket
> ws1(ioc
);
549 stream
<tcp::socket
> ws2(ioc
);
550 test::connect(ws1
.next_layer(), ws2
.next_layer());
552 ws1
.set_option(stream_base::timeout
{
553 std::chrono::milliseconds(50),
556 ws1
.async_handshake("test", "/", test::success_handler());
557 ws2
.async_accept(test::success_handler());
558 test::run_for(ioc
, std::chrono::seconds(1));
562 stream
<test::stream
> ws1(ioc
);
563 stream
<test::stream
> ws2(ioc
);
564 test::connect(ws1
.next_layer(), ws2
.next_layer());
566 ws1
.set_option(stream_base::timeout
{
567 std::chrono::milliseconds(50),
570 ws1
.async_handshake("test", "/", test::success_handler());
571 ws2
.async_accept(test::success_handler());
572 test::run_for(ioc
, std::chrono::seconds(1));
578 stream
<tcp::socket
> ws1(ioc
);
579 stream
<tcp::socket
> ws2(ioc
);
580 test::connect(ws1
.next_layer(), ws2
.next_layer());
582 ws1
.set_option(stream_base::timeout
{
583 std::chrono::milliseconds(50),
586 ws1
.async_handshake("test", "/",
587 test::fail_handler(beast::error::timeout
));
588 test::run_for(ioc
, std::chrono::seconds(1));
592 stream
<test::stream
> ws1(ioc
);
593 stream
<test::stream
> ws2(ioc
);
594 test::connect(ws1
.next_layer(), ws2
.next_layer());
596 ws1
.set_option(stream_base::timeout
{
597 std::chrono::milliseconds(50),
600 ws1
.async_handshake("test", "/",
601 test::fail_handler(beast::error::timeout
));
602 test::run_for(ioc
, std::chrono::seconds(1));
605 // abandoned operation
609 stream
<tcp::socket
> ws1(ioc
);
610 ws1
.async_handshake("test", "/",
612 net::error::operation_aborted
));
618 // https://github.com/boostorg/beast/issues/1460
623 auto const make_big
= [](response_type
& res
)
625 res
.insert("Date", "Mon, 18 Feb 2019 12:48:36 GMT");
626 res
.insert("Set-Cookie",
627 "__cfduid=de1e209833e7f05aaa1044c6d448994761550494116; "
628 "expires=Tue, 18-Feb-20 12:48:36 GMT; path=/; domain=.cryptofacilities.com; HttpOnly; Secure");
629 res
.insert("Feature-Policy",
630 "accelerometer 'none'; ambient-light-sensor 'none'; "
631 "animations 'none'; autoplay 'none'; camera 'none'; document-write 'none'; "
632 "encrypted-media 'none'; geolocation 'none'; gyroscope 'none'; legacy-image-formats 'none'; "
633 "magnetometer 'none'; max-downscaling-image 'none'; microphone 'none'; midi 'none'; "
634 "payment 'none'; picture-in-picture 'none'; unsized-media 'none'; usb 'none'; vr 'none'");
635 res
.insert("Referrer-Policy", "origin");
636 res
.insert("Strict-Transport-Security", "max-age=15552000; includeSubDomains; preload");
637 res
.insert("X-Content-Type-Options", "nosniff");
638 res
.insert("Content-Security-Policy",
639 "default-src 'none'; manifest-src 'self'; object-src 'self'; "
640 "child-src 'self' https://www.google.com; "
641 "font-src 'self' https://use.typekit.net https://maxcdn.bootstrapcdn.com https://fonts.gstatic.com data:; "
642 "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ajax.cloudflare.com https://use.typekit.net "
643 "https://www.googletagmanager.com https://www.google-analytics.com https://www.google.com https://www.googleadservices.com "
644 "https://googleads.g.doubleclick.net https://www.gstatic.com; connect-src 'self' wss://*.cryptofacilities.com/ws/v1 wss://*.cryptofacilities.com/ws/indices "
645 "https://uat.cryptofacilities.com https://uat.cf0.io wss://*.cf0.io https://www.googletagmanager.com https://www.google-analytics.com https://www.google.com "
646 "https://fonts.googleapis.com https://google-analytics.com https://use.typekit.net https://p.typekit.net https://fonts.gstatic.com https://www.gstatic.com "
647 "https://chart.googleapis.com; worker-src 'self'; img-src 'self' https://chart.googleapis.com https://p.typekit.net https://www.google.co.uk https://www.google.com "
648 "https://www.google-analytics.com https://stats.g.doubleclick.net data:; style-src 'self' 'unsafe-inline' https://use.typekit.net https://p.typekit.net "
649 "https://fonts.googleapis.com https://maxcdn.bootstrapcdn.com");
650 res
.insert("X-Frame-Options", "SAMEORIGIN");
651 res
.insert("X-Xss-Protection", "1; mode=block");
652 res
.insert("Expect-CT", "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"");
653 res
.insert("Server", "cloudflare");
654 res
.insert("CF-RAY", "4ab09be1a9d0cb06-ARN");
656 "****************************************************************************************************"
657 "****************************************************************************************************"
658 "****************************************************************************************************"
659 "****************************************************************************************************"
660 "****************************************************************************************************"
661 "****************************************************************************************************"
662 "****************************************************************************************************"
663 "****************************************************************************************************"
664 "****************************************************************************************************"
665 "****************************************************************************************************"
666 "****************************************************************************************************"
667 "****************************************************************************************************"
668 "****************************************************************************************************"
669 "****************************************************************************************************"
670 "****************************************************************************************************"
671 "****************************************************************************************************"
672 "****************************************************************************************************"
673 "****************************************************************************************************"
674 "****************************************************************************************************"
675 "****************************************************************************************************");
679 stream
<test::stream
> ws1(ioc
);
680 stream
<test::stream
> ws2(ioc
);
681 test::connect(ws1
.next_layer(), ws2
.next_layer());
683 ws2
.set_option(stream_base::decorator(make_big
));
685 ws2
.async_accept(test::success_handler());
692 ws1
.handshake("test", "/", ec
);
693 BEAST_EXPECTS(! ec
, ec
.message());
698 stream
<test::stream
> ws1(ioc
);
699 stream
<test::stream
> ws2(ioc
);
700 test::connect(ws1
.next_layer(), ws2
.next_layer());
702 ws2
.set_option(stream_base::decorator(make_big
));
703 ws2
.async_accept(test::success_handler());
704 ws1
.async_handshake("test", "/", test::success_handler());
710 #if BOOST_ASIO_HAS_CO_AWAIT
711 void testAwaitableCompiles(
712 stream
<test::stream
>& s
,
717 static_assert(std::is_same_v
<
718 net::awaitable
<void>, decltype(
719 s
.async_handshake(host
, port
, net::use_awaitable
))>);
721 static_assert(std::is_same_v
<
722 net::awaitable
<void>, decltype(
723 s
.async_handshake(resp
, host
, port
, net::use_awaitable
))>);
737 #if BOOST_ASIO_HAS_CO_AWAIT
738 boost::ignore_unused(&handshake_test::testAwaitableCompiles
);
743 BEAST_DEFINE_TESTSUITE(beast
,websocket
,handshake
);