]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/beast/websocket/handshake.cpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / libs / beast / test / beast / websocket / handshake.cpp
1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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/websocket/stream.hpp>
12
13 #include <boost/beast/_experimental/test/stream.hpp>
14 #include <boost/beast/_experimental/test/tcp.hpp>
15
16 #include "test.hpp"
17
18 #include <boost/asio/io_context.hpp>
19 #include <boost/asio/strand.hpp>
20 #include <thread>
21
22 namespace boost {
23 namespace beast {
24 namespace websocket {
25
26 class handshake_test : public websocket_test_suite
27 {
28 public:
29 template<class Wrap>
30 void
31 doTestHandshake(Wrap const& w)
32 {
33 class req_decorator
34 {
35 bool& b_;
36
37 public:
38 req_decorator(req_decorator const&) = default;
39
40 explicit
41 req_decorator(bool& b)
42 : b_(b)
43 {
44 }
45
46 void
47 operator()(request_type&) const
48 {
49 b_ = true;
50 }
51 };
52
53 // handshake
54 doStreamLoop([&](test::stream& ts)
55 {
56 echo_server es{log};
57 ws_type ws{ts};
58 ws.next_layer().connect(es.stream());
59 try
60 {
61 w.handshake(ws, "localhost", "/");
62 }
63 catch(...)
64 {
65 ts.close();
66 throw;
67 }
68 ts.close();
69 });
70
71 // handshake, response
72 doStreamLoop([&](test::stream& ts)
73 {
74 echo_server es{log};
75 ws_type ws{ts};
76 ws.next_layer().connect(es.stream());
77 response_type res;
78 try
79 {
80 w.handshake(ws, res, "localhost", "/");
81 // VFALCO validate res?
82 }
83 catch(...)
84 {
85 ts.close();
86 throw;
87 }
88 ts.close();
89 });
90
91 // handshake, decorator
92 doStreamLoop([&](test::stream& ts)
93 {
94 echo_server es{log};
95 ws_type ws{ts};
96 ws.next_layer().connect(es.stream());
97 bool called = false;
98 try
99 {
100 ws.set_option(stream_base::decorator(
101 req_decorator{called}));
102 w.handshake(ws, "localhost", "/");
103 BEAST_EXPECT(called);
104 }
105 catch(...)
106 {
107 ts.close();
108 throw;
109 }
110 ts.close();
111 });
112
113 // handshake, response, decorator
114 doStreamLoop([&](test::stream& ts)
115 {
116 echo_server es{log};
117 ws_type ws{ts};
118 ws.next_layer().connect(es.stream());
119 bool called = false;
120 response_type res;
121 try
122 {
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);
128 }
129 catch(...)
130 {
131 ts.close();
132 throw;
133 }
134 ts.close();
135 });
136 }
137
138 void
139 testHandshake()
140 {
141 doTestHandshake(SyncClient{});
142
143 yield_to([&](yield_context yield)
144 {
145 doTestHandshake(AsyncClient{yield});
146 });
147
148 auto const check =
149 [&](error e, std::string const& s)
150 {
151 stream<test::stream> ws{ioc_};
152 auto tr = connect(ws.next_layer());
153 ws.next_layer().append(s);
154 tr.close();
155 try
156 {
157 ws.handshake("localhost:80", "/");
158 fail();
159 }
160 catch(system_error const& se)
161 {
162 BEAST_EXPECTS(se.code() == e, se.what());
163 }
164 };
165 // bad HTTP version
166 check(error::bad_http_version,
167 "HTTP/1.0 101 Switching Protocols\r\n"
168 "Server: beast\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"
173 "\r\n"
174 );
175 // no Connection
176 check(error::no_connection,
177 "HTTP/1.1 101 Switching Protocols\r\n"
178 "Server: beast\r\n"
179 "Upgrade: WebSocket\r\n"
180 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
181 "Sec-WebSocket-Version: 13\r\n"
182 "\r\n"
183 );
184 // no Connection upgrade
185 check(error::no_connection_upgrade,
186 "HTTP/1.1 101 Switching Protocols\r\n"
187 "Server: beast\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"
192 "\r\n"
193 );
194 // no Upgrade
195 check(error::no_upgrade,
196 "HTTP/1.1 101 Switching Protocols\r\n"
197 "Server: beast\r\n"
198 "Connection: upgrade\r\n"
199 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
200 "Sec-WebSocket-Version: 13\r\n"
201 "\r\n"
202 );
203 // no Upgrade websocket
204 check(error::no_upgrade_websocket,
205 "HTTP/1.1 101 Switching Protocols\r\n"
206 "Server: beast\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"
211 "\r\n"
212 );
213 // no Sec-WebSocket-Accept
214 check(error::no_sec_accept,
215 "HTTP/1.1 101 Switching Protocols\r\n"
216 "Server: beast\r\n"
217 "Upgrade: WebSocket\r\n"
218 "Connection: upgrade\r\n"
219 "Sec-WebSocket-Version: 13\r\n"
220 "\r\n"
221 );
222 // bad Sec-WebSocket-Accept
223 check(error::bad_sec_accept,
224 "HTTP/1.1 101 Switching Protocols\r\n"
225 "Server: beast\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"
230 "\r\n"
231 );
232 // declined
233 check(error::upgrade_declined,
234 "HTTP/1.1 200 OK\r\n"
235 "Server: beast\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"
240 "\r\n"
241 );
242 }
243
244 // Compression Extensions for WebSocket
245 //
246 // https://tools.ietf.org/html/rfc7692
247 //
248 void
249 testExtRead()
250 {
251 detail::pmd_offer po;
252
253 auto const accept =
254 [&](string_view s)
255 {
256 http::fields f;
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);
261 };
262
263 auto const reject =
264 [&](string_view s)
265 {
266 http::fields f;
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);
271 };
272
273 // duplicate parameters
274 reject("permessage-deflate; server_max_window_bits=8; server_max_window_bits=8");
275
276 // missing value
277 reject("permessage-deflate; server_max_window_bits");
278 reject("permessage-deflate; server_max_window_bits=");
279
280 // invalid value
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");
286
287 // duplicate parameters
288 reject("permessage-deflate; client_max_window_bits=8; client_max_window_bits=8");
289
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);
295
296 // invalid value
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");
301
302 // duplicate parameters
303 reject("permessage-deflate; server_no_context_takeover; server_no_context_takeover");
304
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);
310
311 // disallowed value
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");
316
317 // duplicate parameters
318 reject("permessage-deflate; client_no_context_takeover; client_no_context_takeover");
319
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);
325
326 // disallowed value
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");
331
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\"");
338 }
339
340 void
341 testExtWrite()
342 {
343 detail::pmd_offer po;
344
345 auto const check =
346 [&](string_view match)
347 {
348 http::fields f;
349 detail::pmd_write(f, po);
350 BEAST_EXPECT(
351 f[http::field::sec_websocket_extensions]
352 == match);
353 };
354
355 po.accept = true;
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;
360
361 check("permessage-deflate");
362
363 po.server_max_window_bits = 10;
364 check("permessage-deflate; server_max_window_bits=10");
365
366 po.server_max_window_bits = -1;
367 check("permessage-deflate; server_max_window_bits");
368
369 po.server_max_window_bits = 0;
370 po.client_max_window_bits = 10;
371 check("permessage-deflate; client_max_window_bits=10");
372
373 po.client_max_window_bits = -1;
374 check("permessage-deflate; client_max_window_bits");
375
376 po.client_max_window_bits = 0;
377 po.server_no_context_takeover = true;
378 check("permessage-deflate; server_no_context_takeover");
379
380 po.server_no_context_takeover = false;
381 po.client_no_context_takeover = true;
382 check("permessage-deflate; client_no_context_takeover");
383 }
384
385 void
386 testExtNegotiate()
387 {
388 permessage_deflate pmd;
389
390 auto const reject =
391 [&](
392 string_view offer)
393 {
394 detail::pmd_offer po;
395 {
396 http::fields f;
397 f.set(http::field::sec_websocket_extensions, offer);
398 detail::pmd_read(po, f);
399 }
400 http::fields f;
401 detail::pmd_offer config;
402 detail::pmd_negotiate(f, config, po, pmd);
403 BEAST_EXPECT(! config.accept);
404 };
405
406 auto const accept =
407 [&](
408 string_view offer,
409 string_view result)
410 {
411 detail::pmd_offer po;
412 {
413 http::fields f;
414 f.set(http::field::sec_websocket_extensions, offer);
415 detail::pmd_read(po, f);
416 }
417 http::fields f;
418 detail::pmd_offer config;
419 detail::pmd_negotiate(f, config, po, pmd);
420 auto const got =
421 f[http::field::sec_websocket_extensions];
422 BEAST_EXPECTS(got == result, got);
423 {
424 detail::pmd_offer poc;
425 detail::pmd_read(poc, f);
426 detail::pmd_normalize(poc);
427 BEAST_EXPECT(poc.accept);
428 }
429 BEAST_EXPECT(config.server_max_window_bits != 0);
430 BEAST_EXPECT(config.client_max_window_bits != 0);
431 };
432
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;
438
439 // default
440 accept(
441 "permessage-deflate",
442 "permessage-deflate");
443
444 // non-default server_max_window_bits
445 accept(
446 "permessage-deflate; server_max_window_bits=14",
447 "permessage-deflate; server_max_window_bits=14");
448
449 // explicit default server_max_window_bits
450 accept(
451 "permessage-deflate; server_max_window_bits=15",
452 "permessage-deflate");
453
454 // minimum window size of 8 bits (a zlib bug)
455 accept(
456 "permessage-deflate; server_max_window_bits=8",
457 "permessage-deflate; server_max_window_bits=9");
458
459 // non-default server_max_window_bits setting
460 pmd.server_max_window_bits = 10;
461 accept(
462 "permessage-deflate",
463 "permessage-deflate; server_max_window_bits=10");
464
465 // clamped server_max_window_bits setting #1
466 pmd.server_max_window_bits = 10;
467 accept(
468 "permessage-deflate; server_max_window_bits=14",
469 "permessage-deflate; server_max_window_bits=10");
470
471 // clamped server_max_window_bits setting #2
472 pmd.server_max_window_bits=8;
473 accept(
474 "permessage-deflate; server_max_window_bits=14",
475 "permessage-deflate; server_max_window_bits=9");
476
477 pmd.server_max_window_bits = 15;
478
479 // present with no value
480 accept(
481 "permessage-deflate; client_max_window_bits",
482 "permessage-deflate");
483
484 // present with no value, non-default setting
485 pmd.client_max_window_bits = 10;
486 accept(
487 "permessage-deflate; client_max_window_bits",
488 "permessage-deflate; client_max_window_bits=10");
489
490 // absent, non-default setting
491 pmd.client_max_window_bits = 10;
492 reject(
493 "permessage-deflate");
494 }
495
496 void
497 testMoveOnly()
498 {
499 net::io_context ioc;
500 stream<test::stream> ws{ioc};
501 ws.async_handshake("", "", move_only_handler{});
502 }
503
504 struct copyable_handler
505 {
506 template<class... Args>
507 void
508 operator()(Args&&...) const
509 {
510 }
511 };
512
513 void
514 testAsync()
515 {
516 using tcp = net::ip::tcp;
517
518 net::io_context ioc;
519
520 // success, no timeout
521
522 {
523 stream<tcp::socket> ws1(ioc);
524 stream<tcp::socket> ws2(ioc);
525 test::connect(ws1.next_layer(), ws2.next_layer());
526
527 ws1.async_handshake("test", "/", test::success_handler());
528 ws2.async_accept(test::success_handler());
529 test::run_for(ioc, std::chrono::seconds(1));
530 }
531
532 {
533 stream<test::stream> ws1(ioc);
534 stream<test::stream> ws2(ioc);
535 test::connect(ws1.next_layer(), ws2.next_layer());
536
537 ws1.async_handshake("test", "/", test::success_handler());
538 ws2.async_accept(test::success_handler());
539 test::run_for(ioc, std::chrono::seconds(1));
540 }
541
542 // success, timeout enabled
543
544 {
545 stream<tcp::socket> ws1(ioc);
546 stream<tcp::socket> ws2(ioc);
547 test::connect(ws1.next_layer(), ws2.next_layer());
548
549 ws1.set_option(stream_base::timeout{
550 std::chrono::milliseconds(50),
551 stream_base::none(),
552 false});
553 ws1.async_handshake("test", "/", test::success_handler());
554 ws2.async_accept(test::success_handler());
555 test::run_for(ioc, std::chrono::seconds(1));
556 }
557
558 {
559 stream<test::stream> ws1(ioc);
560 stream<test::stream> ws2(ioc);
561 test::connect(ws1.next_layer(), ws2.next_layer());
562
563 ws1.set_option(stream_base::timeout{
564 std::chrono::milliseconds(50),
565 stream_base::none(),
566 false});
567 ws1.async_handshake("test", "/", test::success_handler());
568 ws2.async_accept(test::success_handler());
569 test::run_for(ioc, std::chrono::seconds(1));
570 }
571
572 // timeout
573
574 {
575 stream<tcp::socket> ws1(ioc);
576 stream<tcp::socket> ws2(ioc);
577 test::connect(ws1.next_layer(), ws2.next_layer());
578
579 ws1.set_option(stream_base::timeout{
580 std::chrono::milliseconds(50),
581 stream_base::none(),
582 false});
583 ws1.async_handshake("test", "/",
584 test::fail_handler(beast::error::timeout));
585 test::run_for(ioc, std::chrono::seconds(1));
586 }
587
588 {
589 stream<test::stream> ws1(ioc);
590 stream<test::stream> ws2(ioc);
591 test::connect(ws1.next_layer(), ws2.next_layer());
592
593 ws1.set_option(stream_base::timeout{
594 std::chrono::milliseconds(50),
595 stream_base::none(),
596 false});
597 ws1.async_handshake("test", "/",
598 test::fail_handler(beast::error::timeout));
599 test::run_for(ioc, std::chrono::seconds(1));
600 }
601
602 // abandoned operation
603
604 {
605 {
606 stream<tcp::socket> ws1(ioc);
607 ws1.async_handshake("test", "/",
608 test::fail_handler(
609 net::error::operation_aborted));
610 }
611 test::run(ioc);
612 }
613 }
614
615 // https://github.com/boostorg/beast/issues/1460
616 void
617 testIssue1460()
618 {
619 net::io_context ioc;
620 auto const make_big = [](response_type& res)
621 {
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");
652 res.insert("Bulk",
653 "****************************************************************************************************"
654 "****************************************************************************************************"
655 "****************************************************************************************************"
656 "****************************************************************************************************"
657 "****************************************************************************************************"
658 "****************************************************************************************************"
659 "****************************************************************************************************"
660 "****************************************************************************************************"
661 "****************************************************************************************************"
662 "****************************************************************************************************"
663 "****************************************************************************************************"
664 "****************************************************************************************************"
665 "****************************************************************************************************"
666 "****************************************************************************************************"
667 "****************************************************************************************************"
668 "****************************************************************************************************"
669 "****************************************************************************************************"
670 "****************************************************************************************************"
671 "****************************************************************************************************"
672 "****************************************************************************************************");
673 };
674
675 {
676 stream<test::stream> ws1(ioc);
677 stream<test::stream> ws2(ioc);
678 test::connect(ws1.next_layer(), ws2.next_layer());
679
680 ws2.set_option(stream_base::decorator(make_big));
681 error_code ec;
682 ws2.async_accept(test::success_handler());
683 std::thread t(
684 [&ioc]
685 {
686 ioc.run();
687 ioc.restart();
688 });
689 ws1.handshake("test", "/", ec);
690 BEAST_EXPECTS(! ec, ec.message());
691 t.join();
692 }
693
694 {
695 stream<test::stream> ws1(ioc);
696 stream<test::stream> ws2(ioc);
697 test::connect(ws1.next_layer(), ws2.next_layer());
698
699 ws2.set_option(stream_base::decorator(make_big));
700 ws2.async_accept(test::success_handler());
701 ws1.async_handshake("test", "/", test::success_handler());
702 ioc.run();
703 ioc.restart();
704 }
705 }
706
707 void
708 run() override
709 {
710 testHandshake();
711 testExtRead();
712 testExtWrite();
713 testExtNegotiate();
714 testMoveOnly();
715 testAsync();
716 testIssue1460();
717 }
718 };
719
720 BEAST_DEFINE_TESTSUITE(beast,websocket,handshake);
721
722 } // websocket
723 } // beast
724 } // boost