]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / beast / example / websocket / server / async-ssl / websocket_server_async_ssl.cpp
1 //
2 // Copyright (c) 2016-2017 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 server, asynchronous
13 //
14 //------------------------------------------------------------------------------
15
16 #include "example/common/server_certificate.hpp"
17
18 #include <boost/beast/core.hpp>
19 #include <boost/beast/websocket.hpp>
20 #include <boost/beast/websocket/ssl.hpp>
21 #include <boost/asio/bind_executor.hpp>
22 #include <boost/asio/strand.hpp>
23 #include <boost/asio/ip/tcp.hpp>
24 #include <boost/asio/ssl/stream.hpp>
25 #include <algorithm>
26 #include <cstdlib>
27 #include <functional>
28 #include <iostream>
29 #include <memory>
30 #include <string>
31 #include <thread>
32 #include <vector>
33
34 using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
35 namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
36 namespace websocket = boost::beast::websocket; // from <boost/beast/websocket.hpp>
37
38 //------------------------------------------------------------------------------
39
40 // Report a failure
41 void
42 fail(boost::system::error_code ec, char const* what)
43 {
44 std::cerr << what << ": " << ec.message() << "\n";
45 }
46
47 // Echoes back all received WebSocket messages
48 class session : public std::enable_shared_from_this<session>
49 {
50 tcp::socket socket_;
51 websocket::stream<ssl::stream<tcp::socket&>> ws_;
52 boost::asio::strand<
53 boost::asio::io_context::executor_type> strand_;
54 boost::beast::multi_buffer buffer_;
55
56 public:
57 // Take ownership of the socket
58 session(tcp::socket socket, ssl::context& ctx)
59 : socket_(std::move(socket))
60 , ws_(socket_, ctx)
61 , strand_(ws_.get_executor())
62 {
63 }
64
65 // Start the asynchronous operation
66 void
67 run()
68 {
69 // Perform the SSL handshake
70 ws_.next_layer().async_handshake(
71 ssl::stream_base::server,
72 boost::asio::bind_executor(
73 strand_,
74 std::bind(
75 &session::on_handshake,
76 shared_from_this(),
77 std::placeholders::_1)));
78 }
79
80 void
81 on_handshake(boost::system::error_code ec)
82 {
83 if(ec)
84 return fail(ec, "handshake");
85
86 // Accept the websocket handshake
87 ws_.async_accept(
88 boost::asio::bind_executor(
89 strand_,
90 std::bind(
91 &session::on_accept,
92 shared_from_this(),
93 std::placeholders::_1)));
94 }
95
96 void
97 on_accept(boost::system::error_code ec)
98 {
99 if(ec)
100 return fail(ec, "accept");
101
102 // Read a message
103 do_read();
104 }
105
106 void
107 do_read()
108 {
109 // Read a message into our buffer
110 ws_.async_read(
111 buffer_,
112 boost::asio::bind_executor(
113 strand_,
114 std::bind(
115 &session::on_read,
116 shared_from_this(),
117 std::placeholders::_1,
118 std::placeholders::_2)));
119 }
120
121 void
122 on_read(
123 boost::system::error_code ec,
124 std::size_t bytes_transferred)
125 {
126 boost::ignore_unused(bytes_transferred);
127
128 // This indicates that the session was closed
129 if(ec == websocket::error::closed)
130 return;
131
132 if(ec)
133 fail(ec, "read");
134
135 // Echo the message
136 ws_.text(ws_.got_text());
137 ws_.async_write(
138 buffer_.data(),
139 boost::asio::bind_executor(
140 strand_,
141 std::bind(
142 &session::on_write,
143 shared_from_this(),
144 std::placeholders::_1,
145 std::placeholders::_2)));
146 }
147
148 void
149 on_write(
150 boost::system::error_code ec,
151 std::size_t bytes_transferred)
152 {
153 boost::ignore_unused(bytes_transferred);
154
155 if(ec)
156 return fail(ec, "write");
157
158 // Clear the buffer
159 buffer_.consume(buffer_.size());
160
161 // Do another read
162 do_read();
163 }
164 };
165
166 //------------------------------------------------------------------------------
167
168 // Accepts incoming connections and launches the sessions
169 class listener : public std::enable_shared_from_this<listener>
170 {
171 ssl::context& ctx_;
172 tcp::acceptor acceptor_;
173 tcp::socket socket_;
174
175 public:
176 listener(
177 boost::asio::io_context& ioc,
178 ssl::context& ctx,
179 tcp::endpoint endpoint)
180 : ctx_(ctx)
181 , acceptor_(ioc)
182 , socket_(ioc)
183 {
184 boost::system::error_code ec;
185
186 // Open the acceptor
187 acceptor_.open(endpoint.protocol(), ec);
188 if(ec)
189 {
190 fail(ec, "open");
191 return;
192 }
193
194 // Bind to the server address
195 acceptor_.bind(endpoint, ec);
196 if(ec)
197 {
198 fail(ec, "bind");
199 return;
200 }
201
202 // Start listening for connections
203 acceptor_.listen(
204 boost::asio::socket_base::max_listen_connections, ec);
205 if(ec)
206 {
207 fail(ec, "listen");
208 return;
209 }
210 }
211
212 // Start accepting incoming connections
213 void
214 run()
215 {
216 if(! acceptor_.is_open())
217 return;
218 do_accept();
219 }
220
221 void
222 do_accept()
223 {
224 acceptor_.async_accept(
225 socket_,
226 std::bind(
227 &listener::on_accept,
228 shared_from_this(),
229 std::placeholders::_1));
230 }
231
232 void
233 on_accept(boost::system::error_code ec)
234 {
235 if(ec)
236 {
237 fail(ec, "accept");
238 }
239 else
240 {
241 // Create the session and run it
242 std::make_shared<session>(std::move(socket_), ctx_)->run();
243 }
244
245 // Accept another connection
246 do_accept();
247 }
248 };
249
250 //------------------------------------------------------------------------------
251
252 int main(int argc, char* argv[])
253 {
254 // Check command line arguments.
255 if (argc != 4)
256 {
257 std::cerr <<
258 "Usage: websocket-server-async-ssl <address> <port> <threads>\n" <<
259 "Example:\n" <<
260 " websocket-server-async-ssl 0.0.0.0 8080 1\n";
261 return EXIT_FAILURE;
262 }
263 auto const address = boost::asio::ip::make_address(argv[1]);
264 auto const port = static_cast<unsigned short>(std::atoi(argv[2]));
265 auto const threads = std::max<int>(1, std::atoi(argv[3]));
266
267 // The io_context is required for all I/O
268 boost::asio::io_context ioc{threads};
269
270 // The SSL context is required, and holds certificates
271 ssl::context ctx{ssl::context::sslv23};
272
273 // This holds the self-signed certificate used by the server
274 load_server_certificate(ctx);
275
276 // Create and launch a listening port
277 std::make_shared<listener>(ioc, ctx, tcp::endpoint{address, port})->run();
278
279 // Run the I/O service on the requested number of threads
280 std::vector<std::thread> v;
281 v.reserve(threads - 1);
282 for(auto i = threads - 1; i > 0; --i)
283 v.emplace_back(
284 [&ioc]
285 {
286 ioc.run();
287 });
288 ioc.run();
289
290 return EXIT_SUCCESS;
291 }