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)
115 return fail(ec, "connect");
117 // Set a timeout on the operation
118 beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
120 // Perform the SSL handshake
121 ws_.next_layer().async_handshake(
122 ssl::stream_base::client,
123 beast::bind_front_handler(
124 &session::on_ssl_handshake,
125 shared_from_this()));
129 on_ssl_handshake(beast::error_code ec)
132 return fail(ec, "ssl_handshake");
134 // Turn off the timeout on the tcp_stream, because
135 // the websocket stream has its own timeout system.
136 beast::get_lowest_layer(ws_).expires_never();
138 // Set suggested timeout settings for the websocket
140 websocket::stream_base::timeout::suggested(
141 beast::role_type::client));
143 // Set a decorator to change the User-Agent of the handshake
144 ws_.set_option(websocket::stream_base::decorator(
145 [](websocket::request_type& req)
147 req.set(http::field::user_agent,
148 std::string(BOOST_BEAST_VERSION_STRING) +
149 " websocket-client-async-ssl");
152 // Perform the websocket handshake
153 ws_.async_handshake(host_, "/",
154 beast::bind_front_handler(
155 &session::on_handshake,
156 shared_from_this()));
160 on_handshake(beast::error_code ec)
163 return fail(ec, "handshake");
168 beast::bind_front_handler(
170 shared_from_this()));
175 beast::error_code ec,
176 std::size_t bytes_transferred)
178 boost::ignore_unused(bytes_transferred);
181 return fail(ec, "write");
183 // Read a message into our buffer
186 beast::bind_front_handler(
188 shared_from_this()));
193 beast::error_code ec,
194 std::size_t bytes_transferred)
196 boost::ignore_unused(bytes_transferred);
199 return fail(ec, "read");
201 // Close the WebSocket connection
202 ws_.async_close(websocket::close_code::normal,
203 beast::bind_front_handler(
205 shared_from_this()));
209 on_close(beast::error_code ec)
212 return fail(ec, "close");
214 // If we get here then the connection is closed gracefully
216 // The make_printable() function helps print a ConstBufferSequence
217 std::cout << beast::make_printable(buffer_.data()) << std::endl;
221 //------------------------------------------------------------------------------
223 int main(int argc, char** argv)
225 // Check command line arguments.
229 "Usage: websocket-client-async-ssl <host> <port> <text>\n" <<
231 " websocket-client-async-ssl echo.websocket.org 443 \"Hello, world!\"\n";
234 auto const host = argv[1];
235 auto const port = argv[2];
236 auto const text = argv[3];
239 // The SSL context is required, and holds certificates
240 ssl::context ctx{ssl::context::tlsv12_client};
242 // This holds the root certificate used for verification
243 load_root_certificates(ctx);
245 // Launch the asynchronous operation
246 std::make_shared<session>(ctx)->run(host, port, text);
248 // The async operations will run on the system_executor.
249 // Because the main thread has nothing to do in this example, we just wait
250 // for the system_executor to run out of work.
251 net::system_executor().context().join();