irccd  3.0.3
connector.hpp
1 /*
2  * connector.hpp -- abstract connection 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_CONNECTOR_HPP
20 #define IRCCD_CONNECTOR_HPP
21 
27 #include <cassert>
28 #include <functional>
29 #include <memory>
30 #include <system_error>
31 
32 #include <boost/asio.hpp>
33 
34 #if defined(IRCCD_HAVE_SSL)
35 # include <boost/asio/ssl.hpp>
36 #endif
37 
38 #include <boost/filesystem/path.hpp>
39 
40 #include "stream.hpp"
41 
42 namespace irccd {
43 
53 class connector {
54 public:
58  using handler = std::function<void (std::error_code, std::shared_ptr<stream>)>;
59 
63  connector() = default;
64 
68  virtual ~connector() = default;
69 
80  virtual void connect(handler handler) = 0;
81 };
82 
83 // {{{ basic_socket_connector
84 
90 protected:
94  boost::asio::io_context& service_;
95 
96 public:
102  basic_socket_connector(boost::asio::io_context& service);
103 
109  auto get_service() const noexcept -> const boost::asio::io_context&;
110 
116  auto get_service() noexcept -> boost::asio::io_context&;
117 };
118 
119 inline basic_socket_connector::basic_socket_connector(boost::asio::io_context& service)
120  : service_(service)
121 {
122 }
123 
124 inline auto basic_socket_connector::get_service() const noexcept -> const boost::asio::io_context&
125 {
126  return service_;
127 }
128 
129 inline auto basic_socket_connector::get_service() noexcept -> boost::asio::io_context&
130 {
131  return service_;
132 }
133 
134 // }}}
135 
136 // {{{ ip_connector
137 
143 public:
147  using socket_type = boost::asio::ip::tcp::socket;
148 
149 private:
150  boost::asio::ip::tcp::resolver resolver_;
151 
152  std::string hostname_;
153  std::string port_;
154 
155  bool ipv4_;
156  bool ipv6_;
157 
158 #if !defined(NDEBUG)
159  bool is_connecting_{false};
160 #endif
161 
162  template <typename Handler>
163  void resolve(Handler handler);
164 
165 public:
176  ip_connector(boost::asio::io_context& service,
177  std::string hostname,
178  std::string port,
179  bool ipv4 = true,
180  bool ipv6 = true) noexcept;
181 
189  template <typename Socket, typename Handler>
190  void connect(Socket& sc, Handler handler);
191 
195  void connect(handler handler);
196 };
197 
198 template <typename Handler>
199 inline void ip_connector::resolve(Handler handler)
200 {
201  using boost::asio::ip::tcp;
202 
203  if (ipv6_ && ipv4_)
204  resolver_.async_resolve(hostname_, port_, handler);
205  else if (ipv6_)
206  resolver_.async_resolve(tcp::v6(), hostname_, port_, handler);
207  else
208  resolver_.async_resolve(tcp::v4(), hostname_, port_, handler);
209 }
210 
211 inline ip_connector::ip_connector(boost::asio::io_context& service,
212  std::string hostname,
213  std::string port,
214  bool ipv4,
215  bool ipv6) noexcept
216  : basic_socket_connector(service)
217  , resolver_(service)
218  , hostname_(std::move(hostname))
219  , port_(std::move(port))
220  , ipv4_(ipv4)
221  , ipv6_(ipv6)
222 {
223  assert(!hostname_.empty());
224  assert(!port_.empty());
225  assert(ipv4 || ipv6);
226 }
227 
228 template <typename Socket, typename Handler>
229 inline void ip_connector::connect(Socket& sc, Handler handler)
230 {
231 #if !defined(NDEBUG)
232  assert(!is_connecting_);
233 
234  is_connecting_ = true;
235 #endif
236 
237  resolve([this, &sc, handler] (auto code, auto res) {
238 #if !defined(NDEBUG)
239  is_connecting_ = false;
240 #endif
241  (void)this;
242 
243  if (code) {
244  handler(std::move(code));
245  return;
246  }
247 
248  async_connect(sc, res, [handler] (auto code, auto) {
249  handler(std::move(code));
250  });
251  });
252 }
253 
255 {
256  auto stream = std::make_shared<ip_stream>(service_);
257 
258  connect(stream->get_socket(), [handler, stream] (auto code) {
259  if (code)
260  handler(std::move(code), nullptr);
261  else
262  handler(std::move(code), std::move(stream));
263  });
264 }
265 
266 // }}}
267 
268 // {{{ local_connector
269 
270 #if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
271 
277 public:
281  using socket_type = boost::asio::local::stream_protocol::socket;
282 
283 private:
284  boost::filesystem::path path_;
285 
286 #if !defined(NDEBUG)
287  bool is_connecting_{false};
288 #endif
289 
290 public:
297  local_connector(boost::asio::io_context& service,
298  boost::filesystem::path path) noexcept;
299 
307  template <typename Socket, typename Handler>
308  void connect(Socket& sc, Handler handler) noexcept;
309 
313  void connect(handler handler);
314 };
315 
316 inline local_connector::local_connector(boost::asio::io_context& service,
317  boost::filesystem::path path) noexcept
318  : basic_socket_connector(service)
319  , path_(std::move(path))
320 {
321 }
322 
323 template <typename Socket, typename Handler>
324 inline void local_connector::connect(Socket& sc, Handler handler) noexcept
325 {
326 #if !defined(NDEBUG)
327  assert(!is_connecting_);
328 
329  is_connecting_ = true;
330 #endif
331 
332  sc.async_connect({ path_.string() }, [this, handler] (auto code) {
333 #if !defined(NDEBUG)
334  is_connecting_ = false;
335 #endif
336  (void)this;
337  handler(std::move(code));
338  });
339 }
340 
342 {
343  auto stream = std::make_shared<local_stream>(service_);
344 
345  connect(stream->get_socket(), [handler, stream] (auto code) {
346  if (code)
347  handler(std::move(code), nullptr);
348  else
349  handler(std::move(code), std::move(stream));
350  });
351 }
352 
353 #endif // !BOOST_ASIO_HAS_LOCAL_SOCKETS
354 
355 // }}}
356 
357 // {{{ tls_connector
358 
359 #if defined(IRCCD_HAVE_SSL)
360 
366 template <typename SocketConnector>
367 class tls_connector : public connector {
368 public:
372  using socket_type = typename SocketConnector::socket_type;
373 
374 private:
375  std::shared_ptr<boost::asio::ssl::context> context_;
376  SocketConnector connector_;
377 
378 public:
385  template <typename... Args>
386  tls_connector(boost::asio::ssl::context context, Args&&... args);
387 
391  void connect(handler handler) override;
392 };
393 
394 template <typename SocketConnector>
395 template <typename... Args>
396 inline tls_connector<SocketConnector>::tls_connector(boost::asio::ssl::context context, Args&&... args)
397  : context_(std::make_shared<boost::asio::ssl::context>(std::move(context)))
398  , connector_(std::forward<Args>(args)...)
399 {
400 }
401 
402 template <typename SocketConnector>
404 {
405  using boost::asio::ssl::stream_base;
406 
407  assert(handler);
408 
409  auto stream = std::make_shared<tls_stream<socket_type>>(connector_.get_service(), context_);
410 
411  connector_.connect(stream->get_socket().lowest_layer(), [handler, stream] (auto code) {
412  if (code) {
413  handler(code, nullptr);
414  return;
415  }
416 
417  stream->get_socket().async_handshake(stream_base::client, [handler, stream] (auto code) {
418  if (code)
419  handler(std::move(code), nullptr);
420  else
421  handler(std::move(code), std::move(stream));
422  });
423  });
424 }
425 
430 
431 #if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
432 
437 
438 #endif // !BOOST_ASIO_HAS_LOCAL_SOCKETS
439 
440 #endif // !IRCCD_HAVE_SSL
441 
442 // }}}
443 
444 } // !irccd
445 
446 #endif // !IRCCD_CONNECTOR_HPP
irccd::local_connector::connect
void connect(Socket &sc, Handler handler) noexcept
Definition: connector.hpp:324
irccd::ip_connector::connect
void connect(Socket &sc, Handler handler)
Definition: connector.hpp:229
irccd::basic_socket_connector::basic_socket_connector
basic_socket_connector(boost::asio::io_context &service)
Definition: connector.hpp:119
irccd::ip_connector::socket_type
boost::asio::ip::tcp::socket socket_type
Definition: connector.hpp:147
irccd::basic_socket_connector::service_
boost::asio::io_context & service_
The I/O service.
Definition: connector.hpp:94
irccd::local_connector
Unix domain connector.
Definition: connector.hpp:276
irccd::tls_connector::socket_type
typename SocketConnector::socket_type socket_type
the underlying socket type.
Definition: connector.hpp:372
irccd::ip_connector::ip_connector
ip_connector(boost::asio::io_context &service, std::string hostname, std::string port, bool ipv4=true, bool ipv6=true) noexcept
Definition: connector.hpp:211
irccd::ip_connector
TCP/IP connector.
Definition: connector.hpp:142
irccd::connector::handler
std::function< void(std::error_code, std::shared_ptr< stream >)> handler
Connect completion handler.
Definition: connector.hpp:58
irccd::stream
Abstract stream interface.
Definition: stream.hpp:58
irccd::basic_socket_connector
Provide convenient functions for connectors.
Definition: connector.hpp:89
irccd::basic_socket_connector::get_service
auto get_service() const noexcept -> const boost::asio::io_context &
Definition: connector.hpp:124
irccd::connector::connect
virtual void connect(handler handler)=0
irccd
Parent namespace.
Definition: acceptor.hpp:43
std
Definition: bot.hpp:253
irccd::local_connector::socket_type
boost::asio::local::stream_protocol::socket socket_type
Definition: connector.hpp:281
irccd::tls_connector
TLS/SSL connectors.
Definition: connector.hpp:367
irccd::connector::connector
connector()=default
irccd::tls_connector::tls_connector
tls_connector(boost::asio::ssl::context context, Args &&... args)
Definition: connector.hpp:396
irccd::local_connector::local_connector
local_connector(boost::asio::io_context &service, boost::filesystem::path path) noexcept
Definition: connector.hpp:316
irccd::connector::~connector
virtual ~connector()=default
irccd::connector
Abstract connection interface.
Definition: connector.hpp:53
irccd::tls_connector::connect
void connect(handler handler) override
Definition: connector.hpp:403