response.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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_RESPONSE_IMPL_HPP__
  11. #define __ASIO2_HTTP_RESPONSE_IMPL_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/base/define.hpp>
  17. #include <asio2/base/detail/filesystem.hpp>
  18. #include <asio2/base/impl/user_data_cp.hpp>
  19. #include <asio2/http/detail/flex_body.hpp>
  20. #include <asio2/http/detail/http_util.hpp>
  21. namespace asio2::detail
  22. {
  23. template<class, class> class http_router_t;
  24. }
  25. #ifdef ASIO2_HEADER_ONLY
  26. namespace bho::beast::http
  27. #else
  28. namespace boost::beast::http
  29. #endif
  30. {
  31. class response_defer
  32. {
  33. template<class, class> friend class asio2::detail::http_router_t;
  34. public:
  35. response_defer(std::function<void()> cb, std::shared_ptr<void> session)
  36. : cb_(std::move(cb)), session_(std::move(session))
  37. {
  38. ASIO2_ASSERT(session_);
  39. }
  40. ~response_defer()
  41. {
  42. if (aop_after_cb_)
  43. {
  44. (*aop_after_cb_)();
  45. }
  46. if (cb_)
  47. {
  48. cb_();
  49. }
  50. }
  51. protected:
  52. std::function<void()> cb_;
  53. std::unique_ptr<std::function<void()>> aop_after_cb_;
  54. // hold the http_session ptr, otherwise when the cb_ is calling, the session
  55. // maybe destroyed already, then the response("rep_") in the cb_ is destroyed
  56. // already, then it cause crash.
  57. std::shared_ptr<void> session_;
  58. };
  59. }
  60. namespace asio2::detail
  61. {
  62. ASIO2_CLASS_FORWARD_DECLARE_BASE;
  63. ASIO2_CLASS_FORWARD_DECLARE_TCP_BASE;
  64. ASIO2_CLASS_FORWARD_DECLARE_TCP_CLIENT;
  65. ASIO2_CLASS_FORWARD_DECLARE_TCP_SESSION;
  66. template<class Body, class Fields = http::fields>
  67. class http_response_impl_t
  68. : public http::message<false, Body, Fields>
  69. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  70. , public user_data_cp<http_response_impl_t<Body, Fields>>
  71. #endif
  72. {
  73. ASIO2_CLASS_FRIEND_DECLARE_BASE;
  74. ASIO2_CLASS_FRIEND_DECLARE_TCP_BASE;
  75. ASIO2_CLASS_FRIEND_DECLARE_TCP_CLIENT;
  76. ASIO2_CLASS_FRIEND_DECLARE_TCP_SESSION;
  77. template<class, class> friend class asio2::detail::http_router_t;
  78. public:
  79. using self = http_response_impl_t<Body, Fields>;
  80. using super = http::message<false, Body, Fields>;
  81. using header_type = typename super::header_type;
  82. using body_type = typename super::body_type;
  83. public:
  84. /**
  85. * @brief constructor
  86. * this default constructor it used for rdc call, beacuse the default status of
  87. * http response is 200, and if the rdc call failed, the status of the returned
  88. * http response is 200 too, so we set the status to another value at here.
  89. */
  90. explicit http_response_impl_t()
  91. : super()
  92. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  93. , user_data_cp<http_response_impl_t<Body, Fields>>()
  94. #endif
  95. {
  96. super::result(http::status::unknown);
  97. }
  98. template<typename... Args>
  99. explicit http_response_impl_t(Args&&... args)
  100. : super(std::forward<Args>(args)...)
  101. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  102. , user_data_cp<http_response_impl_t<Body, Fields>>()
  103. #endif
  104. {
  105. }
  106. http_response_impl_t(const http_response_impl_t& o)
  107. : super()
  108. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  109. , user_data_cp<http_response_impl_t<Body, Fields>>()
  110. #endif
  111. {
  112. this->base() = o.base();
  113. this->root_directory_ = o.root_directory_;
  114. this->defer_callback_ = o.defer_callback_;
  115. this->defer_guard_ = o.defer_guard_;
  116. this->session_ptr_ = o.session_ptr_;
  117. }
  118. http_response_impl_t(http_response_impl_t&& o)
  119. : super()
  120. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  121. , user_data_cp<http_response_impl_t<Body, Fields>>()
  122. #endif
  123. {
  124. this->base() = std::move(o.base());
  125. this->root_directory_ = o.root_directory_;
  126. this->defer_callback_ = o.defer_callback_;
  127. this->defer_guard_ = o.defer_guard_;
  128. this->session_ptr_ = o.session_ptr_;
  129. }
  130. self& operator=(const http_response_impl_t& o)
  131. {
  132. this->base() = o.base();
  133. this->root_directory_ = o.root_directory_;
  134. this->defer_callback_ = o.defer_callback_;
  135. this->defer_guard_ = o.defer_guard_;
  136. this->session_ptr_ = o.session_ptr_;
  137. return *this;
  138. }
  139. self& operator=(http_response_impl_t&& o)
  140. {
  141. this->base() = std::move(o.base());
  142. this->root_directory_ = o.root_directory_;
  143. this->defer_callback_ = o.defer_callback_;
  144. this->defer_guard_ = o.defer_guard_;
  145. this->session_ptr_ = o.session_ptr_;
  146. return *this;
  147. }
  148. template<class BodyT = Body>
  149. http_response_impl_t(const http::message<false, BodyT, Fields>& rep)
  150. : super()
  151. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  152. , user_data_cp<http_response_impl_t<Body, Fields>>()
  153. #endif
  154. {
  155. this->base() = rep;
  156. }
  157. template<class BodyT = Body>
  158. http_response_impl_t(http::message<false, BodyT, Fields>&& rep)
  159. : super()
  160. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  161. , user_data_cp<http_response_impl_t<Body, Fields>>()
  162. #endif
  163. {
  164. this->base() = std::move(rep);
  165. }
  166. template<class BodyT = Body>
  167. self& operator=(const http::message<false, BodyT, Fields>& rep)
  168. {
  169. this->base() = rep;
  170. return *this;
  171. }
  172. template<class BodyT = Body>
  173. self& operator=(http::message<false, BodyT, Fields>&& rep)
  174. {
  175. this->base() = std::move(rep);
  176. return *this;
  177. }
  178. //-------------------------------------------------
  179. http_response_impl_t(const http::message<false, http::string_body, Fields>& rep)
  180. : super()
  181. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  182. , user_data_cp<http_response_impl_t<Body, Fields>>()
  183. #endif
  184. {
  185. this->base().base() = rep.base();
  186. this->body().text() = rep.body();
  187. }
  188. http_response_impl_t(http::message<false, http::string_body, Fields>&& rep)
  189. : super()
  190. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  191. , user_data_cp<http_response_impl_t<Body, Fields>>()
  192. #endif
  193. {
  194. this->base().base() = std::move(rep.base());
  195. this->body().text() = std::move(rep.body());
  196. }
  197. self& operator=(const http::message<false, http::string_body, Fields>& rep)
  198. {
  199. this->base().base() = rep.base();
  200. this->body().text() = rep.body();
  201. return *this;
  202. }
  203. self& operator=(http::message<false, http::string_body, Fields>&& rep)
  204. {
  205. this->base().base() = std::move(rep.base());
  206. this->body().text() = std::move(rep.body());
  207. return *this;
  208. }
  209. //-------------------------------------------------
  210. http_response_impl_t(const http::message<false, http::file_body, Fields>& rep)
  211. : super()
  212. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  213. , user_data_cp<http_response_impl_t<Body, Fields>>()
  214. #endif
  215. {
  216. this->base().base() = rep.base();
  217. this->body().file() = rep.body();
  218. }
  219. http_response_impl_t(http::message<false, http::file_body, Fields>&& rep)
  220. : super()
  221. #ifdef ASIO2_ENABLE_HTTP_RESPONSE_USER_DATA
  222. , user_data_cp<http_response_impl_t<Body, Fields>>()
  223. #endif
  224. {
  225. this->base().base() = std::move(rep.base());
  226. this->body().file() = std::move(rep.body());
  227. }
  228. self& operator=(const http::message<false, http::file_body, Fields>& rep)
  229. {
  230. this->base().base() = rep.base();
  231. this->body().file() = rep.body();
  232. return *this;
  233. }
  234. self& operator=(http::message<false, http::file_body, Fields>&& rep)
  235. {
  236. this->base().base() = std::move(rep.base());
  237. this->body().file() = std::move(rep.body());
  238. return *this;
  239. }
  240. /**
  241. * @brief destructor
  242. */
  243. ~http_response_impl_t()
  244. {
  245. }
  246. /// Returns the base portion of the message
  247. inline super const& base() const noexcept
  248. {
  249. return *this;
  250. }
  251. /// Returns the base portion of the message
  252. inline super& base() noexcept
  253. {
  254. return *this;
  255. }
  256. inline void reset()
  257. {
  258. static_cast<super&>(*this) = {};
  259. this->result(http::status::unknown);
  260. }
  261. /**
  262. * @brief set the root directory where we load the files.
  263. */
  264. inline self& set_root_directory(const std::filesystem::path& path)
  265. {
  266. std::error_code ec{};
  267. this->root_directory_ = std::filesystem::canonical(path, ec);
  268. assert(!ec);
  269. return *this;
  270. }
  271. /**
  272. * @brief get the root directory where we load the files.
  273. */
  274. inline const std::filesystem::path& get_root_directory() noexcept
  275. {
  276. return this->root_directory_;
  277. }
  278. /**
  279. * @brief create a deferred http response, the response will not be send immediately,
  280. * the http response will be sent only when the returned std::shared_ptr<http::response_defer>
  281. * is completely destroyed
  282. */
  283. inline std::shared_ptr<http::response_defer> defer()
  284. {
  285. this->defer_guard_ = std::make_shared<http::response_defer>(
  286. this->defer_callback_, this->session_ptr_.lock());
  287. return this->defer_guard_;
  288. }
  289. public:
  290. /**
  291. * @brief Respond to http request with plain text content
  292. * @param content - the response body, it's usually a simple string,
  293. * and the content-type is "text/plain" by default.
  294. */
  295. template<class StringT>
  296. inline self& fill_text(StringT&& content, http::status result = http::status::ok,
  297. std::string_view mimetype = "text/plain", unsigned version = 11)
  298. {
  299. // must clear file_body
  300. this->body().file().close();
  301. this->set(http::field::server, BEAST_VERSION_STRING);
  302. this->set(http::field::content_type, mimetype.empty() ? "text/plain" : mimetype);
  303. this->result(result);
  304. this->version(version < 10 ? 11 : version);
  305. this->body().text() = detail::to_string(std::forward<StringT>(content));
  306. http::try_prepare_payload(*this);
  307. return (*this);
  308. }
  309. /**
  310. * @brief Respond to http request with json content
  311. */
  312. template<class StringT>
  313. inline self& fill_json(StringT&& content, http::status result = http::status::ok,
  314. std::string_view mimetype = "application/json", unsigned version = 11)
  315. {
  316. return this->fill_text(std::forward<StringT>(content), result,
  317. mimetype.empty() ? "application/json" : mimetype, version);
  318. }
  319. /**
  320. * @brief Respond to http request with html content
  321. * @param content - the response body, may be a plain text string, or a stardand
  322. * <html>...</html> string, it's just that the content-type is "text/html" by default.
  323. */
  324. template<class StringT>
  325. inline self& fill_html(StringT&& content, http::status result = http::status::ok,
  326. std::string_view mimetype = "text/html", unsigned version = 11)
  327. {
  328. return this->fill_text(std::forward<StringT>(content), result,
  329. mimetype.empty() ? "text/html" : mimetype, version);
  330. }
  331. /**
  332. * @brief Respond to http request with pre-prepared error page content
  333. * Generated a standard html error page automatically use the status coe 'result',
  334. * like <html>...</html>, and the content-type is "text/html" by default.
  335. */
  336. template<class StringT = std::string_view>
  337. inline self& fill_page(http::status result, StringT&& desc = std::string_view{},
  338. std::string_view mimetype = "text/html", unsigned version = 11)
  339. {
  340. return this->fill_text(http::error_page(result, std::forward<StringT>(desc)), result,
  341. mimetype.empty() ? "text/html" : mimetype, version);
  342. }
  343. /**
  344. * @brief Respond to http request with local file
  345. */
  346. inline self& fill_file(std::filesystem::path path,
  347. http::status result = http::status::ok, unsigned version = 11)
  348. {
  349. // if you want to build a absolute path by youself and passed it to fill_file function,
  350. // call set_root_directory("") first, then passed you absolute path to fill_file is ok.
  351. // Build the path to the requested file
  352. std::filesystem::path filepath;
  353. if (this->root_directory_.empty())
  354. {
  355. filepath = std::move(path);
  356. }
  357. else
  358. {
  359. filepath = detail::make_filepath(this->root_directory_, path);
  360. }
  361. filepath.make_preferred();
  362. // Attempt to open the file
  363. beast::error_code ec;
  364. this->body().file().open(filepath.string().c_str(), beast::file_mode::scan, ec);
  365. // Handle the case where the file doesn't exist
  366. if (ec == beast::errc::no_such_file_or_directory)
  367. return this->fill_page(http::status::not_found, {}, {}, version);
  368. // Handle an unknown error
  369. if (ec)
  370. return this->fill_page(http::status::not_found, ec.message(), {}, version);
  371. // Cache the size since we need it after the move
  372. auto const size = this->body().size();
  373. // Respond to GET request
  374. this->content_length(size);
  375. this->set(http::field::server, BEAST_VERSION_STRING);
  376. this->set(http::field::content_type, http::extension_to_mimetype(path.extension().string()));
  377. this->result(result);
  378. this->version(version < 10 ? 11 : version);
  379. return (*this);
  380. }
  381. /**
  382. * @brief Returns `true` if this HTTP response's Content-Type is "multipart/form-data";
  383. */
  384. inline bool has_multipart() const noexcept
  385. {
  386. return http::has_multipart(*this);
  387. }
  388. /**
  389. * @brief Get the "multipart/form-data" body content.
  390. */
  391. inline decltype(auto) get_multipart()
  392. {
  393. return http::multipart(*this);
  394. }
  395. /**
  396. * @brief Get the "multipart/form-data" body content. same as get_multipart
  397. */
  398. inline decltype(auto) multipart()
  399. {
  400. return this->get_multipart();
  401. }
  402. protected:
  403. std::filesystem::path root_directory_ = std::filesystem::current_path();
  404. std::function<void()> defer_callback_;
  405. std::shared_ptr<http::response_defer> defer_guard_;
  406. std::weak_ptr<void> session_ptr_;
  407. };
  408. }
  409. #ifdef ASIO2_HEADER_ONLY
  410. namespace bho::beast::http
  411. #else
  412. namespace boost::beast::http
  413. #endif
  414. {
  415. using web_response = asio2::detail::http_response_impl_t<http::flex_body>;
  416. }
  417. #include <asio2/base/detail/pop_options.hpp>
  418. #endif // !__ASIO2_HTTP_RESPONSE_IMPL_HPP__