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
24namespace {
25
26#include "websocket_common.ipp"
27
28void
29snippets()
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(t&: ws).connect(endpoints: resolver.resolve(host: "www.example.com", service: "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 host: "www.example.com", // The Host field
44 target: "/" // 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 host: "www.example.com", // The Host field
66 target: "/" // 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(s&: sock, buffers: net::dynamic_buffer(data&: s), delim: "\r\n\r\n");
95
96 // Now accept the connection, using the buffered data.
97 ws.accept(buffers: net::buffer(data&: 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(stream&: sock, buffer, msg&: 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(first: offered.begin(), last: offered.end(), val: proto);
159 if (iter != offered.end())
160 {
161 // we found a supported protocol in the list offered by the client
162 result.assign(first: proto.begin(), last: 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(stream&: sock, buffer, msg&: 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(v: 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(stream&: sock, msg: 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 name: http::field::sec_websocket_protocol,
209 value: 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
227struct websocket_2_test
228 : public boost::beast::unit_test::suite
229{
230 void
231 run() override
232 {
233 BEAST_EXPECT(&snippets);
234 }
235};
236
237BEAST_DEFINE_TESTSUITE(beast,doc,websocket_2);
238
239} // (anon)
240
241#ifdef BOOST_MSVC
242#pragma warning(pop)
243#endif
244

source code of boost/libs/beast/test/doc/websocket_2_handshaking.cpp