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