CppServer  1.0.4.0
C++ Server Library
ssl_server.cpp
Go to the documentation of this file.
1 
10 
11 namespace CppServer {
12 namespace Asio {
13 
14 SSLServer::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 
52 SSLServer::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 
83 SSLServer::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());
128  if (option_reuse_address())
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
182  DisconnectAll();
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 
212 void 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 
253 bool 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 
300 std::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 
309 void 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 
317 void 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 
330 void SSLServer::ClearBuffers()
331 {
332  // Update statistic
333  _bytes_pending = 0;
334 }
335 
336 void 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
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.
Definition: ssl_server.cpp:300
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.
Definition: ssl_server.cpp:253
virtual bool Stop()
Stop the server.
Definition: ssl_server.cpp:162
int port() const noexcept
Get the server port number.
Definition: ssl_server.h:85
std::map< CppCommon::UUID, std::shared_ptr< SSLSession > > _sessions
Definition: ssl_server.h:223
virtual bool DisconnectAll()
Disconnect all connected sessions.
Definition: ssl_server.cpp:274
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
const std::string & address() const noexcept
Get the server address.
Definition: ssl_server.h:83
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.
Definition: ssl_server.cpp:14
virtual bool Restart()
Restart the server.
Definition: ssl_server.cpp:201
virtual std::shared_ptr< SSLSession > CreateSession(const std::shared_ptr< SSLServer > &server)
Create SSL session factory method.
Definition: ssl_server.h:188
std::shared_ptr< Service > & service() noexcept
Get the Asio service.
Definition: ssl_server.h:70
virtual bool Start()
Start the server.
Definition: ssl_server.cpp:112
bool option_reuse_address() const noexcept
Get the option: reuse address.
Definition: ssl_server.h:101
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.