ping_op.hpp 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. //
  2. // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MQTT5_PING_OP_HPP
  8. #define BOOST_MQTT5_PING_OP_HPP
  9. #include <boost/mqtt5/detail/control_packet.hpp>
  10. #include <boost/mqtt5/detail/internal_types.hpp>
  11. #include <boost/mqtt5/impl/codecs/message_encoders.hpp>
  12. #include <boost/asio/consign.hpp>
  13. #include <boost/asio/error.hpp>
  14. #include <boost/asio/prepend.hpp>
  15. #include <chrono>
  16. #include <limits>
  17. namespace boost::mqtt5::detail {
  18. namespace asio = boost::asio;
  19. template <typename ClientService, typename Handler>
  20. class ping_op {
  21. using client_service = ClientService;
  22. using handler_type = Handler;
  23. struct on_timer {};
  24. struct on_pingreq {};
  25. std::shared_ptr<client_service> _svc_ptr;
  26. handler_type _handler;
  27. public:
  28. ping_op(std::shared_ptr<client_service> svc_ptr, Handler&& handler) :
  29. _svc_ptr(std::move(svc_ptr)), _handler(std::move(handler))
  30. {}
  31. ping_op(ping_op&&) noexcept = default;
  32. ping_op(const ping_op&) = delete;
  33. ping_op& operator=(ping_op&&) noexcept = default;
  34. ping_op& operator=(const ping_op&) = delete;
  35. using allocator_type = asio::associated_allocator_t<handler_type>;
  36. allocator_type get_allocator() const noexcept {
  37. return asio::get_associated_allocator(_handler);
  38. }
  39. using executor_type = typename client_service::executor_type;
  40. executor_type get_executor() const noexcept {
  41. return _svc_ptr->get_executor();
  42. }
  43. void perform() {
  44. _svc_ptr->_ping_timer.expires_after(compute_wait_time());
  45. _svc_ptr->_ping_timer.async_wait(
  46. asio::prepend(std::move(*this), on_timer {})
  47. );
  48. }
  49. void operator()(on_timer, error_code ec) {
  50. if (!_svc_ptr->is_open())
  51. return complete();
  52. else if (ec == asio::error::operation_aborted)
  53. return perform();
  54. auto pingreq = control_packet<allocator_type>::of(
  55. no_pid, get_allocator(), encoders::encode_pingreq
  56. );
  57. auto wire_data = pingreq.wire_data();
  58. _svc_ptr->async_send(
  59. wire_data,
  60. no_serial, send_flag::none,
  61. asio::consign(
  62. asio::prepend(std::move(*this), on_pingreq {}),
  63. std::move(pingreq)
  64. )
  65. );
  66. }
  67. void operator()(on_pingreq, error_code ec) {
  68. if (!ec || ec == asio::error::try_again)
  69. return perform();
  70. complete();
  71. }
  72. private:
  73. duration compute_wait_time() const {
  74. auto negotiated_ka = _svc_ptr->negotiated_keep_alive();
  75. return negotiated_ka ?
  76. std::chrono::seconds(negotiated_ka) :
  77. duration((std::numeric_limits<duration::rep>::max)());
  78. }
  79. void complete() {
  80. return std::move(_handler)();
  81. }
  82. };
  83. } // end namespace boost::mqtt5::detail
  84. #endif // !BOOST_MQTT5_PING_OP_HPP