]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
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, coroutine | |
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/ip/tcp.hpp> | |
22 | #include <boost/asio/spawn.hpp> | |
23 | #include <boost/asio/ssl/stream.hpp> | |
24 | #include <algorithm> | |
25 | #include <cstdlib> | |
26 | #include <functional> | |
27 | #include <iostream> | |
28 | #include <memory> | |
29 | #include <string> | |
30 | #include <thread> | |
31 | #include <vector> | |
32 | ||
33 | using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> | |
34 | namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> | |
35 | namespace websocket = boost::beast::websocket; // from <boost/beast/websocket.hpp> | |
36 | ||
37 | //------------------------------------------------------------------------------ | |
38 | ||
39 | // Report a failure | |
40 | void | |
41 | fail(boost::system::error_code ec, char const* what) | |
42 | { | |
43 | std::cerr << what << ": " << ec.message() << "\n"; | |
44 | } | |
45 | ||
46 | // Echoes back all received WebSocket messages | |
47 | void | |
48 | do_session( | |
49 | tcp::socket& socket, | |
50 | ssl::context& ctx, | |
51 | boost::asio::yield_context yield) | |
52 | { | |
53 | boost::system::error_code ec; | |
54 | ||
55 | // Construct the stream by moving in the socket | |
56 | websocket::stream<ssl::stream<tcp::socket&>> ws{socket, ctx}; | |
57 | ||
58 | // Perform the SSL handshake | |
59 | ws.next_layer().async_handshake(ssl::stream_base::server, yield[ec]); | |
60 | if(ec) | |
61 | return fail(ec, "handshake"); | |
62 | ||
63 | // Accept the websocket handshake | |
64 | ws.async_accept(yield[ec]); | |
65 | if(ec) | |
66 | return fail(ec, "accept"); | |
67 | ||
68 | for(;;) | |
69 | { | |
70 | // This buffer will hold the incoming message | |
71 | boost::beast::multi_buffer buffer; | |
72 | ||
73 | // Read a message | |
74 | ws.async_read(buffer, yield[ec]); | |
75 | ||
76 | // This indicates that the session was closed | |
77 | if(ec == websocket::error::closed) | |
78 | break; | |
79 | ||
80 | if(ec) | |
81 | return fail(ec, "read"); | |
82 | ||
83 | // Echo the message back | |
84 | ws.text(ws.got_text()); | |
85 | ws.async_write(buffer.data(), yield[ec]); | |
86 | if(ec) | |
87 | return fail(ec, "write"); | |
88 | } | |
89 | } | |
90 | ||
91 | //------------------------------------------------------------------------------ | |
92 | ||
93 | // Accepts incoming connections and launches the sessions | |
94 | void | |
95 | do_listen( | |
96 | boost::asio::io_context& ioc, | |
97 | ssl::context& ctx, | |
98 | tcp::endpoint endpoint, | |
99 | boost::asio::yield_context yield) | |
100 | { | |
101 | boost::system::error_code ec; | |
102 | ||
103 | // Open the acceptor | |
104 | tcp::acceptor acceptor(ioc); | |
105 | acceptor.open(endpoint.protocol(), ec); | |
106 | if(ec) | |
107 | return fail(ec, "open"); | |
11fdf7f2 TL |
108 | |
109 | // Allow address reuse | |
110 | acceptor.set_option(boost::asio::socket_base::reuse_address(true)); | |
111 | if(ec) | |
112 | return fail(ec, "set_option"); | |
b32b8144 FG |
113 | |
114 | // Bind to the server address | |
115 | acceptor.bind(endpoint, ec); | |
116 | if(ec) | |
117 | return fail(ec, "bind"); | |
118 | ||
119 | // Start listening for connections | |
120 | acceptor.listen(boost::asio::socket_base::max_listen_connections, ec); | |
121 | if(ec) | |
122 | return fail(ec, "listen"); | |
123 | ||
124 | for(;;) | |
125 | { | |
126 | tcp::socket socket(ioc); | |
127 | acceptor.async_accept(socket, yield[ec]); | |
128 | if(ec) | |
129 | fail(ec, "accept"); | |
130 | else | |
131 | boost::asio::spawn( | |
132 | acceptor.get_executor().context(), | |
133 | std::bind( | |
134 | &do_session, | |
135 | std::move(socket), | |
136 | std::ref(ctx), | |
137 | std::placeholders::_1)); | |
138 | } | |
139 | } | |
140 | ||
141 | int main(int argc, char* argv[]) | |
142 | { | |
143 | // Check command line arguments. | |
144 | if (argc != 4) | |
145 | { | |
146 | std::cerr << | |
147 | "Usage: websocket-server-coro-ssl <address> <port> <threads>\n" << | |
148 | "Example:\n" << | |
149 | " websocket-server-coro-ssl 0.0.0.0 8080 1\n"; | |
150 | return EXIT_FAILURE; | |
151 | } | |
152 | auto const address = boost::asio::ip::make_address(argv[1]); | |
153 | auto const port = static_cast<unsigned short>(std::atoi(argv[2])); | |
154 | auto const threads = std::max<int>(1, std::atoi(argv[3])); | |
155 | ||
156 | // The io_context is required for all I/O | |
157 | boost::asio::io_context ioc{threads}; | |
158 | ||
159 | // The SSL context is required, and holds certificates | |
160 | ssl::context ctx{ssl::context::sslv23}; | |
161 | ||
162 | // This holds the self-signed certificate used by the server | |
163 | load_server_certificate(ctx); | |
164 | ||
165 | // Spawn a listening port | |
166 | boost::asio::spawn(ioc, | |
167 | std::bind( | |
168 | &do_listen, | |
169 | std::ref(ioc), | |
170 | std::ref(ctx), | |
171 | tcp::endpoint{address, port}, | |
172 | std::placeholders::_1)); | |
173 | ||
174 | // Run the I/O service on the requested number of threads | |
175 | std::vector<std::thread> v; | |
176 | v.reserve(threads - 1); | |
177 | for(auto i = threads - 1; i > 0; --i) | |
178 | v.emplace_back( | |
179 | [&ioc] | |
180 | { | |
181 | ioc.run(); | |
182 | }); | |
183 | ioc.run(); | |
184 | ||
185 | return EXIT_SUCCESS; | |
186 | } |