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