irccd  3.0.3
acceptor.hpp
1 /*
2  * acceptor.hpp -- abstract stream acceptor interface
3  *
4  * Copyright (c) 2013-2019 David Demelier <markand@malikania.fr>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #ifndef IRCCD_ACCEPTOR_HPP
20 #define IRCCD_ACCEPTOR_HPP
21 
27 #include <irccd/sysconfig.hpp>
28 
29 #include <cassert>
30 #include <functional>
31 #include <memory>
32 #include <system_error>
33 
34 #include <boost/asio.hpp>
35 #include <boost/filesystem/path.hpp>
36 
37 #if defined(IRCCD_HAVE_SSL)
38 # include <boost/asio/ssl.hpp>
39 #endif
40 
41 #include "stream.hpp"
42 
43 namespace irccd {
44 
52 class acceptor {
53 public:
57  using handler = std::function<void (std::error_code, std::shared_ptr<stream>)>;
58 
59 public:
63  acceptor() = default;
64 
68  virtual ~acceptor() = default;
69 
80  virtual void accept(handler handler) = 0;
81 };
82 
83 // {{{ basic_socket_acceptor
84 
89 template <typename Acceptor>
91 public:
95  using socket_type = typename Acceptor::protocol_type::socket;
96 
97 private:
98 #if !defined(NDEBUG)
99  bool is_accepting_{false};
100 #endif
101 
102 protected:
106  boost::asio::io_context& service_;
107 
111  Acceptor acceptor_;
112 
113 public:
119  basic_socket_acceptor(boost::asio::io_context& service);
120 
128  basic_socket_acceptor(boost::asio::io_context& service, Acceptor acceptor) noexcept;
129 
135  auto get_service() const noexcept -> const boost::asio::io_context&;
136 
142  auto get_service() noexcept -> boost::asio::io_context&;
143 
149  auto get_acceptor() const noexcept -> const Acceptor&;
150 
156  auto get_acceptor() noexcept -> Acceptor&;
157 
166  template <typename Socket, typename Handler>
167  void accept(Socket& sc, Handler handler);
168 };
169 
170 template <typename Acceptor>
171 inline basic_socket_acceptor<Acceptor>::basic_socket_acceptor(boost::asio::io_context& service)
172  : service_(service)
173  , acceptor_(service)
174 {
175 }
176 
177 template <typename Acceptor>
178 inline basic_socket_acceptor<Acceptor>::basic_socket_acceptor(boost::asio::io_context& service, Acceptor acceptor) noexcept
179  : service_(service)
180  , acceptor_(std::move(acceptor))
181 {
182 }
183 
184 template <typename Acceptor>
185 inline auto basic_socket_acceptor<Acceptor>::get_service() const noexcept -> const boost::asio::io_context&
186 {
187  return service_;
188 }
189 
190 template <typename Acceptor>
191 inline auto basic_socket_acceptor<Acceptor>::get_service() noexcept -> boost::asio::io_context&
192 {
193  return service_;
194 }
195 
196 template <typename Acceptor>
197 inline auto basic_socket_acceptor<Acceptor>::get_acceptor() const noexcept -> const Acceptor&
198 {
199  return acceptor_;
200 }
201 
202 template <typename Acceptor>
203 inline auto basic_socket_acceptor<Acceptor>::get_acceptor() noexcept -> Acceptor&
204 {
205  return acceptor_;
206 }
207 
208 template <typename Acceptor>
209 template <typename Socket, typename Handler>
210 inline void basic_socket_acceptor<Acceptor>::accept(Socket& sc, Handler handler)
211 {
212 #if !defined(NDEBUG)
213  assert(!is_accepting_);
214 
215  is_accepting_ = true;
216 #endif
217 
218  assert(acceptor_.is_open());
219 
220  acceptor_.async_accept(sc, [this, handler] (auto code) {
221 #if !defined(NDEBUG)
222  is_accepting_ = false;
223 #endif
224  (void)this;
225  handler(std::move(code));
226  });
227 }
228 
229 // }}}
230 
231 // {{{ ip_acceptor
232 
237 class ip_acceptor : public basic_socket_acceptor<boost::asio::ip::tcp::acceptor> {
238 private:
239  void open(bool ipv6);
240  void set(bool ipv4, bool ipv6);
241  void bind(const std::string& address, std::uint16_t port, bool ipv6);
242 
243 public:
259  ip_acceptor(boost::asio::io_context& service,
260  std::string address,
261  std::uint16_t port,
262  bool ipv4 = true,
263  bool ipv6 = true);
264 
269 
274 
278  void accept(handler handler) override;
279 };
280 
281 inline void ip_acceptor::open(bool ipv6)
282 {
283  using boost::asio::ip::tcp;
284 
285  if (ipv6)
286  acceptor_.open(tcp::v6());
287  else
288  acceptor_.open(tcp::v4());
289 }
290 
291 inline void ip_acceptor::set(bool ipv4, bool ipv6)
292 {
293  using boost::asio::socket_base;
294  using boost::asio::ip::v6_only;
295 
296  if (ipv6)
297  acceptor_.set_option(v6_only(!ipv4));
298 
299  acceptor_.set_option(socket_base::reuse_address(true));
300 }
301 
302 inline void ip_acceptor::bind(const std::string& address, std::uint16_t port, bool ipv6)
303 {
304  using boost::asio::ip::make_address_v4;
305  using boost::asio::ip::make_address_v6;
306  using boost::asio::ip::tcp;
307 
308  tcp::endpoint ep;
309 
310  if (address == "*")
311  ep = tcp::endpoint(ipv6 ? tcp::v6() : tcp::v4(), port);
312  else if (ipv6)
313  ep = tcp::endpoint(make_address_v6(address), port);
314  else
315  ep = tcp::endpoint(make_address_v4(address), port);
316 
317  acceptor_.bind(ep);
318  acceptor_.listen();
319 }
320 
321 inline ip_acceptor::ip_acceptor(boost::asio::io_context& service,
322  std::string address,
323  std::uint16_t port,
324  bool ipv4,
325  bool ipv6)
326  : basic_socket_acceptor(service)
327 {
328  assert(ipv4 || ipv6);
329 
330  open(ipv6);
331  set(ipv4, ipv6);
332  bind(address, port, ipv6);
333 }
334 
336 {
337  auto stream = std::make_shared<ip_stream>(service_);
338 
339  basic_socket_acceptor::accept(stream->get_socket(), [handler, stream] (auto code) {
340  if (code)
341  handler(std::move(code), nullptr);
342  else
343  handler(std::move(code), std::move(stream));
344  });
345 }
346 
347 // }}}
348 
349 // {{{ local_acceptor
350 
351 #if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
352 
358 class local_acceptor : public basic_socket_acceptor<boost::asio::local::stream_protocol::acceptor> {
359 public:
366  local_acceptor(boost::asio::io_context& service,
367  const boost::filesystem::path& path);
368 
373 
378 
382  void accept(handler handler) override;
383 };
384 
385 inline local_acceptor::local_acceptor(boost::asio::io_context& service,
386  const boost::filesystem::path& path)
387  : basic_socket_acceptor(service)
388 {
389  using boost::asio::socket_base;
390 
391  std::remove(path.string().c_str());
392 
393  acceptor_.open();
394  acceptor_.set_option(socket_base::reuse_address(true));
395  acceptor_.bind({ path.string() });
396  acceptor_.listen();
397 }
398 
400 {
401  auto stream = std::make_shared<local_stream>(service_);
402 
403  basic_socket_acceptor::accept(stream->get_socket(), [handler, stream] (auto code) {
404  if (code)
405  handler(std::move(code), nullptr);
406  else
407  handler(std::move(code), std::move(stream));
408  });
409 }
410 
411 #endif
412 
413 // }}}
414 
415 // {{{ tls_acceptor
416 
417 #if defined(IRCCD_HAVE_SSL)
418 
444 template <typename SocketAcceptor>
445 class tls_acceptor : public acceptor {
446 private:
447  using socket_type = typename SocketAcceptor::socket_type;
448 
449  std::shared_ptr<boost::asio::ssl::context> context_;
450  SocketAcceptor acceptor_;
451 
452 public:
459  template <typename... Args>
460  tls_acceptor(boost::asio::ssl::context context, Args&&... args);
461 
465  void accept(handler handler) override;
466 };
467 
468 template <typename SocketAcceptor>
469 template <typename... Args>
470 inline tls_acceptor<SocketAcceptor>::tls_acceptor(boost::asio::ssl::context context, Args&&... args)
471  : context_(std::make_shared<boost::asio::ssl::context>(std::move(context)))
472  , acceptor_(std::forward<Args>(args)...)
473 {
474 }
475 
476 template <typename SocketAcceptor>
478 {
479  auto client = std::make_shared<tls_stream<socket_type>>(acceptor_.get_service(), context_);
480 
481  acceptor_.accept(client->get_socket().lowest_layer(), [handler, client] (auto code) {
482  using boost::asio::ssl::stream_base;
483 
484  if (code) {
485  handler(std::move(code), nullptr);
486  return;
487  }
488 
489  client->get_socket().async_handshake(stream_base::server, [handler, client] (auto code) {
490  if (code)
491  handler(std::move(code), nullptr);
492  else
493  handler(std::move(code), std::move(client));
494  });
495  });
496 }
497 
502 
503 #if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
504 
509 
510 #endif // !BOOST_ASIO_HAS_LOCAL_SOCKETS
511 
512 #endif // !IRCCD_HAVE_SSL
513 
514 // }}}
515 
516 } // !irccd
517 
518 #endif // !IRCCD_ACCEPTOR_HPP
irccd::basic_socket_acceptor::basic_socket_acceptor
basic_socket_acceptor(boost::asio::io_context &service)
Definition: acceptor.hpp:171
irccd::acceptor::~acceptor
virtual ~acceptor()=default
irccd::basic_socket_acceptor< boost::asio::ip::tcp::acceptor >::socket_type
typename Acceptor::protocol_type::socket socket_type
Definition: acceptor.hpp:95
irccd::basic_socket_acceptor::accept
void accept(Socket &sc, Handler handler)
Definition: acceptor.hpp:210
irccd::tls_acceptor::tls_acceptor
tls_acceptor(boost::asio::ssl::context context, Args &&... args)
Definition: acceptor.hpp:470
irccd::basic_socket_acceptor::get_acceptor
auto get_acceptor() const noexcept -> const Acceptor &
Definition: acceptor.hpp:197
irccd::ip_acceptor::ip_acceptor
ip_acceptor(boost::asio::io_context &service, std::string address, std::uint16_t port, bool ipv4=true, bool ipv6=true)
Definition: acceptor.hpp:321
irccd::basic_socket_acceptor
Convenient acceptor owner.
Definition: acceptor.hpp:90
irccd::acceptor
Abstract stream acceptor interface.
Definition: acceptor.hpp:52
irccd::stream
Abstract stream interface.
Definition: stream.hpp:58
irccd::acceptor::handler
std::function< void(std::error_code, std::shared_ptr< stream >)> handler
Accept completion handler.
Definition: acceptor.hpp:57
irccd::local_acceptor::local_acceptor
local_acceptor(boost::asio::io_context &service, const boost::filesystem::path &path)
Definition: acceptor.hpp:385
irccd::tls_acceptor
TLS/SSL acceptors.
Definition: acceptor.hpp:445
irccd::local_acceptor
Local acceptor.
Definition: acceptor.hpp:358
irccd::basic_socket_acceptor::basic_socket_acceptor
basic_socket_acceptor(boost::asio::io_context &service, Acceptor acceptor) noexcept
Definition: acceptor.hpp:178
irccd::basic_socket_acceptor::service_
boost::asio::io_context & service_
The I/O context.
Definition: acceptor.hpp:106
irccd::ip_acceptor::accept
void accept(handler handler) override
Definition: acceptor.hpp:335
irccd::basic_socket_acceptor::acceptor_
Acceptor acceptor_
The underlying acceptor.
Definition: acceptor.hpp:111
irccd
Parent namespace.
Definition: acceptor.hpp:43
std
Definition: bot.hpp:253
irccd::ip_acceptor
TCP/IP acceptor.
Definition: acceptor.hpp:237
irccd::tls_acceptor::accept
void accept(handler handler) override
Definition: acceptor.hpp:477
irccd::local_acceptor::accept
void accept(handler handler) override
Definition: acceptor.hpp:399
irccd::acceptor::acceptor
acceptor()=default
irccd::acceptor::accept
virtual void accept(handler handler)=0
irccd::basic_socket_acceptor::get_service
auto get_service() const noexcept -> const boost::asio::io_context &
Definition: acceptor.hpp:185