Imported Upstream version 1.72.0
[platform/upstream/boost.git] / 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