Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / beast / example / websocket / client / async-ssl-system-executor / websocket_client_async_ssl_system_executor.cpp
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 //------------------------------------------------------------------------------
11 //
12 // Example: WebSocket SSL client, asynchronous, using system_executor
13 //
14 //------------------------------------------------------------------------------
15
16 #include "example/common/root_certificates.hpp"
17
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>
24 #include <cstdlib>
25 #include <functional>
26 #include <iostream>
27 #include <memory>
28 #include <string>
29
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>
36
37 //------------------------------------------------------------------------------
38
39 // Report a failure
40 void
41 fail(beast::error_code ec, char const* what)
42 {
43     std::cerr << what << ": " << ec.message() << "\n";
44 }
45
46 // Sends a WebSocket message and prints the response
47 class session : public std::enable_shared_from_this<session>
48 {
49     tcp::resolver resolver_;
50     websocket::stream<
51         beast::ssl_stream<beast::tcp_stream>> ws_;
52     beast::flat_buffer buffer_;
53     std::string host_;
54     std::string text_;
55
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)
59         : resolver_(ex)
60         , ws_(ex, ctx)
61     {
62     }
63 public:
64     // Delegate construction to a prive constructor to be able to use
65     // the same strand for both I/O objects.
66     explicit
67     session(ssl::context& ctx)
68         : session(net::make_strand(net::system_executor{}), ctx)
69     {
70     }
71
72     // Start the asynchronous operation
73     void
74     run(
75         char const* host,
76         char const* port,
77         char const* text)
78     {
79         // Save these for later
80         host_ = host;
81         text_ = text;
82
83         // Look up the domain name
84         resolver_.async_resolve(
85             host,
86             port,
87             beast::bind_front_handler(
88                 &session::on_resolve,
89                 shared_from_this()));
90     }
91
92     void
93     on_resolve(
94         beast::error_code ec,
95         tcp::resolver::results_type results)
96     {
97         if(ec)
98             return fail(ec, "resolve");
99
100         // Set a timeout on the operation
101         beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
102
103         // Make the connection on the IP address we get from a lookup
104         beast::get_lowest_layer(ws_).async_connect(
105             results,
106             beast::bind_front_handler(
107                 &session::on_connect,
108                 shared_from_this()));
109     }
110
111     void
112     on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type)
113     {
114         if(ec)
115             return fail(ec, "connect");
116
117         // Set a timeout on the operation
118         beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
119
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()));
126     }
127
128     void
129     on_ssl_handshake(beast::error_code ec)
130     {
131         if(ec)
132             return fail(ec, "ssl_handshake");
133
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();
137
138         // Set suggested timeout settings for the websocket
139         ws_.set_option(
140             websocket::stream_base::timeout::suggested(
141                 beast::role_type::client));
142
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)
146             {
147                 req.set(http::field::user_agent,
148                     std::string(BOOST_BEAST_VERSION_STRING) +
149                         " websocket-client-async-ssl");
150             }));
151
152         // Perform the websocket handshake
153         ws_.async_handshake(host_, "/",
154             beast::bind_front_handler(
155                 &session::on_handshake,
156                 shared_from_this()));
157     }
158
159     void
160     on_handshake(beast::error_code ec)
161     {
162         if(ec)
163             return fail(ec, "handshake");
164
165         // Send the message
166         ws_.async_write(
167             net::buffer(text_),
168             beast::bind_front_handler(
169                 &session::on_write,
170                 shared_from_this()));
171     }
172
173     void
174     on_write(
175         beast::error_code ec,
176         std::size_t bytes_transferred)
177     {
178         boost::ignore_unused(bytes_transferred);
179
180         if(ec)
181             return fail(ec, "write");
182
183         // Read a message into our buffer
184         ws_.async_read(
185             buffer_,
186             beast::bind_front_handler(
187                 &session::on_read,
188                 shared_from_this()));
189     }
190
191     void
192     on_read(
193         beast::error_code ec,
194         std::size_t bytes_transferred)
195     {
196         boost::ignore_unused(bytes_transferred);
197
198         if(ec)
199             return fail(ec, "read");
200
201         // Close the WebSocket connection
202         ws_.async_close(websocket::close_code::normal,
203             beast::bind_front_handler(
204                 &session::on_close,
205                 shared_from_this()));
206     }
207
208     void
209     on_close(beast::error_code ec)
210     {
211         if(ec)
212             return fail(ec, "close");
213
214         // If we get here then the connection is closed gracefully
215
216         // The make_printable() function helps print a ConstBufferSequence
217         std::cout << beast::make_printable(buffer_.data()) << std::endl;
218     }
219 };
220
221 //------------------------------------------------------------------------------
222
223 int main(int argc, char** argv)
224 {
225     // Check command line arguments.
226     if(argc != 4)
227     {
228         std::cerr <<
229             "Usage: websocket-client-async-ssl <host> <port> <text>\n" <<
230             "Example:\n" <<
231             "    websocket-client-async-ssl echo.websocket.org 443 \"Hello, world!\"\n";
232         return EXIT_FAILURE;
233     }
234     auto const host = argv[1];
235     auto const port = argv[2];
236     auto const text = argv[3];
237
238
239     // The SSL context is required, and holds certificates
240     ssl::context ctx{ssl::context::tlsv12_client};
241
242     // This holds the root certificate used for verification
243     load_root_certificates(ctx);
244
245     // Launch the asynchronous operation
246     std::make_shared<session>(ctx)->run(host, port, text);
247
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();
252
253     return EXIT_SUCCESS;
254 }