2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 //------------------------------------------------------------------------------
12 // Example: WebSocket SSL client, asynchronous, using system_executor
14 //------------------------------------------------------------------------------
16 #include "example/common/root_certificates.hpp"
18 #include <boost/beast/core.hpp>
19 #include <boost/beast/ssl.hpp>
20 #include <boost/beast/websocket.hpp>
21 #include <boost/beast/websocket/ssl.hpp>
22 #include <boost/asio/strand.hpp>
23 #include <boost/asio/system_executor.hpp>
30 namespace beast
= boost::beast
; // from <boost/beast.hpp>
31 namespace http
= beast::http
; // from <boost/beast/http.hpp>
32 namespace websocket
= beast::websocket
; // from <boost/beast/websocket.hpp>
33 namespace net
= boost::asio
; // from <boost/asio.hpp>
34 namespace ssl
= boost::asio::ssl
; // from <boost/asio/ssl.hpp>
35 using tcp
= boost::asio::ip::tcp
; // from <boost/asio/ip/tcp.hpp>
37 //------------------------------------------------------------------------------
41 fail(beast::error_code ec
, char const* what
)
43 std::cerr
<< what
<< ": " << ec
.message() << "\n";
46 // Sends a WebSocket message and prints the response
47 class session
: public std::enable_shared_from_this
<session
>
49 tcp::resolver resolver_
;
51 beast::ssl_stream
<beast::tcp_stream
>> ws_
;
52 beast::flat_buffer buffer_
;
56 // Objects are constructed with a strand to
57 // ensure that handlers do not execute concurrently.
58 session(net::strand
<net::system_executor
> ex
, ssl::context
& ctx
)
64 // Delegate construction to a prive constructor to be able to use
65 // the same strand for both I/O objects.
67 session(ssl::context
& ctx
)
68 : session(net::make_strand(net::system_executor
{}), ctx
)
72 // Start the asynchronous operation
79 // Save these for later
83 // Look up the domain name
84 resolver_
.async_resolve(
87 beast::bind_front_handler(
95 tcp::resolver::results_type results
)
98 return fail(ec
, "resolve");
100 // Set a timeout on the operation
101 beast::get_lowest_layer(ws_
).expires_after(std::chrono::seconds(30));
103 // Make the connection on the IP address we get from a lookup
104 beast::get_lowest_layer(ws_
).async_connect(
106 beast::bind_front_handler(
107 &session::on_connect
,
108 shared_from_this()));
112 on_connect(beast::error_code ec
, tcp::resolver::results_type::endpoint_type ep
)
115 return fail(ec
, "connect");
117 // Update the host_ string. This will provide the value of the
118 // Host HTTP header during the WebSocket handshake.
119 // See https://tools.ietf.org/html/rfc7230#section-5.4
120 host_
+= ':' + std::to_string(ep
.port());
122 // Set a timeout on the operation
123 beast::get_lowest_layer(ws_
).expires_after(std::chrono::seconds(30));
125 // Set SNI Hostname (many hosts need this to handshake successfully)
126 if(! SSL_set_tlsext_host_name(
127 ws_
.next_layer().native_handle(),
130 ec
= beast::error_code(static_cast<int>(::ERR_get_error()),
131 net::error::get_ssl_category());
132 return fail(ec
, "connect");
135 // Perform the SSL handshake
136 ws_
.next_layer().async_handshake(
137 ssl::stream_base::client
,
138 beast::bind_front_handler(
139 &session::on_ssl_handshake
,
140 shared_from_this()));
144 on_ssl_handshake(beast::error_code ec
)
147 return fail(ec
, "ssl_handshake");
149 // Turn off the timeout on the tcp_stream, because
150 // the websocket stream has its own timeout system.
151 beast::get_lowest_layer(ws_
).expires_never();
153 // Set suggested timeout settings for the websocket
155 websocket::stream_base::timeout::suggested(
156 beast::role_type::client
));
158 // Set a decorator to change the User-Agent of the handshake
159 ws_
.set_option(websocket::stream_base::decorator(
160 [](websocket::request_type
& req
)
162 req
.set(http::field::user_agent
,
163 std::string(BOOST_BEAST_VERSION_STRING
) +
164 " websocket-client-async-ssl");
167 // Perform the websocket handshake
168 ws_
.async_handshake(host_
, "/",
169 beast::bind_front_handler(
170 &session::on_handshake
,
171 shared_from_this()));
175 on_handshake(beast::error_code ec
)
178 return fail(ec
, "handshake");
183 beast::bind_front_handler(
185 shared_from_this()));
190 beast::error_code ec
,
191 std::size_t bytes_transferred
)
193 boost::ignore_unused(bytes_transferred
);
196 return fail(ec
, "write");
198 // Read a message into our buffer
201 beast::bind_front_handler(
203 shared_from_this()));
208 beast::error_code ec
,
209 std::size_t bytes_transferred
)
211 boost::ignore_unused(bytes_transferred
);
214 return fail(ec
, "read");
216 // Close the WebSocket connection
217 ws_
.async_close(websocket::close_code::normal
,
218 beast::bind_front_handler(
220 shared_from_this()));
224 on_close(beast::error_code ec
)
227 return fail(ec
, "close");
229 // If we get here then the connection is closed gracefully
231 // The make_printable() function helps print a ConstBufferSequence
232 std::cout
<< beast::make_printable(buffer_
.data()) << std::endl
;
236 //------------------------------------------------------------------------------
238 int main(int argc
, char** argv
)
240 // Check command line arguments.
244 "Usage: websocket-client-async-ssl <host> <port> <text>\n" <<
246 " websocket-client-async-ssl echo.websocket.org 443 \"Hello, world!\"\n";
249 auto const host
= argv
[1];
250 auto const port
= argv
[2];
251 auto const text
= argv
[3];
254 // The SSL context is required, and holds certificates
255 ssl::context ctx
{ssl::context::tlsv12_client
};
257 // This holds the root certificate used for verification
258 load_root_certificates(ctx
);
260 // Launch the asynchronous operation
261 std::make_shared
<session
>(ctx
)->run(host
, port
, text
);
263 // The async operations will run on the system_executor.
264 // Because the main thread has nothing to do in this example, we just wait
265 // for the system_executor to run out of work.
266 net::query(net::system_executor(), net::execution::context
).join();