handshaker.hpp 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /* Copyright (c) 2018-2023 Marcelo Zimbres Silva ([email protected])
  2. *
  3. * Distributed under the Boost Software License, Version 1.0. (See
  4. * accompanying file LICENSE.txt)
  5. */
  6. #ifndef BOOST_REDIS_SSL_CONNECTOR_HPP
  7. #define BOOST_REDIS_SSL_CONNECTOR_HPP
  8. #include <boost/redis/detail/helper.hpp>
  9. #include <boost/redis/error.hpp>
  10. #include <boost/asio/compose.hpp>
  11. #include <boost/asio/connect.hpp>
  12. #include <boost/asio/coroutine.hpp>
  13. #include <boost/asio/experimental/parallel_group.hpp>
  14. #include <boost/asio/ip/tcp.hpp>
  15. #include <boost/asio/steady_timer.hpp>
  16. #include <boost/asio/ssl.hpp>
  17. #include <string>
  18. #include <chrono>
  19. namespace boost::redis::detail
  20. {
  21. template <class Handshaker, class Stream>
  22. struct handshake_op {
  23. Handshaker* hsher_ = nullptr;
  24. Stream* stream_ = nullptr;
  25. asio::coroutine coro{};
  26. template <class Self>
  27. void operator()( Self& self
  28. , std::array<std::size_t, 2> const& order = {}
  29. , system::error_code const& ec1 = {}
  30. , system::error_code const& ec2 = {})
  31. {
  32. BOOST_ASIO_CORO_REENTER (coro)
  33. {
  34. hsher_->timer_.expires_after(hsher_->timeout_);
  35. BOOST_ASIO_CORO_YIELD
  36. asio::experimental::make_parallel_group(
  37. [this](auto token) { return stream_->async_handshake(asio::ssl::stream_base::client, token); },
  38. [this](auto token) { return hsher_->timer_.async_wait(token);}
  39. ).async_wait(
  40. asio::experimental::wait_for_one(),
  41. std::move(self));
  42. if (is_cancelled(self)) {
  43. self.complete(asio::error::operation_aborted);
  44. return;
  45. }
  46. switch (order[0]) {
  47. case 0: {
  48. self.complete(ec1);
  49. } break;
  50. case 1:
  51. {
  52. if (ec2) {
  53. self.complete(ec2);
  54. } else {
  55. self.complete(error::ssl_handshake_timeout);
  56. }
  57. } break;
  58. default: BOOST_ASSERT(false);
  59. }
  60. }
  61. }
  62. };
  63. template <class Executor>
  64. class handshaker {
  65. public:
  66. using timer_type =
  67. asio::basic_waitable_timer<
  68. std::chrono::steady_clock,
  69. asio::wait_traits<std::chrono::steady_clock>,
  70. Executor>;
  71. handshaker(Executor ex)
  72. : timer_{ex}
  73. {}
  74. template <class Stream, class CompletionToken>
  75. auto
  76. async_handshake(Stream& stream, CompletionToken&& token)
  77. {
  78. return asio::async_compose
  79. < CompletionToken
  80. , void(system::error_code)
  81. >(handshake_op<handshaker, Stream>{this, &stream}, token, timer_);
  82. }
  83. std::size_t cancel(operation op)
  84. {
  85. switch (op) {
  86. case operation::ssl_handshake:
  87. case operation::all:
  88. timer_.cancel();
  89. break;
  90. default: /* ignore */;
  91. }
  92. return 0;
  93. }
  94. constexpr bool is_dummy() const noexcept
  95. {return false;}
  96. void set_config(config const& cfg)
  97. { timeout_ = cfg.ssl_handshake_timeout; }
  98. private:
  99. template <class, class> friend struct handshake_op;
  100. timer_type timer_;
  101. std::chrono::steady_clock::duration timeout_;
  102. };
  103. } // boost::redis::detail
  104. #endif // BOOST_REDIS_SSL_CONNECTOR_HPP