http_cache.hpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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_HTTP_CACHE_HPP__
  11. #define __ASIO2_HTTP_CACHE_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 <cstdint>
  17. #include <memory>
  18. #include <chrono>
  19. #include <functional>
  20. #include <atomic>
  21. #include <string>
  22. #include <string_view>
  23. #include <map>
  24. #include <unordered_map>
  25. #include <type_traits>
  26. #include <asio2/base/detail/function_traits.hpp>
  27. #include <asio2/base/detail/util.hpp>
  28. #include <asio2/base/detail/shared_mutex.hpp>
  29. #ifdef ASIO2_HEADER_ONLY
  30. namespace bho::beast::http
  31. #else
  32. namespace boost::beast::http
  33. #endif
  34. {
  35. struct enable_cache_t {};
  36. constexpr static enable_cache_t enable_cache;
  37. template<bool isRequest, class Body, class Fields = fields>
  38. inline bool is_cache_enabled(http::message<isRequest, Body, Fields>& msg)
  39. {
  40. if constexpr (isRequest)
  41. {
  42. if (msg.method() == http::verb::get)
  43. {
  44. return true;
  45. }
  46. return false;
  47. }
  48. else
  49. {
  50. return true;
  51. }
  52. }
  53. }
  54. namespace asio2::detail
  55. {
  56. ASIO2_CLASS_FORWARD_DECLARE_BASE;
  57. ASIO2_CLASS_FORWARD_DECLARE_TCP_BASE;
  58. ASIO2_CLASS_FORWARD_DECLARE_TCP_SERVER;
  59. ASIO2_CLASS_FORWARD_DECLARE_TCP_SESSION;
  60. template<class caller_t, class args_t, class MessageT>
  61. class basic_http_cache_t
  62. {
  63. friend caller_t;
  64. ASIO2_CLASS_FRIEND_DECLARE_BASE;
  65. ASIO2_CLASS_FRIEND_DECLARE_TCP_BASE;
  66. ASIO2_CLASS_FRIEND_DECLARE_TCP_SERVER;
  67. ASIO2_CLASS_FRIEND_DECLARE_TCP_SESSION;
  68. protected:
  69. struct cache_node
  70. {
  71. std::chrono::steady_clock::time_point alive;
  72. MessageT msg;
  73. cache_node(std::chrono::steady_clock::time_point t, MessageT m)
  74. : alive(t), msg(std::move(m))
  75. {
  76. }
  77. inline void update_alive_time() noexcept
  78. {
  79. alive = std::chrono::steady_clock::now();
  80. }
  81. };
  82. public:
  83. using self = basic_http_cache_t<caller_t, args_t, MessageT>;
  84. /**
  85. * @brief constructor
  86. */
  87. basic_http_cache_t()
  88. {
  89. }
  90. /**
  91. * @brief destructor
  92. */
  93. ~basic_http_cache_t() = default;
  94. /**
  95. * @brief Add a element into the cache.
  96. */
  97. template<class StringT>
  98. inline cache_node* emplace(StringT&& url, MessageT msg)
  99. {
  100. asio2::unique_locker guard(this->http_cache_mutex_);
  101. if (this->http_cache_map_.size() >= http_caches_max_count_)
  102. return nullptr;
  103. // can't use insert_or_assign, it maybe cause the msg was changed in multithread, and
  104. // other thread are using the msg at this time.
  105. return std::addressof(this->http_cache_map_.emplace(
  106. detail::to_string(std::forward<StringT>(url)),
  107. cache_node{ std::chrono::steady_clock::now(), std::move(msg) }).first->second);
  108. }
  109. /**
  110. * @brief Checks if the cache has no elements.
  111. */
  112. inline bool empty() const noexcept
  113. {
  114. asio2::shared_locker guard(this->http_cache_mutex_);
  115. return this->http_cache_map_.empty();
  116. }
  117. /**
  118. * @brief Finds the cache with key equivalent to url.
  119. */
  120. template<class StringT>
  121. inline cache_node* find(const StringT& url)
  122. {
  123. asio2::shared_locker guard(this->http_cache_mutex_);
  124. if (this->http_cache_map_.empty())
  125. return nullptr;
  126. // If rehashing occurs due to the insertion, all iterators are invalidated.
  127. // Otherwise iterators are not affected. References are not invalidated.
  128. if constexpr (std::is_same_v<StringT, std::string>)
  129. {
  130. if (auto it = this->http_cache_map_.find(url); it != this->http_cache_map_.end())
  131. return std::addressof(it->second);
  132. }
  133. else
  134. {
  135. std::string str = detail::to_string(url);
  136. if (auto it = this->http_cache_map_.find(str); it != this->http_cache_map_.end())
  137. return std::addressof(it->second);
  138. }
  139. return nullptr;
  140. }
  141. /**
  142. * @brief Set the max number of elements in the container.
  143. */
  144. inline self& set_cache_max_count(std::size_t count) noexcept
  145. {
  146. this->http_caches_max_count_ = count;
  147. return (*this);
  148. }
  149. /**
  150. * @brief Get the max number of elements in the container.
  151. */
  152. inline std::size_t get_cache_max_count() const noexcept
  153. {
  154. return this->http_caches_max_count_;
  155. }
  156. /**
  157. * @brief Get the current number of elements in the container.
  158. */
  159. inline std::size_t get_cache_count() const noexcept
  160. {
  161. asio2::shared_locker guard(this->http_cache_mutex_);
  162. return this->http_cache_map_.size();
  163. }
  164. /**
  165. * @brief Requests the removal of expired elements.
  166. */
  167. inline self& shrink_to_fit()
  168. {
  169. asio2::unique_locker guard(this->http_cache_mutex_);
  170. if (this->http_cache_map_.size() < http_caches_max_count_)
  171. return (*this);
  172. std::multimap<std::chrono::steady_clock::duration::rep, const std::string*> mms;
  173. for (auto& [url, node] : this->http_cache_map_)
  174. {
  175. mms.emplace(node.alive.time_since_epoch().count(), std::addressof(url));
  176. }
  177. std::size_t i = 0, n = mms.size() / 3;
  178. for (auto& [t, purl] : mms)
  179. {
  180. detail::ignore_unused(t);
  181. if (++i > n)
  182. break;
  183. this->http_cache_map_.erase(*purl);
  184. }
  185. return (*this);
  186. }
  187. /**
  188. * @brief Erases all elements from the container.
  189. */
  190. inline self& clear() noexcept
  191. {
  192. asio2::unique_locker guard(this->http_cache_mutex_);
  193. this->http_cache_map_.clear();
  194. return (*this);
  195. }
  196. protected:
  197. mutable asio2::shared_mutexer http_cache_mutex_;
  198. std::unordered_map<std::string, cache_node> http_cache_map_ ASIO2_GUARDED_BY(http_cache_mutex_);
  199. std::size_t http_caches_max_count_ = 100000;
  200. };
  201. template<class caller_t, class args_t>
  202. using http_cache_t = basic_http_cache_t<caller_t, args_t, http::response<http::flex_body>>;
  203. }
  204. #include <asio2/base/detail/pop_options.hpp>
  205. #endif // !__ASIO2_HTTP_CACHE_HPP__