CppServer 1.0.5.0
C++ Server Library
Loading...
Searching...
No Matches
ssl_server.cpp
Go to the documentation of this file.
1
10
11namespace CppServer {
12namespace Asio {
13
14SSLServer::SSLServer(const std::shared_ptr<Service>& service, const std::shared_ptr<SSLContext>& context, int port, InternetProtocol protocol)
15 : _id(CppCommon::UUID::Sequential()),
16 _service(service),
17 _io_service(_service->GetAsioService()),
18 _strand(*_io_service),
19 _strand_required(_service->IsStrandRequired()),
20 _port(port),
21 _context(context),
22 _acceptor(*_io_service),
23 _started(false),
24 _bytes_pending(0),
25 _bytes_sent(0),
26 _bytes_received(0),
27 _option_keep_alive(false),
28 _option_no_delay(false),
29 _option_reuse_address(false),
30 _option_reuse_port(false)
31{
32 assert((service != nullptr) && "Asio service is invalid!");
33 if (service == nullptr)
34 throw CppCommon::ArgumentException("Asio service is invalid!");
35
36 assert((context != nullptr) && "SSL context is invalid!");
37 if (context == nullptr)
38 throw CppCommon::ArgumentException("SSL context is invalid!");
39
40 // Prepare endpoint
41 switch (protocol)
42 {
44 _endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v4(), (unsigned short)port);
45 break;
47 _endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v6(), (unsigned short)port);
48 break;
49 }
50}
51
52SSLServer::SSLServer(const std::shared_ptr<Service>& service, const std::shared_ptr<SSLContext>& context, const std::string& address, int port)
53 : _id(CppCommon::UUID::Sequential()),
54 _service(service),
55 _io_service(_service->GetAsioService()),
56 _strand(*_io_service),
57 _strand_required(_service->IsStrandRequired()),
58 _address(address),
59 _port(port),
60 _context(context),
61 _acceptor(*_io_service),
62 _started(false),
63 _bytes_pending(0),
64 _bytes_sent(0),
65 _bytes_received(0),
66 _option_keep_alive(false),
67 _option_no_delay(false),
68 _option_reuse_address(false),
69 _option_reuse_port(false)
70{
71 assert((service != nullptr) && "Asio service is invalid!");
72 if (service == nullptr)
73 throw CppCommon::ArgumentException("Asio service is invalid!");
74
75 assert((context != nullptr) && "SSL context is invalid!");
76 if (context == nullptr)
77 throw CppCommon::ArgumentException("SSL context is invalid!");
78
79 // Prepare endpoint
80 _endpoint = asio::ip::tcp::endpoint(asio::ip::make_address(address), (unsigned short)port);
81}
82
83SSLServer::SSLServer(const std::shared_ptr<Service>& service, const std::shared_ptr<SSLContext>& context, const asio::ip::tcp::endpoint& endpoint)
84 : _id(CppCommon::UUID::Sequential()),
85 _service(service),
86 _io_service(_service->GetAsioService()),
87 _strand(*_io_service),
88 _strand_required(_service->IsStrandRequired()),
89 _address(endpoint.address().to_string()),
90 _port(endpoint.port()),
91 _context(context),
92 _endpoint(endpoint),
93 _acceptor(*_io_service),
94 _started(false),
95 _bytes_pending(0),
96 _bytes_sent(0),
97 _bytes_received(0),
98 _option_keep_alive(false),
99 _option_no_delay(false),
100 _option_reuse_address(false),
101 _option_reuse_port(false)
102{
103 assert((service != nullptr) && "Asio service is invalid!");
104 if (service == nullptr)
105 throw CppCommon::ArgumentException("Asio service is invalid!");
106
107 assert((context != nullptr) && "SSL context is invalid!");
108 if (context == nullptr)
109 throw CppCommon::ArgumentException("SSL context is invalid!");
110}
111
113{
114 assert(!IsStarted() && "SSL server is already started!");
115 if (IsStarted())
116 return false;
117
118 // Post the start handler
119 auto self(this->shared_from_this());
120 auto start_handler = [this, self]()
121 {
122 if (IsStarted())
123 return;
124
125 // Create a server acceptor
126 _acceptor = asio::ip::tcp::acceptor(*_io_service);
127 _acceptor.open(_endpoint.protocol());
129 _acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
130#if (defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)) && !defined(__CYGWIN__)
131 if (option_reuse_port())
132 {
133 typedef asio::detail::socket_option::boolean<SOL_SOCKET, SO_REUSEPORT> reuse_port;
134 _acceptor.set_option(reuse_port(true));
135 }
136#endif
137 _acceptor.bind(_endpoint);
138 _acceptor.listen();
139
140 // Reset statistic
141 _bytes_pending = 0;
142 _bytes_sent = 0;
143 _bytes_received = 0;
144
145 // Update the started flag
146 _started = true;
147
148 // Call the server started handler
149 onStarted();
150
151 // Perform the first server accept
152 Accept();
153 };
154 if (_strand_required)
155 _strand.post(start_handler);
156 else
157 _io_service->post(start_handler);
158
159 return true;
160}
161
163{
164 assert(IsStarted() && "SSL server is not started!");
165 if (!IsStarted())
166 return false;
167
168 // Post the stop handler
169 auto self(this->shared_from_this());
170 auto stop_handler = [this, self]()
171 {
172 if (!IsStarted())
173 return;
174
175 // Close the server acceptor
176 _acceptor.close();
177
178 // Reset the session
179 _session->ResetServer();
180
181 // Disconnect all sessions
183
184 // Update the started flag
185 _started = false;
186
187 // Clear multicast buffer
188 ClearBuffers();
189
190 // Call the server stopped handler
191 onStopped();
192 };
193 if (_strand_required)
194 _strand.post(stop_handler);
195 else
196 _io_service->post(stop_handler);
197
198 return true;
199}
200
202{
203 if (!Stop())
204 return false;
205
206 while (IsStarted())
207 CppCommon::Thread::Yield();
208
209 return Start();
210}
211
212void SSLServer::Accept()
213{
214 if (!IsStarted())
215 return;
216
217 // Dispatch the accept handler
218 auto self(this->shared_from_this());
219 auto accept_handler = make_alloc_handler(_acceptor_storage, [this, self]()
220 {
221 if (!IsStarted())
222 return;
223
224 // Create a new session to accept
225 _session = CreateSession(self);
226
227 auto async_accept_handler = make_alloc_handler(_acceptor_storage, [this, self](std::error_code ec)
228 {
229 if (!ec)
230 {
231 RegisterSession();
232
233 // Connect a new session
234 _session->Connect();
235 }
236 else
237 SendError(ec);
238
239 // Perform the next server accept
240 Accept();
241 });
242 if (_strand_required)
243 _acceptor.async_accept(_session->socket(), bind_executor(_strand, async_accept_handler));
244 else
245 _acceptor.async_accept(_session->socket(), async_accept_handler);
246 });
247 if (_strand_required)
248 _strand.dispatch(accept_handler);
249 else
250 _io_service->dispatch(accept_handler);
251}
252
253bool SSLServer::Multicast(const void* buffer, size_t size)
254{
255 if (!IsStarted())
256 return false;
257
258 if (size == 0)
259 return true;
260
261 assert((buffer != nullptr) && "Pointer to the buffer should not be null!");
262 if (buffer == nullptr)
263 return false;
264
265 std::shared_lock<std::shared_mutex> locker(_sessions_lock);
266
267 // Multicast all sessions
268 for (auto& session : _sessions)
269 session.second->SendAsync(buffer, size);
270
271 return true;
272}
273
275{
276 if (!IsStarted())
277 return false;
278
279 // Dispatch the disconnect all handler
280 auto self(this->shared_from_this());
281 auto disconnect_all_handler = [this, self]()
282 {
283 if (!IsStarted())
284 return;
285
286 std::shared_lock<std::shared_mutex> locker(_sessions_lock);
287
288 // Disconnect all sessions
289 for (auto& session : _sessions)
290 session.second->Disconnect();
291 };
292 if (_strand_required)
293 _strand.dispatch(disconnect_all_handler);
294 else
295 _io_service->dispatch(disconnect_all_handler);
296
297 return true;
298}
299
300std::shared_ptr<SSLSession> SSLServer::FindSession(const CppCommon::UUID& id)
301{
302 std::shared_lock<std::shared_mutex> locker(_sessions_lock);
303
304 // Try to find the required session
305 auto it = _sessions.find(id);
306 return (it != _sessions.end()) ? it->second : nullptr;
307}
308
309void SSLServer::RegisterSession()
310{
311 std::unique_lock<std::shared_mutex> locker(_sessions_lock);
312
313 // Register a new session
314 _sessions.emplace(_session->id(), _session);
315}
316
317void SSLServer::UnregisterSession(const CppCommon::UUID& id)
318{
319 std::unique_lock<std::shared_mutex> locker(_sessions_lock);
320
321 // Try to find the unregistered session
322 auto it = _sessions.find(id);
323 if (it != _sessions.end())
324 {
325 // Erase the session
326 _sessions.erase(it);
327 }
328}
329
330void SSLServer::ClearBuffers()
331{
332 // Update statistic
333 _bytes_pending = 0;
334}
335
336void SSLServer::SendError(std::error_code ec)
337{
338 // Skip Asio disconnect errors
339 if ((ec == asio::error::connection_aborted) ||
340 (ec == asio::error::connection_refused) ||
341 (ec == asio::error::connection_reset) ||
342 (ec == asio::error::eof) ||
343 (ec == asio::error::operation_aborted))
344 return;
345
346 // Skip Winsock error 995: The I/O operation has been aborted because of either a thread exit or an application request
347 if (ec.value() == 995)
348 return;
349
350 onError(ec.value(), ec.category().name(), ec.message());
351}
352
353} // namespace Asio
354} // namespace CppServer
Asio allocate handler wrapper.
Definition memory.h:133
bool IsStarted() const noexcept
Is the server started?
Definition ssl_server.h:106
std::shared_mutex _sessions_lock
Definition ssl_server.h:222
std::shared_ptr< SSLSession > FindSession(const CppCommon::UUID &id)
Find a session with a given Id.
virtual void onStopped()
Handle server stopped notification.
Definition ssl_server.h:194
virtual bool Multicast(const void *buffer, size_t size)
Multicast data to all connected sessions.
virtual bool Stop()
Stop the server.
int port() const noexcept
Get the server port number.
Definition ssl_server.h:85
std::shared_ptr< Service > & service() noexcept
Get the Asio service.
Definition ssl_server.h:70
std::map< CppCommon::UUID, std::shared_ptr< SSLSession > > _sessions
Definition ssl_server.h:223
virtual bool DisconnectAll()
Disconnect all connected sessions.
const std::string & address() const noexcept
Get the server address.
Definition ssl_server.h:83
virtual void onStarted()
Handle server started notification.
Definition ssl_server.h:192
bool option_reuse_port() const noexcept
Get the option: reuse port.
Definition ssl_server.h:103
std::shared_ptr< SSLContext > & context() noexcept
Get the server SSL context.
Definition ssl_server.h:76
virtual void onError(int error, const std::string &category, const std::string &message)
Handle error notification.
Definition ssl_server.h:218
SSLServer(const std::shared_ptr< Service > &service, const std::shared_ptr< SSLContext > &context, int port, InternetProtocol protocol=InternetProtocol::IPv4)
Initialize SSL server with a given Asio service, SSL context and port number.
virtual bool Restart()
Restart the server.
virtual bool Start()
Start the server.
bool option_reuse_address() const noexcept
Get the option: reuse address.
Definition ssl_server.h:101
virtual std::shared_ptr< SSLSession > CreateSession(const std::shared_ptr< SSLServer > &server)
Create SSL session factory method.
Definition ssl_server.h:188
AllocateHandler< THandler > make_alloc_handler(HandlerStorage &storage, THandler handler)
Helper function to wrap a handler object to add custom allocation.
Definition memory.inl:39
InternetProtocol
Internet protocol.
Definition asio.h:66
@ IPv4
Internet Protocol version 4.
@ IPv6
Internet Protocol version 6.
C++ Server project definitions.
Definition asio.h:56
SSL server definition.