ws_client.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. */
  10. #ifndef __ASIO2_WS_CLIENT_HPP__
  11. #define __ASIO2_WS_CLIENT_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <asio2/base/detail/push_options.hpp>
  16. #include <asio2/tcp/tcp_client.hpp>
  17. #include <asio2/http/detail/http_util.hpp>
  18. #include <asio2/http/impl/ws_stream_cp.hpp>
  19. #include <asio2/http/impl/ws_send_op.hpp>
  20. namespace asio2::detail
  21. {
  22. struct template_args_ws_client : public template_args_tcp_client
  23. {
  24. using stream_t = websocket::stream<typename template_args_tcp_client::socket_t&>;
  25. using body_t = http::string_body;
  26. using buffer_t = beast::flat_buffer;
  27. };
  28. ASIO2_CLASS_FORWARD_DECLARE_BASE;
  29. ASIO2_CLASS_FORWARD_DECLARE_TCP_BASE;
  30. ASIO2_CLASS_FORWARD_DECLARE_TCP_CLIENT;
  31. template<class derived_t, class args_t = template_args_ws_client>
  32. class ws_client_impl_t
  33. : public tcp_client_impl_t<derived_t, args_t>
  34. , public ws_stream_cp <derived_t, args_t>
  35. , public ws_send_op <derived_t, args_t>
  36. {
  37. ASIO2_CLASS_FRIEND_DECLARE_BASE;
  38. ASIO2_CLASS_FRIEND_DECLARE_TCP_BASE;
  39. ASIO2_CLASS_FRIEND_DECLARE_TCP_CLIENT;
  40. public:
  41. using super = tcp_client_impl_t<derived_t, args_t>;
  42. using self = ws_client_impl_t <derived_t, args_t>;
  43. using args_type = args_t;
  44. using body_type = typename args_t::body_t;
  45. using buffer_type = typename args_t::buffer_t;
  46. using ws_stream_comp = ws_stream_cp<derived_t, args_t>;
  47. using super::send;
  48. using super::async_send;
  49. public:
  50. /**
  51. * @brief constructor
  52. */
  53. template<class... Args>
  54. explicit ws_client_impl_t(Args&&... args)
  55. : super(std::forward<Args>(args)...)
  56. , ws_stream_cp<derived_t, args_t>()
  57. , ws_send_op <derived_t, args_t>()
  58. {
  59. }
  60. /**
  61. * @brief destructor
  62. */
  63. ~ws_client_impl_t()
  64. {
  65. this->stop();
  66. }
  67. /**
  68. * @brief return the websocket stream object reference
  69. */
  70. inline typename args_t::stream_t & stream() noexcept
  71. {
  72. return this->derived().ws_stream();
  73. }
  74. /**
  75. * @brief return the websocket stream object reference
  76. */
  77. inline typename args_t::stream_t const& stream() const noexcept
  78. {
  79. return this->derived().ws_stream();
  80. }
  81. /**
  82. * @brief start the client, blocking connect to server
  83. * @param host - A string identifying a location. May be a descriptive name or
  84. * a numeric address string.
  85. * @param port - A string identifying the requested service. This may be a
  86. * descriptive name or a numeric string corresponding to a port number.
  87. * @param args - The args can be include the upgraged target.
  88. * eg: start("127.0.0.1", 8883); start("127.0.0.1", 8883, "/admin");
  89. * the "/admin" is the websocket upgraged target
  90. */
  91. template<typename String, typename StrOrInt, typename... Args>
  92. inline bool start(String&& host, StrOrInt&& port, Args&&... args)
  93. {
  94. if constexpr (sizeof...(Args) > std::size_t(0))
  95. return this->derived().template _do_connect_with_target<false>(
  96. std::forward<String>(host), std::forward<StrOrInt>(port),
  97. std::forward<Args>(args)...);
  98. else
  99. return this->derived().template _do_connect<false>(
  100. std::forward<String>(host), std::forward<StrOrInt>(port),
  101. ecs_helper::make_ecs('0', std::forward<Args>(args)...));
  102. }
  103. /**
  104. * @brief start the client, asynchronous connect to server
  105. * @param host - A string identifying a location. May be a descriptive name or
  106. * a numeric address string.
  107. * @param port - A string identifying the requested service. This may be a
  108. * descriptive name or a numeric string corresponding to a port number.
  109. * @param args - The args can be include the upgraged target.
  110. * eg: async_start("127.0.0.1", 8883); async_start("127.0.0.1", 8883, "/admin");
  111. * the "/admin" is the websocket upgraged target
  112. */
  113. template<typename String, typename StrOrInt, typename... Args>
  114. inline bool async_start(String&& host, StrOrInt&& port, Args&&... args)
  115. {
  116. if constexpr (sizeof...(Args) > std::size_t(0))
  117. return this->derived().template _do_connect_with_target<true>(
  118. std::forward<String>(host), std::forward<StrOrInt>(port),
  119. std::forward<Args>(args)...);
  120. else
  121. return this->derived().template _do_connect<true>(
  122. std::forward<String>(host), std::forward<StrOrInt>(port),
  123. ecs_helper::make_ecs('0', std::forward<Args>(args)...));
  124. }
  125. /**
  126. * @brief destroy the content of all member variables, this is used for solve the memory leaks.
  127. * After this function is called, this class object cannot be used again.
  128. */
  129. inline void destroy()
  130. {
  131. derived_t& derive = this->derived();
  132. derive.ws_stream_.reset();
  133. super::destroy();
  134. }
  135. /**
  136. * @brief get the websocket upgraged response object
  137. */
  138. inline websocket::response_type& get_upgrade_response() noexcept { return this->upgrade_rep_; }
  139. /**
  140. * @brief get the websocket upgraged response object
  141. */
  142. inline const websocket::response_type& get_upgrade_response() const noexcept { return this->upgrade_rep_; }
  143. /**
  144. * @brief get the websocket upgraged target
  145. */
  146. inline const std::string& get_upgrade_target() const noexcept { return this->upgrade_target_; }
  147. /**
  148. * @brief set the websocket upgraged target
  149. */
  150. inline derived_t & set_upgrade_target(std::string target)
  151. {
  152. this->upgrade_target_ = std::move(target);
  153. return (this->derived());
  154. }
  155. public:
  156. /**
  157. * @brief bind websocket upgrade listener
  158. * @param fun - a user defined callback function.
  159. * Function signature : void()
  160. */
  161. template<class F, class ...C>
  162. inline derived_t & bind_upgrade(F&& fun, C&&... obj)
  163. {
  164. this->listener_.bind(event_type::upgrade,
  165. observer_t<>(std::forward<F>(fun), std::forward<C>(obj)...));
  166. return (this->derived());
  167. }
  168. protected:
  169. template<bool IsAsync, typename String, typename StrOrInt, typename Arg1, typename... Args>
  170. bool _do_connect_with_target(String&& host, StrOrInt&& port, Arg1&& arg1, Args&&... args)
  171. {
  172. if constexpr (detail::can_convert_to_string<detail::remove_cvref_t<Arg1>>::value)
  173. {
  174. this->derived().set_upgrade_target(std::forward<Arg1>(arg1));
  175. return this->derived().template _do_connect<IsAsync>(
  176. std::forward<String>(host), std::forward<StrOrInt>(port),
  177. ecs_helper::make_ecs('0', std::forward<Args>(args)...));
  178. }
  179. else
  180. {
  181. return this->derived().template _do_connect<IsAsync>(
  182. std::forward<String>(host), std::forward<StrOrInt>(port),
  183. ecs_helper::make_ecs('0',
  184. std::forward<Arg1>(arg1), std::forward<Args>(args)...));
  185. }
  186. }
  187. template<typename C>
  188. inline void _do_init(std::shared_ptr<ecs_t<C>>& ecs)
  189. {
  190. super::_do_init(ecs);
  191. this->derived()._ws_init(ecs, this->derived().socket());
  192. }
  193. template<typename DeferEvent>
  194. inline void _post_shutdown(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  195. {
  196. ASIO2_LOG_DEBUG("ws_client::_post_shutdown: {} {}", ec.value(), ec.message());
  197. this->derived()._ws_stop(this_ptr, defer_event
  198. {
  199. [this, ec, this_ptr, e = chain.move_event()] (event_queue_guard<derived_t> g) mutable
  200. {
  201. super::_post_shutdown(ec, std::move(this_ptr), defer_event(std::move(e), std::move(g)));
  202. }, chain.move_guard()
  203. });
  204. }
  205. template<typename C, typename DeferEvent>
  206. inline void _handle_connect(
  207. const error_code& ec,
  208. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs, DeferEvent chain)
  209. {
  210. set_last_error(ec);
  211. derived_t& derive = this->derived();
  212. if (ec)
  213. {
  214. return derive._done_connect(ec, std::move(this_ptr), std::move(ecs), std::move(chain));
  215. }
  216. derive._ws_start(this_ptr, ecs, derive.socket());
  217. derive._post_control_callback(this_ptr, ecs);
  218. derive._post_upgrade(std::move(this_ptr), std::move(ecs), this->upgrade_rep_, std::move(chain));
  219. }
  220. template<class Data, class Callback>
  221. inline bool _do_send(Data& data, Callback&& callback)
  222. {
  223. return this->derived()._ws_send(data, std::forward<Callback>(callback));
  224. }
  225. protected:
  226. template<typename C>
  227. inline void _post_recv(std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs)
  228. {
  229. this->derived()._ws_post_recv(std::move(this_ptr), std::move(ecs));
  230. }
  231. template<typename C>
  232. inline void _handle_recv(
  233. const error_code& ec, std::size_t bytes_recvd,
  234. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs)
  235. {
  236. this->derived()._ws_handle_recv(ec, bytes_recvd, std::move(this_ptr), std::move(ecs));
  237. }
  238. inline void _fire_upgrade(std::shared_ptr<derived_t>& this_ptr)
  239. {
  240. // the _fire_upgrade must be executed in the thread 0.
  241. ASIO2_ASSERT(this->derived().io_->running_in_this_thread());
  242. detail::ignore_unused(this_ptr);
  243. this->listener_.notify(event_type::upgrade);
  244. }
  245. protected:
  246. websocket::response_type upgrade_rep_;
  247. std::string upgrade_target_ = "/";
  248. };
  249. }
  250. namespace asio2
  251. {
  252. using ws_client_args = detail::template_args_ws_client;
  253. template<class derived_t, class args_t>
  254. using ws_client_impl_t = detail::ws_client_impl_t<derived_t, args_t>;
  255. template<class derived_t>
  256. class ws_client_t : public detail::ws_client_impl_t<derived_t, detail::template_args_ws_client>
  257. {
  258. public:
  259. using detail::ws_client_impl_t<derived_t, detail::template_args_ws_client>::ws_client_impl_t;
  260. };
  261. class ws_client : public ws_client_t<ws_client>
  262. {
  263. public:
  264. using ws_client_t<ws_client>::ws_client_t;
  265. };
  266. }
  267. #if defined(ASIO2_INCLUDE_RATE_LIMIT)
  268. #include <asio2/tcp/tcp_stream.hpp>
  269. namespace asio2
  270. {
  271. struct ws_rate_client_args : public ws_client_args
  272. {
  273. using socket_t = asio2::tcp_stream<asio2::simple_rate_policy>;
  274. using stream_t = websocket::stream<socket_t&>;
  275. };
  276. template<class derived_t>
  277. class ws_rate_client_t : public asio2::ws_client_impl_t<derived_t, ws_rate_client_args>
  278. {
  279. public:
  280. using asio2::ws_client_impl_t<derived_t, ws_rate_client_args>::ws_client_impl_t;
  281. };
  282. class ws_rate_client : public asio2::ws_rate_client_t<ws_rate_client>
  283. {
  284. public:
  285. using asio2::ws_rate_client_t<ws_rate_client>::ws_rate_client_t;
  286. };
  287. }
  288. #endif
  289. #include <asio2/base/detail/pop_options.hpp>
  290. #endif // !__ASIO2_WS_CLIENT_HPP__