http_router.hpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147
  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_ROUTER_HPP__
  11. #define __ASIO2_HTTP_ROUTER_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 <queue>
  24. #include <any>
  25. #include <future>
  26. #include <tuple>
  27. #include <map>
  28. #include <unordered_map>
  29. #include <type_traits>
  30. #include <asio2/external/asio.hpp>
  31. #include <asio2/external/beast.hpp>
  32. #include <asio2/base/iopool.hpp>
  33. #include <asio2/base/detail/filesystem.hpp>
  34. #include <asio2/base/detail/function_traits.hpp>
  35. #include <asio2/base/detail/util.hpp>
  36. #include <asio2/http/detail/http_util.hpp>
  37. #include <asio2/http/detail/http_cache.hpp>
  38. #include <asio2/http/request.hpp>
  39. #include <asio2/http/response.hpp>
  40. #include <asio2/util/string.hpp>
  41. namespace asio2::detail
  42. {
  43. template<class, class> class http_router_t;
  44. }
  45. #ifdef ASIO2_HEADER_ONLY
  46. namespace bho::beast::websocket
  47. #else
  48. namespace boost::beast::websocket
  49. #endif
  50. {
  51. namespace detail
  52. {
  53. struct listener_tag {};
  54. }
  55. template<class caller_t>
  56. class listener : public detail::listener_tag
  57. {
  58. template<class, class> friend class asio2::detail::http_router_t;
  59. // Cannot access the protected member of bho::beast::websocket::listener<caller_t>::operator ()
  60. template<typename, typename> friend struct asio2::detail::function_traits;
  61. public:
  62. using self = listener<caller_t>;
  63. listener() = default;
  64. listener(listener&&) noexcept = default;
  65. listener(listener const&) = default;
  66. listener& operator=(listener&&) noexcept = default;
  67. listener& operator=(listener const&) = default;
  68. template<class F>
  69. inline listener& on_message(F&& f)
  70. {
  71. this->_bind(websocket::frame::message, std::forward<F>(f));
  72. return (*this);
  73. }
  74. template<class F>
  75. inline listener& on_ping(F&& f)
  76. {
  77. this->_bind(websocket::frame::ping, std::forward<F>(f));
  78. return (*this);
  79. }
  80. template<class F>
  81. inline listener& on_pong(F&& f)
  82. {
  83. this->_bind(websocket::frame::pong, std::forward<F>(f));
  84. return (*this);
  85. }
  86. template<class F>
  87. inline listener& on_open(F&& f)
  88. {
  89. this->_bind(websocket::frame::open, std::forward<F>(f));
  90. return (*this);
  91. }
  92. template<class F>
  93. inline listener& on_close(F&& f)
  94. {
  95. this->_bind(websocket::frame::close, std::forward<F>(f));
  96. return (*this);
  97. }
  98. template<class F, class C>
  99. inline listener& on_message(F&& f, C* c)
  100. {
  101. auto mf = std::bind(std::forward<F>(f), c, std::placeholders::_1, std::placeholders::_2);
  102. this->_bind(websocket::frame::message, std::move(mf));
  103. return (*this);
  104. }
  105. template<class F, class C>
  106. inline listener& on_ping(F&& f, C* c)
  107. {
  108. auto mf = std::bind(std::forward<F>(f), c, std::placeholders::_1, std::placeholders::_2);
  109. this->_bind(websocket::frame::ping, std::move(mf));
  110. return (*this);
  111. }
  112. template<class F, class C>
  113. inline listener& on_pong(F&& f, C* c)
  114. {
  115. auto mf = std::bind(std::forward<F>(f), c, std::placeholders::_1, std::placeholders::_2);
  116. this->_bind(websocket::frame::pong, std::move(mf));
  117. return (*this);
  118. }
  119. template<class F, class C>
  120. inline listener& on_open(F&& f, C* c)
  121. {
  122. auto mf = std::bind(std::forward<F>(f), c, std::placeholders::_1, std::placeholders::_2);
  123. this->_bind(websocket::frame::open, std::move(mf));
  124. return (*this);
  125. }
  126. template<class F, class C>
  127. inline listener& on_close(F&& f, C* c)
  128. {
  129. auto mf = std::bind(std::forward<F>(f), c, std::placeholders::_1, std::placeholders::_2);
  130. this->_bind(websocket::frame::close, std::move(mf));
  131. return (*this);
  132. }
  133. template<class F, class C>
  134. inline listener& on_message(F&& f, C& c)
  135. {
  136. return this->on_message(std::forward<F>(f), std::addressof(c));
  137. }
  138. template<class F, class C>
  139. inline listener& on_ping(F&& f, C& c)
  140. {
  141. return this->on_ping(std::forward<F>(f), std::addressof(c));
  142. }
  143. template<class F, class C>
  144. inline listener& on_pong(F&& f, C& c)
  145. {
  146. return this->on_pong(std::forward<F>(f), std::addressof(c));
  147. }
  148. template<class F, class C>
  149. inline listener& on_open(F&& f, C& c)
  150. {
  151. return this->on_open(std::forward<F>(f), std::addressof(c));
  152. }
  153. template<class F, class C>
  154. inline listener& on_close(F&& f, C& c)
  155. {
  156. return this->on_close(std::forward<F>(f), std::addressof(c));
  157. }
  158. template<class F>
  159. inline listener& on(std::string_view type, F&& f)
  160. {
  161. if (beast::iequals(type, "message")) this->_bind(websocket::frame::message, std::forward<F>(f));
  162. else if (beast::iequals(type, "ping" )) this->_bind(websocket::frame::ping , std::forward<F>(f));
  163. else if (beast::iequals(type, "pong" )) this->_bind(websocket::frame::pong , std::forward<F>(f));
  164. else if (beast::iequals(type, "open" )) this->_bind(websocket::frame::open , std::forward<F>(f));
  165. else if (beast::iequals(type, "close" )) this->_bind(websocket::frame::close , std::forward<F>(f));
  166. return (*this);
  167. }
  168. template<class F, class C>
  169. inline listener& on(std::string_view type, F&& f, C* c)
  170. {
  171. auto mf = std::bind(std::forward<F>(f), c, std::placeholders::_1, std::placeholders::_2);
  172. if (beast::iequals(type, "message")) this->_bind(websocket::frame::message, std::move(mf));
  173. else if (beast::iequals(type, "ping" )) this->_bind(websocket::frame::ping , std::move(mf));
  174. else if (beast::iequals(type, "pong" )) this->_bind(websocket::frame::pong , std::move(mf));
  175. else if (beast::iequals(type, "open" )) this->_bind(websocket::frame::open , std::move(mf));
  176. else if (beast::iequals(type, "close" )) this->_bind(websocket::frame::close , std::move(mf));
  177. return (*this);
  178. }
  179. template<class F, class C>
  180. inline listener& on(std::string_view type, F&& f, C& c)
  181. {
  182. return this->on(std::move(type), std::forward<F>(f), std::addressof(c));
  183. }
  184. public:
  185. // under gcc 8.2.0, if this "operator()" is protected, it compiled error :
  186. // is protected within this context : function_traits<decltype(&Callable::operator())>{};
  187. inline void operator()(std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response&)
  188. {
  189. ASIO2_ASSERT(caller->is_websocket());
  190. auto& f = cbs_[asio2::detail::to_underlying(req.ws_frame_type_)];
  191. if (f) f(caller, req.ws_frame_data_);
  192. }
  193. protected:
  194. template<class F>
  195. inline void _bind(websocket::frame type, F&& f)
  196. {
  197. this->cbs_[asio2::detail::to_underlying(type)] = std::bind(
  198. &self::template _proxy<F>, this, std::forward<F>(f),
  199. std::placeholders::_1, std::placeholders::_2);
  200. }
  201. template<class F>
  202. inline void _proxy(F& f, std::shared_ptr<caller_t>& caller, std::string_view data)
  203. {
  204. asio2::detail::ignore_unused(data);
  205. if constexpr (asio2::detail::is_template_callable_v<F, std::shared_ptr<caller_t>&, std::string_view>)
  206. {
  207. f(caller, data);
  208. }
  209. else
  210. {
  211. f(caller);
  212. }
  213. }
  214. protected:
  215. std::array<std::function<void(std::shared_ptr<caller_t>&, std::string_view)>,
  216. asio2::detail::to_underlying(frame::close) + 1> cbs_;
  217. };
  218. }
  219. namespace asio2::detail
  220. {
  221. ASIO2_CLASS_FORWARD_DECLARE_BASE;
  222. ASIO2_CLASS_FORWARD_DECLARE_TCP_BASE;
  223. ASIO2_CLASS_FORWARD_DECLARE_TCP_SERVER;
  224. ASIO2_CLASS_FORWARD_DECLARE_TCP_SESSION;
  225. template<class caller_t, class args_t>
  226. class http_router_t
  227. {
  228. friend caller_t;
  229. ASIO2_CLASS_FRIEND_DECLARE_BASE;
  230. ASIO2_CLASS_FRIEND_DECLARE_TCP_BASE;
  231. ASIO2_CLASS_FRIEND_DECLARE_TCP_SERVER;
  232. ASIO2_CLASS_FRIEND_DECLARE_TCP_SESSION;
  233. template<class T, class R, class... Args>
  234. struct has_member_before : std::false_type {};
  235. template<class T, class... Args>
  236. struct has_member_before<T, decltype(std::declval<std::decay_t<T>>().
  237. before((std::declval<Args>())...)), Args...> : std::true_type {};
  238. template<class T, class R, class... Args>
  239. struct has_member_after : std::false_type {};
  240. template<class T, class... Args>
  241. struct has_member_after<T, decltype(std::declval<std::decay_t<T>>().
  242. after((std::declval<Args>())...)), Args...> : std::true_type {};
  243. /////////////////////////////////////////////////////////////////////////////
  244. // cinatra-master/include/cinatra/utils.hpp
  245. template <typename T, typename Tuple>
  246. struct has_type;
  247. template <typename T, typename... Us>
  248. struct has_type<T, std::tuple<Us...>> : std::disjunction<std::is_same<T, Us>...> {};
  249. template< typename T>
  250. struct filter_helper
  251. {
  252. static constexpr auto func()
  253. {
  254. return std::tuple<>();
  255. }
  256. template< class... Args >
  257. static constexpr auto func(T&&, Args&&...args)
  258. {
  259. return filter_helper::func(std::forward<Args>(args)...);
  260. }
  261. template< class... Args >
  262. static constexpr auto func(const T&, Args&&...args)
  263. {
  264. return filter_helper::func(std::forward<Args>(args)...);
  265. }
  266. template< class X, class... Args >
  267. static constexpr auto func(X&& x, Args&&...args)
  268. {
  269. return std::tuple_cat(std::make_tuple(std::forward<X>(x)),
  270. filter_helper::func(std::forward<Args>(args)...));
  271. }
  272. };
  273. template<typename T, typename... Args>
  274. inline auto filter_cache(Args&&... args)
  275. {
  276. return filter_helper<T>::func(std::forward<Args>(args)...);
  277. }
  278. /////////////////////////////////////////////////////////////////////////////
  279. public:
  280. using self = http_router_t<caller_t, args_t>;
  281. using opret = http::response<http::flex_body>*;
  282. using opfun = std::function<opret(std::shared_ptr<caller_t>&, http::web_request&, http::web_response&)>;
  283. /**
  284. * @brief constructor
  285. */
  286. http_router_t()
  287. {
  288. this->not_found_router_ = std::make_shared<opfun>(
  289. [](std::shared_ptr<caller_t>&, http::web_request& req, http::web_response& rep) mutable
  290. {
  291. std::string desc;
  292. desc.reserve(64);
  293. desc += "The resource for ";
  294. desc += req.method_string();
  295. desc += " \"";
  296. desc += http::url_decode(req.target());
  297. desc += "\" was not found";
  298. rep.fill_page(http::status::not_found, std::move(desc), {}, req.version());
  299. return std::addressof(rep.base());
  300. });
  301. #if defined(_DEBUG) || defined(DEBUG)
  302. // used to test ThreadSafetyAnalysis
  303. asio2::ignore_unused(this->http_cache_.empty());
  304. asio2::ignore_unused(this->http_cache_.get_cache_count());
  305. #endif
  306. }
  307. /**
  308. * @brief destructor
  309. */
  310. ~http_router_t() = default;
  311. /**
  312. * @brief bind a function for http router
  313. * @param name - uri name in string format.
  314. * @param fun - Function object.
  315. * @param caop - A pointer or reference to a class object, and aop object list.
  316. * if fun is member function, the first caop param must the class object's pointer or reference.
  317. */
  318. template<http::verb... M, class F, class ...CAOP>
  319. typename std::enable_if_t<!std::is_base_of_v<websocket::detail::listener_tag,
  320. detail::remove_cvref_t<F>>, self&>
  321. inline bind(std::string name, F&& fun, CAOP&&... caop)
  322. {
  323. asio2::trim_both(name);
  324. if (name == "*")
  325. name = "/*";
  326. if (name.empty())
  327. {
  328. ASIO2_ASSERT(false);
  329. return (*this);
  330. }
  331. if constexpr (sizeof...(M) == std::size_t(0))
  332. {
  333. this->_bind<http::verb::get, http::verb::post>(
  334. std::move(name), std::forward<F>(fun), std::forward<CAOP>(caop)...);
  335. }
  336. else
  337. {
  338. this->_bind<M...>(
  339. std::move(name), std::forward<F>(fun), std::forward<CAOP>(caop)...);
  340. }
  341. return (*this);
  342. }
  343. /**
  344. * @brief bind a function for websocket router
  345. * @param name - uri name in string format.
  346. * @param listener - callback listener.
  347. * @param aop - aop object list.
  348. */
  349. template<class ...AOP>
  350. inline self& bind(std::string name, websocket::listener<caller_t> listener, AOP&&... aop)
  351. {
  352. asio2::trim_both(name);
  353. ASIO2_ASSERT(!name.empty());
  354. if (!name.empty())
  355. this->_bind(std::move(name), std::move(listener), std::forward<AOP>(aop)...);
  356. return (*this);
  357. }
  358. /**
  359. * @brief set the 404 not found router function
  360. */
  361. template<class F, class ...C>
  362. inline self& bind_not_found(F&& f, C&&... obj)
  363. {
  364. this->_bind_not_found(std::forward<F>(f), std::forward<C>(obj)...);
  365. return (*this);
  366. }
  367. /**
  368. * @brief set the 404 not found router function
  369. */
  370. template<class F, class ...C>
  371. inline self& bind_404(F&& f, C&&... obj)
  372. {
  373. this->_bind_not_found(std::forward<F>(f), std::forward<C>(obj)...);
  374. return (*this);
  375. }
  376. public:
  377. /**
  378. * @brief set the root directory where we load the files.
  379. */
  380. inline self& set_root_directory(const std::filesystem::path& path)
  381. {
  382. std::error_code ec{};
  383. this->root_directory_ = std::filesystem::canonical(path, ec);
  384. assert(!ec);
  385. return (*this);
  386. }
  387. /**
  388. * @brief get the root directory where we load the files.
  389. */
  390. inline const std::filesystem::path& get_root_directory() noexcept
  391. {
  392. return this->root_directory_;
  393. }
  394. /**
  395. * @brief set whether websocket is supported, default is true
  396. */
  397. inline self& set_support_websocket(bool v) noexcept
  398. {
  399. this->support_websocket_ = v;
  400. return (*this);
  401. }
  402. /**
  403. * @brief set whether websocket is supported, default is true, same as set_support_websocket
  404. */
  405. inline self& support_websocket(bool v) noexcept
  406. {
  407. return this->set_support_websocket(v);
  408. }
  409. /**
  410. * @brief get whether websocket is supported, default is true
  411. */
  412. inline bool is_support_websocket() const noexcept
  413. {
  414. return this->support_websocket_;
  415. }
  416. protected:
  417. inline self& _router() noexcept { return (*this); }
  418. template<http::verb... M>
  419. inline decltype(auto) _make_uris(std::string name)
  420. {
  421. std::size_t index = 0;
  422. std::array<std::string, sizeof...(M)> uris;
  423. //while (!name.empty() && name.back() == '*')
  424. // name.erase(std::prev(name.end()));
  425. while (name.size() > static_cast<std::string::size_type>(1) && name.back() == '/')
  426. name.erase(std::prev(name.end()));
  427. ASIO2_ASSERT(!name.empty());
  428. if (!name.empty())
  429. {
  430. ((uris[index++] = std::string{ this->_to_char(M) } +name), ...);
  431. }
  432. return uris;
  433. }
  434. template<http::verb... M, class F, class... AOP>
  435. typename std::enable_if_t<!std::is_base_of_v<websocket::detail::listener_tag,
  436. detail::remove_cvref_t<F>>, void>
  437. inline _bind(std::string name, F f, AOP&&... aop)
  438. {
  439. constexpr bool CacheFlag = has_type<http::enable_cache_t, std::tuple<std::decay_t<AOP>...>>::value;
  440. auto tp = filter_cache<http::enable_cache_t>(std::forward<AOP>(aop)...);
  441. using Tup = decltype(tp);
  442. #if defined(ASIO2_ENABLE_LOG)
  443. static_assert(!has_type<http::enable_cache_t, Tup>::value);
  444. #endif
  445. std::shared_ptr<opfun> op = std::make_shared<opfun>(
  446. std::bind(&self::template _proxy<CacheFlag, F, Tup>, this,
  447. std::move(f), std::move(tp),
  448. std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
  449. this->_bind_uris(name, std::move(op), this->_make_uris<M...>(name));
  450. }
  451. template<http::verb... M, class F, class C, class... AOP>
  452. typename std::enable_if_t<!std::is_base_of_v<websocket::detail::listener_tag,
  453. detail::remove_cvref_t<F>> && std::is_same_v<C, typename function_traits<F>::class_type>, void>
  454. inline _bind(std::string name, F f, C* c, AOP&&... aop)
  455. {
  456. constexpr bool CacheFlag = has_type<http::enable_cache_t, std::tuple<std::decay_t<AOP>...>>::value;
  457. auto tp = filter_cache<http::enable_cache_t>(std::forward<AOP>(aop)...);
  458. using Tup = decltype(tp);
  459. #if defined(ASIO2_ENABLE_LOG)
  460. static_assert(!has_type<http::enable_cache_t, Tup>::value);
  461. #endif
  462. std::shared_ptr<opfun> op = std::make_shared<opfun>(
  463. std::bind(&self::template _proxy<CacheFlag, F, C, Tup>, this,
  464. std::move(f), c, std::move(tp),
  465. std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
  466. this->_bind_uris(name, std::move(op), this->_make_uris<M...>(name));
  467. }
  468. template<http::verb... M, class F, class C, class... AOP>
  469. typename std::enable_if_t<!std::is_base_of_v<websocket::detail::listener_tag,
  470. detail::remove_cvref_t<F>> && std::is_same_v<C, typename function_traits<F>::class_type>, void>
  471. inline _bind(std::string name, F f, C& c, AOP&&... aop)
  472. {
  473. this->_bind<M...>(std::move(name), std::move(f), std::addressof(c), std::forward<AOP>(aop)...);
  474. }
  475. template<class... AOP>
  476. inline void _bind(std::string name, websocket::listener<caller_t> listener, AOP&&... aop)
  477. {
  478. auto tp = filter_cache<http::enable_cache_t>(std::forward<AOP>(aop)...);
  479. using Tup = decltype(tp);
  480. #if defined(ASIO2_ENABLE_LOG)
  481. static_assert(!has_type<http::enable_cache_t, Tup>::value);
  482. #endif
  483. std::shared_ptr<opfun> op = std::make_shared<opfun>(
  484. std::bind(&self::template _proxy<Tup>, this,
  485. std::move(listener), std::move(tp),
  486. std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
  487. // https://datatracker.ietf.org/doc/rfc6455/
  488. // Once a connection to the server has been established (including a
  489. // connection via a proxy or over a TLS-encrypted tunnel), the client
  490. // MUST send an opening handshake to the server. The handshake consists
  491. // of an HTTP Upgrade request, along with a list of required and
  492. // optional header fields. The requirements for this handshake are as
  493. // follows.
  494. // 2. The method of the request MUST be GET, and the HTTP version MUST
  495. // be at least 1.1.
  496. this->_bind_uris(name, std::move(op), std::array<std::string, 1>{std::string{ "Z" } +name});
  497. }
  498. template<class URIS>
  499. inline void _bind_uris(std::string& name, std::shared_ptr<opfun> op, URIS uris)
  500. {
  501. for (auto& uri : uris)
  502. {
  503. if (uri.empty())
  504. continue;
  505. if (name.back() == '*')
  506. {
  507. ASIO2_ASSERT(this->wildcard_routers_.find(uri) == this->wildcard_routers_.end());
  508. this->wildcard_routers_[std::move(uri)] = op;
  509. }
  510. else
  511. {
  512. ASIO2_ASSERT(this->strictly_routers_.find(uri) == this->strictly_routers_.end());
  513. this->strictly_routers_[std::move(uri)] = op;
  514. }
  515. }
  516. }
  517. //template<http::verb... M, class C, class R, class...Ps, class... AOP>
  518. //inline void _bind(std::string name, R(C::*memfun)(Ps...), C* c, AOP&&... aop)
  519. //{
  520. //}
  521. template<bool CacheFlag, class F, class Tup>
  522. typename std::enable_if_t<!CacheFlag, opret>
  523. inline _proxy(F& f, Tup& aops,
  524. std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  525. {
  526. using fun_traits_type = function_traits<F>;
  527. using arg0_type = typename std::remove_cv_t<std::remove_reference_t<
  528. typename fun_traits_type::template args<0>::type>>;
  529. if (!_call_aop_before(aops, caller, req, rep))
  530. return std::addressof(rep.base());
  531. if constexpr (std::is_same_v<std::shared_ptr<caller_t>, arg0_type>)
  532. {
  533. f(caller, req, rep);
  534. }
  535. else
  536. {
  537. f(req, rep);
  538. }
  539. _call_aop_after(aops, caller, req, rep);
  540. return std::addressof(rep.base());
  541. }
  542. template<bool CacheFlag, class F, class C, class Tup>
  543. typename std::enable_if_t<!CacheFlag, opret>
  544. inline _proxy(F& f, C* c, Tup& aops,
  545. std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  546. {
  547. using fun_traits_type = function_traits<F>;
  548. using arg0_type = typename std::remove_cv_t<std::remove_reference_t<
  549. typename fun_traits_type::template args<0>::type>>;
  550. if (!_call_aop_before(aops, caller, req, rep))
  551. return std::addressof(rep.base());
  552. if constexpr (std::is_same_v<std::shared_ptr<caller_t>, arg0_type>)
  553. {
  554. if (c) (c->*f)(caller, req, rep);
  555. }
  556. else
  557. {
  558. if (c) (c->*f)(req, rep);
  559. }
  560. _call_aop_after(aops, caller, req, rep);
  561. return std::addressof(rep.base());
  562. }
  563. template<class Tup>
  564. inline opret _proxy(websocket::listener<caller_t>& listener, Tup& aops,
  565. std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  566. {
  567. ASIO2_ASSERT(req.ws_frame_type_ != websocket::frame::unknown);
  568. if (req.ws_frame_type_ == websocket::frame::open)
  569. {
  570. if (!_call_aop_before(aops, caller, req, rep))
  571. return nullptr;
  572. }
  573. if (req.ws_frame_type_ != websocket::frame::unknown)
  574. {
  575. listener(caller, req, rep);
  576. }
  577. if (req.ws_frame_type_ == websocket::frame::open)
  578. {
  579. if (!_call_aop_after(aops, caller, req, rep))
  580. return nullptr;
  581. }
  582. return std::addressof(rep.base());
  583. }
  584. template<bool CacheFlag, class F, class Tup>
  585. typename std::enable_if_t<CacheFlag, opret>
  586. inline _proxy(F& f, Tup& aops,
  587. std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  588. {
  589. using fun_traits_type = function_traits<F>;
  590. using arg0_type = typename std::remove_cv_t<std::remove_reference_t<
  591. typename fun_traits_type::template args<0>::type>>;
  592. using pcache_node = decltype(this->http_cache_.find(""));
  593. if (http::is_cache_enabled(req.base()))
  594. {
  595. pcache_node pcn = this->http_cache_.find(req.target());
  596. if (!pcn)
  597. {
  598. if (!_call_aop_before(aops, caller, req, rep))
  599. return std::addressof(rep.base());
  600. if constexpr (std::is_same_v<std::shared_ptr<caller_t>, arg0_type>)
  601. {
  602. f(caller, req, rep);
  603. }
  604. else
  605. {
  606. f(req, rep);
  607. }
  608. if (_call_aop_after(aops, caller, req, rep) && rep.result() == http::status::ok)
  609. {
  610. http::web_response::super msg{ std::move(rep.base()) };
  611. if (msg.body().to_text())
  612. {
  613. this->http_cache_.shrink_to_fit();
  614. pcn = this->http_cache_.emplace(req.target(), std::move(msg));
  615. return std::addressof(pcn->msg);
  616. }
  617. else
  618. {
  619. rep.base() = std::move(msg);
  620. }
  621. }
  622. return std::addressof(rep.base());
  623. }
  624. else
  625. {
  626. if (!_call_aop_before(aops, caller, req, rep))
  627. return std::addressof(rep.base());
  628. if (!_call_aop_after(aops, caller, req, rep))
  629. return std::addressof(rep.base());
  630. ASIO2_ASSERT(!pcn->msg.body().is_file());
  631. pcn->update_alive_time();
  632. return std::addressof(pcn->msg);
  633. }
  634. }
  635. else
  636. {
  637. if (!_call_aop_before(aops, caller, req, rep))
  638. return std::addressof(rep.base());
  639. if constexpr (std::is_same_v<std::shared_ptr<caller_t>, arg0_type>)
  640. {
  641. f(caller, req, rep);
  642. }
  643. else
  644. {
  645. f(req, rep);
  646. }
  647. _call_aop_after(aops, caller, req, rep);
  648. return std::addressof(rep.base());
  649. }
  650. }
  651. template<bool CacheFlag, class F, class C, class Tup>
  652. typename std::enable_if_t<CacheFlag, opret>
  653. inline _proxy(F& f, C* c, Tup& aops,
  654. std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  655. {
  656. using fun_traits_type = function_traits<F>;
  657. using arg0_type = typename std::remove_cv_t<std::remove_reference_t<
  658. typename fun_traits_type::template args<0>::type>>;
  659. using pcache_node = decltype(this->http_cache_.find(""));
  660. if (http::is_cache_enabled(req.base()))
  661. {
  662. pcache_node pcn = this->http_cache_.find(req.target());
  663. if (!pcn)
  664. {
  665. if (!_call_aop_before(aops, caller, req, rep))
  666. return std::addressof(rep.base());
  667. if constexpr (std::is_same_v<std::shared_ptr<caller_t>, arg0_type>)
  668. {
  669. if (c) (c->*f)(caller, req, rep);
  670. }
  671. else
  672. {
  673. if (c) (c->*f)(req, rep);
  674. }
  675. if (_call_aop_after(aops, caller, req, rep) && rep.result() == http::status::ok)
  676. {
  677. http::web_response::super msg{ std::move(rep.base()) };
  678. if (msg.body().to_text())
  679. {
  680. this->http_cache_.shrink_to_fit();
  681. pcn = this->http_cache_.emplace(req.target(), std::move(msg));
  682. return std::addressof(pcn->msg);
  683. }
  684. else
  685. {
  686. rep.base() = std::move(msg);
  687. }
  688. }
  689. return std::addressof(rep.base());
  690. }
  691. else
  692. {
  693. if (!_call_aop_before(aops, caller, req, rep))
  694. return std::addressof(rep.base());
  695. if (!_call_aop_after(aops, caller, req, rep))
  696. return std::addressof(rep.base());
  697. ASIO2_ASSERT(!pcn->msg.body().is_file());
  698. pcn->update_alive_time();
  699. return std::addressof(pcn->msg);
  700. }
  701. }
  702. else
  703. {
  704. if (!_call_aop_before(aops, caller, req, rep))
  705. return std::addressof(rep.base());
  706. if constexpr (std::is_same_v<std::shared_ptr<caller_t>, arg0_type>)
  707. {
  708. if (c) (c->*f)(caller, req, rep);
  709. }
  710. else
  711. {
  712. if (c) (c->*f)(req, rep);
  713. }
  714. _call_aop_after(aops, caller, req, rep);
  715. return std::addressof(rep.base());
  716. }
  717. }
  718. template<class F>
  719. inline void _bind_not_found(F f)
  720. {
  721. this->not_found_router_ = std::make_shared<opfun>(
  722. std::bind(&self::template _not_found_proxy<F>, this, std::move(f),
  723. std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
  724. }
  725. template<class F, class C>
  726. inline void _bind_not_found(F f, C* c)
  727. {
  728. this->not_found_router_ = std::make_shared<opfun>(
  729. std::bind(&self::template _not_found_proxy<F, C>, this, std::move(f), c,
  730. std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
  731. }
  732. template<class F, class C>
  733. inline void _bind_not_found(F f, C& c)
  734. {
  735. this->_bind_not_found(std::move(f), std::addressof(c));
  736. }
  737. template<class F>
  738. inline opret _not_found_proxy(F& f, std::shared_ptr<caller_t>& caller,
  739. http::web_request& req, http::web_response& rep)
  740. {
  741. asio2::detail::ignore_unused(caller);
  742. if constexpr (detail::is_template_callable_v<F, std::shared_ptr<caller_t>&,
  743. http::web_request&, http::web_response&>)
  744. {
  745. f(caller, req, rep);
  746. }
  747. else
  748. {
  749. f(req, rep);
  750. }
  751. return std::addressof(rep.base());
  752. }
  753. template<class F, class C>
  754. inline opret _not_found_proxy(F& f, C* c, std::shared_ptr<caller_t>& caller,
  755. http::web_request& req, http::web_response& rep)
  756. {
  757. asio2::detail::ignore_unused(caller);
  758. if constexpr (detail::is_template_callable_v<F, std::shared_ptr<caller_t>&,
  759. http::web_request&, http::web_response&>)
  760. {
  761. if (c) (c->*f)(caller, req, rep);
  762. }
  763. else
  764. {
  765. if (c) (c->*f)(req, rep);
  766. }
  767. return std::addressof(rep.base());
  768. }
  769. template <typename... Args, typename F, std::size_t... I>
  770. inline void _for_each_tuple(std::tuple<Args...>& t, const F& f, std::index_sequence<I...>)
  771. {
  772. (f(std::get<I>(t)), ...);
  773. }
  774. template<class Tup>
  775. inline bool _do_call_aop_before(
  776. Tup& aops, std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  777. {
  778. asio2::detail::ignore_unused(aops, caller, req, rep);
  779. asio2::detail::get_current_object<std::shared_ptr<caller_t>>() = caller;
  780. bool continued = true;
  781. _for_each_tuple(aops, [&continued, &caller, &req, &rep](auto& aop)
  782. {
  783. asio2::detail::ignore_unused(caller, req, rep);
  784. if (!continued)
  785. return;
  786. if constexpr (has_member_before<decltype(aop), bool,
  787. std::shared_ptr<caller_t>&, http::web_request&, http::web_response&>::value)
  788. {
  789. continued = aop.before(caller, req, rep);
  790. }
  791. else if constexpr (has_member_before<decltype(aop), bool,
  792. http::web_request&, http::web_response&>::value)
  793. {
  794. continued = aop.before(req, rep);
  795. }
  796. else
  797. {
  798. std::ignore = true;
  799. }
  800. }, std::make_index_sequence<std::tuple_size_v<Tup>>{});
  801. return continued;
  802. }
  803. template<class Tup>
  804. inline bool _call_aop_before(
  805. Tup& aops, std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  806. {
  807. asio2::detail::ignore_unused(aops, caller, req, rep);
  808. if constexpr (!std::tuple_size_v<Tup>)
  809. {
  810. return true;
  811. }
  812. else
  813. {
  814. return this->_do_call_aop_before(aops, caller, req, rep);
  815. }
  816. }
  817. template<class Tup>
  818. inline bool _do_call_aop_after(
  819. Tup& aops, std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  820. {
  821. asio2::detail::ignore_unused(aops, caller, req, rep);
  822. asio2::detail::get_current_object<std::shared_ptr<caller_t>>() = caller;
  823. bool continued = true;
  824. _for_each_tuple(aops, [&continued, &caller, &req, &rep](auto& aop)
  825. {
  826. asio2::detail::ignore_unused(caller, req, rep);
  827. if (!continued)
  828. return;
  829. if constexpr (has_member_after<decltype(aop), bool,
  830. std::shared_ptr<caller_t>&, http::web_request&, http::web_response&>::value)
  831. {
  832. continued = aop.after(caller, req, rep);
  833. }
  834. else if constexpr (has_member_after<decltype(aop), bool,
  835. http::web_request&, http::web_response&>::value)
  836. {
  837. continued = aop.after(req, rep);
  838. }
  839. else
  840. {
  841. std::ignore = true;
  842. }
  843. }, std::make_index_sequence<std::tuple_size_v<Tup>>{});
  844. return continued;
  845. }
  846. template<class Tup>
  847. inline bool _call_aop_after(
  848. Tup& aops, std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  849. {
  850. asio2::detail::ignore_unused(aops, caller, req, rep);
  851. if constexpr (!std::tuple_size_v<Tup>)
  852. {
  853. return true;
  854. }
  855. else
  856. {
  857. if (rep.defer_guard_)
  858. {
  859. rep.defer_guard_->aop_after_cb_ = std::make_unique<std::function<void()>>(
  860. [this, &aops, caller, &req, &rep]() mutable
  861. {
  862. caller_t* p = caller.get();
  863. asio::dispatch(p->io_->context(), make_allocator(p->wallocator(),
  864. [this, &aops, caller = std::move(caller), &req, &rep]() mutable
  865. {
  866. this->_do_call_aop_after(aops, caller, req, rep);
  867. }));
  868. });
  869. return true;
  870. }
  871. else
  872. {
  873. return this->_do_call_aop_after(aops, caller, req, rep);
  874. }
  875. }
  876. }
  877. inline std::string _make_uri(std::string_view root, std::string_view path)
  878. {
  879. std::string uri;
  880. if (http::has_undecode_char(path, 1))
  881. {
  882. std::string decoded = http::url_decode(path);
  883. path = decoded;
  884. while (path.size() > static_cast<std::string_view::size_type>(1) && path.back() == '/')
  885. {
  886. path.remove_suffix(1);
  887. }
  888. uri.reserve(root.size() + path.size());
  889. uri += root;
  890. uri += path;
  891. }
  892. else
  893. {
  894. while (path.size() > static_cast<std::string_view::size_type>(1) && path.back() == '/')
  895. {
  896. path.remove_suffix(1);
  897. }
  898. uri.reserve(root.size() + path.size());
  899. uri += root;
  900. uri += path;
  901. }
  902. return uri;
  903. }
  904. template<bool IsHttp>
  905. inline std::shared_ptr<opfun>& _find(http::web_request& req, http::web_response& rep)
  906. {
  907. asio2::detail::ignore_unused(rep);
  908. std::string uri;
  909. if constexpr (IsHttp)
  910. {
  911. uri = this->_make_uri(this->_to_char(req.method()), req.path());
  912. }
  913. else
  914. {
  915. uri = this->_make_uri(std::string_view{ "Z" }, req.path());
  916. }
  917. {
  918. auto it = this->strictly_routers_.find(uri);
  919. if (it != this->strictly_routers_.end())
  920. {
  921. return (it->second);
  922. }
  923. }
  924. // Find the best match url from tail to head
  925. if (!wildcard_routers_.empty())
  926. {
  927. for (auto it = this->wildcard_routers_.rbegin(); it != this->wildcard_routers_.rend(); ++it)
  928. {
  929. auto& k = it->first;
  930. ASIO2_ASSERT(k.size() >= std::size_t(3));
  931. if (!uri.empty() && !k.empty() && uri.front() == k.front() && uri.size() >= (k.size() - 2)
  932. && uri[k.size() - 3] == k[k.size() - 3] && http::url_match(k, uri))
  933. {
  934. return (it->second);
  935. }
  936. }
  937. }
  938. return this->dummy_router_;
  939. }
  940. inline opret _route(std::shared_ptr<caller_t>& caller, http::web_request& req, http::web_response& rep)
  941. {
  942. if (caller->websocket_router_)
  943. {
  944. return (*(caller->websocket_router_))(caller, req, rep);
  945. }
  946. std::shared_ptr<opfun>& router_ptr = this->template _find<true>(req, rep);
  947. if (router_ptr)
  948. {
  949. return (*router_ptr)(caller, req, rep);
  950. }
  951. if (this->not_found_router_ && (*(this->not_found_router_)))
  952. (*(this->not_found_router_))(caller, req, rep);
  953. return std::addressof(rep.base());
  954. }
  955. inline constexpr std::string_view _to_char(http::verb method) noexcept
  956. {
  957. using namespace std::literals;
  958. // Use a special name to avoid conflicts with global variable names
  959. constexpr std::string_view _hrmchars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"sv;
  960. return _hrmchars.substr(detail::to_underlying(method), 1);
  961. }
  962. protected:
  963. std::filesystem::path root_directory_ = std::filesystem::current_path();
  964. bool support_websocket_ = true;
  965. std::unordered_map<std::string, std::shared_ptr<opfun>> strictly_routers_;
  966. std:: map<std::string, std::shared_ptr<opfun>> wildcard_routers_;
  967. std::shared_ptr<opfun> not_found_router_;
  968. inline static std::shared_ptr<opfun> dummy_router_;
  969. detail::http_cache_t<caller_t, args_t> http_cache_;
  970. };
  971. }
  972. #include <asio2/base/detail/pop_options.hpp>
  973. #endif // !__ASIO2_HTTP_ROUTER_HPP__