• 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 #include <boost/config.hpp>
11 
12 #ifdef BOOST_MSVC
13 #pragma warning(push)
14 #pragma warning(disable: 4459) // declaration hides global declaration
15 #endif
16 
17 #include <boost/beast/_experimental/unit_test/suite.hpp>
18 
19 #include <boost/beast.hpp>
20 #include <boost/beast/ssl.hpp>
21 #include <boost/asio.hpp>
22 #include <boost/asio/ssl.hpp>
23 
24 namespace {
25 
26 #include "websocket_common.ipp"
27 
28 void
snippets()29 snippets()
30 {
31     {
32     //[code_websocket_2_1
33 
34         stream<tcp_stream> ws(ioc);
35         net::ip::tcp::resolver resolver(ioc);
36         get_lowest_layer(ws).connect(resolver.resolve("www.example.com", "ws"));
37 
38         // Do the websocket handshake in the client role, on the connected stream.
39         // The implementation only uses the Host parameter to set the HTTP "Host" field,
40         // it does not perform any DNS lookup. That must be done first, as shown above.
41 
42         ws.handshake(
43             "www.example.com",  // The Host field
44             "/"                 // The request-target
45         );
46 
47     //]
48     }
49 
50     {
51 
52     stream<tcp_stream> ws(ioc);
53 
54     {
55     //[code_websocket_2_2
56 
57         // This variable will receive the HTTP response from the server
58         response_type res;
59 
60         // Perform the websocket handshake in the client role.
61         // On success, `res` will hold the complete HTTP response received.
62 
63         ws.handshake(
64             res,                // Receives the HTTP response
65             "www.example.com",  // The Host field
66             "/"                 // The request-target
67         );
68 
69     //]
70     }
71 
72     //--------------------------------------------------------------------------
73 
74     {
75     //[code_websocket_2_3
76 
77         // Perform the websocket handshake in the server role.
78         // The stream must already be connected to the peer.
79 
80         ws.accept();
81 
82     //]
83     }
84 
85     {
86     //[code_websocket_2_4
87 
88         // This buffer will hold the HTTP request as raw characters
89         std::string s;
90 
91         // Read into our buffer until we reach the end of the HTTP request.
92         // No parsing takes place here, we are just accumulating data.
93 
94         net::read_until(sock, net::dynamic_buffer(s), "\r\n\r\n");
95 
96         // Now accept the connection, using the buffered data.
97         ws.accept(net::buffer(s));
98 
99     //]
100     }
101 
102     }
103 
104     {
105     //[code_websocket_2_5
106 
107         // This buffer is required for reading HTTP messages
108         flat_buffer buffer;
109 
110         // Read the HTTP request ourselves
111         http::request<http::string_body> req;
112         http::read(sock, buffer, req);
113 
114         // See if its a WebSocket upgrade request
115         if(websocket::is_upgrade(req))
116         {
117             // Construct the stream, transferring ownership of the socket
118             stream<tcp_stream> ws(std::move(sock));
119 
120             // Clients SHOULD NOT begin sending WebSocket
121             // frames until the server has provided a response.
122             BOOST_ASSERT(buffer.size() == 0);
123 
124             // Accept the upgrade request
125             ws.accept(req);
126         }
127         else
128         {
129             // Its not a WebSocket upgrade, so
130             // handle it like a normal HTTP request.
131         }
132 
133     //]
134     }
135 
136     {
137         //[code_websocket_2_6
138 
139         // a function to select the most preferred protocol from a comma-separated list
140         auto select_protocol = [](string_view offered_tokens) -> std::string
141         {
142             // tokenize the Sec-Websocket-Protocol header offered by the client
143             http::token_list offered( offered_tokens );
144 
145             // an array of protocols supported by this server
146             // in descending order of preference
147             static const std::array<string_view, 3>
148                 supported = {
149                 "v3.my.chat",
150                 "v2.my.chat",
151                 "v1.my.chat"
152             };
153 
154             std::string result;
155 
156             for (auto proto : supported)
157             {
158                 auto iter = std::find(offered.begin(), offered.end(), proto);
159                 if (iter != offered.end())
160                 {
161                     // we found a supported protocol in the list offered by the client
162                     result.assign(proto.begin(), proto.end());
163                     break;
164                 }
165             }
166 
167             return result;
168         };
169 
170 
171         // This buffer is required for reading HTTP messages
172         flat_buffer buffer;
173 
174         // Read the HTTP request ourselves
175         http::request<http::string_body> req;
176         http::read(sock, buffer, req);
177 
178         // See if it's a WebSocket upgrade request
179         if(websocket::is_upgrade(req))
180         {
181             // we store the selected protocol in a std::string here because
182             // we intend to capture it in the decorator's lambda below
183             std::string protocol =
184                 select_protocol(
185                     req[http::field::sec_websocket_protocol]);
186 
187             if (protocol.empty())
188             {
189                 // none of our supported protocols were offered
190                 http::response<http::string_body> res;
191                 res.result(http::status::bad_request);
192                 res.body() = "No valid sub-protocol was offered."
193                               " This server implements"
194                               " v3.my.chat,"
195                               " v2.my.chat"
196                               " and v1.my.chat";
197                 http::write(sock, res);
198             }
199             else
200             {
201                 // Construct the stream, transferring ownership of the socket
202                 stream<tcp_stream> ws(std::move(sock));
203 
204                 ws.set_option(
205                     stream_base::decorator(
206                         [protocol](http::response_header<> &hdr) {
207                             hdr.set(
208                                 http::field::sec_websocket_protocol,
209                                 protocol);
210                         }));
211 
212                 // Accept the upgrade request
213                 ws.accept(req);
214             }
215         }
216         else
217         {
218             // Its not a WebSocket upgrade, so
219             // handle it like a normal HTTP request.
220         }
221 
222         //]
223     }
224 
225 }
226 
227 struct websocket_2_test
228     : public boost::beast::unit_test::suite
229 {
230     void
run__anonc50606eb0111::websocket_2_test231     run() override
232     {
233         BEAST_EXPECT(&snippets);
234     }
235 };
236 
237 BEAST_DEFINE_TESTSUITE(beast,doc,websocket_2);
238 
239 } // (anon)
240 
241 #ifdef BOOST_MSVC
242 #pragma warning(pop)
243 #endif
244