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>
26 class handshake_test
: public websocket_test_suite
31 doTestHandshake(Wrap
const& w
)
38 req_decorator(req_decorator
const&) = default;
41 req_decorator(bool& b
)
47 operator()(request_type
&) const
54 doStreamLoop([&](test::stream
& ts
)
58 ws
.next_layer().connect(es
.stream());
61 w
.handshake(ws
, "localhost", "/");
71 // handshake, response
72 doStreamLoop([&](test::stream
& ts
)
76 ws
.next_layer().connect(es
.stream());
80 w
.handshake(ws
, res
, "localhost", "/");
81 // VFALCO validate res?
91 // handshake, decorator
92 doStreamLoop([&](test::stream
& ts
)
96 ws
.next_layer().connect(es
.stream());
100 ws
.set_option(stream_base::decorator(
101 req_decorator
{called
}));
102 w
.handshake(ws
, "localhost", "/");
103 BEAST_EXPECT(called
);
113 // handshake, response, decorator
114 doStreamLoop([&](test::stream
& ts
)
118 ws
.next_layer().connect(es
.stream());
123 ws
.set_option(stream_base::decorator(
124 req_decorator
{called
}));
125 w
.handshake(ws
, res
, "localhost", "/");
126 // VFALCO validate res?
127 BEAST_EXPECT(called
);
141 doTestHandshake(SyncClient
{});
143 yield_to([&](yield_context yield
)
145 doTestHandshake(AsyncClient
{yield
});
149 [&](error e
, std::string
const& s
)
151 stream
<test::stream
> ws
{ioc_
};
152 auto tr
= connect(ws
.next_layer());
153 ws
.next_layer().append(s
);
157 ws
.handshake("localhost:80", "/");
160 catch(system_error
const& se
)
162 BEAST_EXPECTS(se
.code() == e
, se
.what());
166 check(error::bad_http_version
,
167 "HTTP/1.0 101 Switching Protocols\r\n"
169 "Upgrade: WebSocket\r\n"
170 "Connection: upgrade\r\n"
171 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
172 "Sec-WebSocket-Version: 13\r\n"
176 check(error::no_connection
,
177 "HTTP/1.1 101 Switching Protocols\r\n"
179 "Upgrade: WebSocket\r\n"
180 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
181 "Sec-WebSocket-Version: 13\r\n"
184 // no Connection upgrade
185 check(error::no_connection_upgrade
,
186 "HTTP/1.1 101 Switching Protocols\r\n"
188 "Upgrade: WebSocket\r\n"
189 "Connection: keep-alive\r\n"
190 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
191 "Sec-WebSocket-Version: 13\r\n"
195 check(error::no_upgrade
,
196 "HTTP/1.1 101 Switching Protocols\r\n"
198 "Connection: upgrade\r\n"
199 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
200 "Sec-WebSocket-Version: 13\r\n"
203 // no Upgrade websocket
204 check(error::no_upgrade_websocket
,
205 "HTTP/1.1 101 Switching Protocols\r\n"
207 "Upgrade: HTTP/2\r\n"
208 "Connection: upgrade\r\n"
209 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
210 "Sec-WebSocket-Version: 13\r\n"
213 // no Sec-WebSocket-Accept
214 check(error::no_sec_accept
,
215 "HTTP/1.1 101 Switching Protocols\r\n"
217 "Upgrade: WebSocket\r\n"
218 "Connection: upgrade\r\n"
219 "Sec-WebSocket-Version: 13\r\n"
222 // bad Sec-WebSocket-Accept
223 check(error::bad_sec_accept
,
224 "HTTP/1.1 101 Switching Protocols\r\n"
226 "Upgrade: WebSocket\r\n"
227 "Connection: upgrade\r\n"
228 "Sec-WebSocket-Accept: *\r\n"
229 "Sec-WebSocket-Version: 13\r\n"
233 check(error::upgrade_declined
,
234 "HTTP/1.1 200 OK\r\n"
236 "Upgrade: WebSocket\r\n"
237 "Connection: upgrade\r\n"
238 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
239 "Sec-WebSocket-Version: 13\r\n"
244 // Compression Extensions for WebSocket
246 // https://tools.ietf.org/html/rfc7692
251 detail::pmd_offer po
;
257 f
.set(http::field::sec_websocket_extensions
, s
);
258 po
= detail::pmd_offer();
259 detail::pmd_read(po
, f
);
260 BEAST_EXPECT(po
.accept
);
267 f
.set(http::field::sec_websocket_extensions
, s
);
268 po
= detail::pmd_offer();
269 detail::pmd_read(po
, f
);
270 BEAST_EXPECT(! po
.accept
);
273 // duplicate parameters
274 reject("permessage-deflate; server_max_window_bits=8; server_max_window_bits=8");
277 reject("permessage-deflate; server_max_window_bits");
278 reject("permessage-deflate; server_max_window_bits=");
281 reject("permessage-deflate; server_max_window_bits=-1");
282 reject("permessage-deflate; server_max_window_bits=7");
283 reject("permessage-deflate; server_max_window_bits=16");
284 reject("permessage-deflate; server_max_window_bits=999999999999999999999999");
285 reject("permessage-deflate; server_max_window_bits=9a");
287 // duplicate parameters
288 reject("permessage-deflate; client_max_window_bits=8; client_max_window_bits=8");
290 // optional value excluded
291 accept("permessage-deflate; client_max_window_bits");
292 BEAST_EXPECT(po
.client_max_window_bits
== -1);
293 accept("permessage-deflate; client_max_window_bits=");
294 BEAST_EXPECT(po
.client_max_window_bits
== -1);
297 reject("permessage-deflate; client_max_window_bits=-1");
298 reject("permessage-deflate; client_max_window_bits=7");
299 reject("permessage-deflate; client_max_window_bits=16");
300 reject("permessage-deflate; client_max_window_bits=999999999999999999999999");
302 // duplicate parameters
303 reject("permessage-deflate; server_no_context_takeover; server_no_context_takeover");
305 // valueless parameter
306 accept("permessage-deflate; server_no_context_takeover");
307 BEAST_EXPECT(po
.server_no_context_takeover
);
308 accept("permessage-deflate; server_no_context_takeover=");
309 BEAST_EXPECT(po
.server_no_context_takeover
);
312 reject("permessage-deflate; server_no_context_takeover=-1");
313 reject("permessage-deflate; server_no_context_takeover=x");
314 reject("permessage-deflate; server_no_context_takeover=\"yz\"");
315 reject("permessage-deflate; server_no_context_takeover=999999999999999999999999");
317 // duplicate parameters
318 reject("permessage-deflate; client_no_context_takeover; client_no_context_takeover");
320 // valueless parameter
321 accept("permessage-deflate; client_no_context_takeover");
322 BEAST_EXPECT(po
.client_no_context_takeover
);
323 accept("permessage-deflate; client_no_context_takeover=");
324 BEAST_EXPECT(po
.client_no_context_takeover
);
327 reject("permessage-deflate; client_no_context_takeover=-1");
328 reject("permessage-deflate; client_no_context_takeover=x");
329 reject("permessage-deflate; client_no_context_takeover=\"yz\"");
330 reject("permessage-deflate; client_no_context_takeover=999999999999999999999999");
332 // unknown extension parameter
333 reject("permessage-deflate; unknown");
334 reject("permessage-deflate; unknown=");
335 reject("permessage-deflate; unknown=1");
336 reject("permessage-deflate; unknown=x");
337 reject("permessage-deflate; unknown=\"xy\"");
343 detail::pmd_offer po
;
346 [&](string_view match
)
349 detail::pmd_write(f
, po
);
351 f
[http::field::sec_websocket_extensions
]
356 po
.server_max_window_bits
= 0;
357 po
.client_max_window_bits
= 0;
358 po
.server_no_context_takeover
= false;
359 po
.client_no_context_takeover
= false;
361 check("permessage-deflate");
363 po
.server_max_window_bits
= 10;
364 check("permessage-deflate; server_max_window_bits=10");
366 po
.server_max_window_bits
= -1;
367 check("permessage-deflate; server_max_window_bits");
369 po
.server_max_window_bits
= 0;
370 po
.client_max_window_bits
= 10;
371 check("permessage-deflate; client_max_window_bits=10");
373 po
.client_max_window_bits
= -1;
374 check("permessage-deflate; client_max_window_bits");
376 po
.client_max_window_bits
= 0;
377 po
.server_no_context_takeover
= true;
378 check("permessage-deflate; server_no_context_takeover");
380 po
.server_no_context_takeover
= false;
381 po
.client_no_context_takeover
= true;
382 check("permessage-deflate; client_no_context_takeover");
388 permessage_deflate pmd
;
394 detail::pmd_offer po
;
397 f
.set(http::field::sec_websocket_extensions
, offer
);
398 detail::pmd_read(po
, f
);
401 detail::pmd_offer config
;
402 detail::pmd_negotiate(f
, config
, po
, pmd
);
403 BEAST_EXPECT(! config
.accept
);
411 detail::pmd_offer po
;
414 f
.set(http::field::sec_websocket_extensions
, offer
);
415 detail::pmd_read(po
, f
);
418 detail::pmd_offer config
;
419 detail::pmd_negotiate(f
, config
, po
, pmd
);
421 f
[http::field::sec_websocket_extensions
];
422 BEAST_EXPECTS(got
== result
, got
);
424 detail::pmd_offer poc
;
425 detail::pmd_read(poc
, f
);
426 detail::pmd_normalize(poc
);
427 BEAST_EXPECT(poc
.accept
);
429 BEAST_EXPECT(config
.server_max_window_bits
!= 0);
430 BEAST_EXPECT(config
.client_max_window_bits
!= 0);
433 pmd
.server_enable
= true;
434 pmd
.server_max_window_bits
= 15;
435 pmd
.client_max_window_bits
= 15;
436 pmd
.server_no_context_takeover
= false;
437 pmd
.client_no_context_takeover
= false;
441 "permessage-deflate",
442 "permessage-deflate");
444 // non-default server_max_window_bits
446 "permessage-deflate; server_max_window_bits=14",
447 "permessage-deflate; server_max_window_bits=14");
449 // explicit default server_max_window_bits
451 "permessage-deflate; server_max_window_bits=15",
452 "permessage-deflate");
454 // minimum window size of 8 bits (a zlib bug)
456 "permessage-deflate; server_max_window_bits=8",
457 "permessage-deflate; server_max_window_bits=9");
459 // non-default server_max_window_bits setting
460 pmd
.server_max_window_bits
= 10;
462 "permessage-deflate",
463 "permessage-deflate; server_max_window_bits=10");
465 // clamped server_max_window_bits setting #1
466 pmd
.server_max_window_bits
= 10;
468 "permessage-deflate; server_max_window_bits=14",
469 "permessage-deflate; server_max_window_bits=10");
471 // clamped server_max_window_bits setting #2
472 pmd
.server_max_window_bits
=8;
474 "permessage-deflate; server_max_window_bits=14",
475 "permessage-deflate; server_max_window_bits=9");
477 pmd
.server_max_window_bits
= 15;
479 // present with no value
481 "permessage-deflate; client_max_window_bits",
482 "permessage-deflate");
484 // present with no value, non-default setting
485 pmd
.client_max_window_bits
= 10;
487 "permessage-deflate; client_max_window_bits",
488 "permessage-deflate; client_max_window_bits=10");
490 // absent, non-default setting
491 pmd
.client_max_window_bits
= 10;
493 "permessage-deflate");
500 stream
<test::stream
> ws
{ioc
};
501 ws
.async_handshake("", "", move_only_handler
{});
504 struct copyable_handler
506 template<class... Args
>
508 operator()(Args
&&...) const
516 using tcp
= net::ip::tcp
;
520 // success, no timeout
523 stream
<tcp::socket
> ws1(ioc
);
524 stream
<tcp::socket
> ws2(ioc
);
525 test::connect(ws1
.next_layer(), ws2
.next_layer());
527 ws1
.async_handshake("test", "/", test::success_handler());
528 ws2
.async_accept(test::success_handler());
529 test::run_for(ioc
, std::chrono::seconds(1));
533 stream
<test::stream
> ws1(ioc
);
534 stream
<test::stream
> ws2(ioc
);
535 test::connect(ws1
.next_layer(), ws2
.next_layer());
537 ws1
.async_handshake("test", "/", test::success_handler());
538 ws2
.async_accept(test::success_handler());
539 test::run_for(ioc
, std::chrono::seconds(1));
542 // success, timeout enabled
545 stream
<tcp::socket
> ws1(ioc
);
546 stream
<tcp::socket
> ws2(ioc
);
547 test::connect(ws1
.next_layer(), ws2
.next_layer());
549 ws1
.set_option(stream_base::timeout
{
550 std::chrono::milliseconds(50),
553 ws1
.async_handshake("test", "/", test::success_handler());
554 ws2
.async_accept(test::success_handler());
555 test::run_for(ioc
, std::chrono::seconds(1));
559 stream
<test::stream
> ws1(ioc
);
560 stream
<test::stream
> ws2(ioc
);
561 test::connect(ws1
.next_layer(), ws2
.next_layer());
563 ws1
.set_option(stream_base::timeout
{
564 std::chrono::milliseconds(50),
567 ws1
.async_handshake("test", "/", test::success_handler());
568 ws2
.async_accept(test::success_handler());
569 test::run_for(ioc
, std::chrono::seconds(1));
575 stream
<tcp::socket
> ws1(ioc
);
576 stream
<tcp::socket
> ws2(ioc
);
577 test::connect(ws1
.next_layer(), ws2
.next_layer());
579 ws1
.set_option(stream_base::timeout
{
580 std::chrono::milliseconds(50),
583 ws1
.async_handshake("test", "/",
584 test::fail_handler(beast::error::timeout
));
585 test::run_for(ioc
, std::chrono::seconds(1));
589 stream
<test::stream
> ws1(ioc
);
590 stream
<test::stream
> ws2(ioc
);
591 test::connect(ws1
.next_layer(), ws2
.next_layer());
593 ws1
.set_option(stream_base::timeout
{
594 std::chrono::milliseconds(50),
597 ws1
.async_handshake("test", "/",
598 test::fail_handler(beast::error::timeout
));
599 test::run_for(ioc
, std::chrono::seconds(1));
602 // abandoned operation
606 stream
<tcp::socket
> ws1(ioc
);
607 ws1
.async_handshake("test", "/",
609 net::error::operation_aborted
));
615 // https://github.com/boostorg/beast/issues/1460
620 auto const make_big
= [](response_type
& res
)
622 res
.insert("Date", "Mon, 18 Feb 2019 12:48:36 GMT");
623 res
.insert("Set-Cookie",
624 "__cfduid=de1e209833e7f05aaa1044c6d448994761550494116; "
625 "expires=Tue, 18-Feb-20 12:48:36 GMT; path=/; domain=.cryptofacilities.com; HttpOnly; Secure");
626 res
.insert("Feature-Policy",
627 "accelerometer 'none'; ambient-light-sensor 'none'; "
628 "animations 'none'; autoplay 'none'; camera 'none'; document-write 'none'; "
629 "encrypted-media 'none'; geolocation 'none'; gyroscope 'none'; legacy-image-formats 'none'; "
630 "magnetometer 'none'; max-downscaling-image 'none'; microphone 'none'; midi 'none'; "
631 "payment 'none'; picture-in-picture 'none'; unsized-media 'none'; usb 'none'; vr 'none'");
632 res
.insert("Referrer-Policy", "origin");
633 res
.insert("Strict-Transport-Security", "max-age=15552000; includeSubDomains; preload");
634 res
.insert("X-Content-Type-Options", "nosniff");
635 res
.insert("Content-Security-Policy",
636 "default-src 'none'; manifest-src 'self'; object-src 'self'; "
637 "child-src 'self' https://www.google.com; "
638 "font-src 'self' https://use.typekit.net https://maxcdn.bootstrapcdn.com https://fonts.gstatic.com data:; "
639 "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://ajax.cloudflare.com https://use.typekit.net "
640 "https://www.googletagmanager.com https://www.google-analytics.com https://www.google.com https://www.googleadservices.com "
641 "https://googleads.g.doubleclick.net https://www.gstatic.com; connect-src 'self' wss://*.cryptofacilities.com/ws/v1 wss://*.cryptofacilities.com/ws/indices "
642 "https://uat.cryptofacilities.com https://uat.cf0.io wss://*.cf0.io https://www.googletagmanager.com https://www.google-analytics.com https://www.google.com "
643 "https://fonts.googleapis.com https://google-analytics.com https://use.typekit.net https://p.typekit.net https://fonts.gstatic.com https://www.gstatic.com "
644 "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 "
645 "https://www.google-analytics.com https://stats.g.doubleclick.net data:; style-src 'self' 'unsafe-inline' https://use.typekit.net https://p.typekit.net "
646 "https://fonts.googleapis.com https://maxcdn.bootstrapcdn.com");
647 res
.insert("X-Frame-Options", "SAMEORIGIN");
648 res
.insert("X-Xss-Protection", "1; mode=block");
649 res
.insert("Expect-CT", "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"");
650 res
.insert("Server", "cloudflare");
651 res
.insert("CF-RAY", "4ab09be1a9d0cb06-ARN");
653 "****************************************************************************************************"
654 "****************************************************************************************************"
655 "****************************************************************************************************"
656 "****************************************************************************************************"
657 "****************************************************************************************************"
658 "****************************************************************************************************"
659 "****************************************************************************************************"
660 "****************************************************************************************************"
661 "****************************************************************************************************"
662 "****************************************************************************************************"
663 "****************************************************************************************************"
664 "****************************************************************************************************"
665 "****************************************************************************************************"
666 "****************************************************************************************************"
667 "****************************************************************************************************"
668 "****************************************************************************************************"
669 "****************************************************************************************************"
670 "****************************************************************************************************"
671 "****************************************************************************************************"
672 "****************************************************************************************************");
676 stream
<test::stream
> ws1(ioc
);
677 stream
<test::stream
> ws2(ioc
);
678 test::connect(ws1
.next_layer(), ws2
.next_layer());
680 ws2
.set_option(stream_base::decorator(make_big
));
682 ws2
.async_accept(test::success_handler());
689 ws1
.handshake("test", "/", ec
);
690 BEAST_EXPECTS(! ec
, ec
.message());
695 stream
<test::stream
> ws1(ioc
);
696 stream
<test::stream
> ws2(ioc
);
697 test::connect(ws1
.next_layer(), ws2
.next_layer());
699 ws2
.set_option(stream_base::decorator(make_big
));
700 ws2
.async_accept(test::success_handler());
701 ws1
.async_handshake("test", "/", test::success_handler());
720 BEAST_DEFINE_TESTSUITE(beast
,websocket
,handshake
);