http_util.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  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_UTIL_HPP__
  11. #define __ASIO2_HTTP_UTIL_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <cctype>
  16. #include <sstream>
  17. #include <memory>
  18. #include <asio2/external/asio.hpp>
  19. #include <asio2/external/beast.hpp>
  20. #include <asio2/base/error.hpp>
  21. #include <asio2/base/detail/util.hpp>
  22. #include <asio2/base/detail/buffer_wrap.hpp>
  23. #include <asio2/http/detail/http_parser.h>
  24. #include <asio2/http/detail/mime_types.hpp>
  25. #include <asio2/http/multipart.hpp>
  26. #include <asio2/util/string.hpp>
  27. #ifdef ASIO2_HEADER_ONLY
  28. namespace bho::beast::http
  29. #else
  30. namespace boost::beast::http
  31. #endif
  32. {
  33. namespace
  34. {
  35. // Percent-encoding : https://en.wikipedia.org/wiki/Percent-encoding
  36. // ---- RFC 3986 section 2.2 Reserved Characters (January 2005)
  37. // !#$&'()*+,/:;=?@[]
  38. //
  39. // ---- RFC 3986 section 2.3 Unreserved Characters (January 2005)
  40. // ABCDEFGHIJKLMNOPQRSTUVWXYZ
  41. // abcdefghijklmnopqrstuvwxyz
  42. // 0123456789-_.~
  43. //
  44. //
  45. // http://www.baidu.com/query?key=x!#$&'()*+,/:;=?@[ ]-_.~%^{}\"|<>`\\y
  46. //
  47. // C# System.Web.HttpUtility.UrlEncode
  48. // http%3a%2f%2fwww.baidu.com%2fquery%3fkey%3dx!%23%24%26%27()*%2b%2c%2f%3a%3b%3d%3f%40%5b+%5d-_.%7e%25%5e%7b%7d%22%7c%3c%3e%60%5cy
  49. //
  50. // java.net.URLEncoder.encode
  51. // http%3A%2F%2Fwww.baidu.com%2Fquery%3Fkey%3Dx%21%23%24%26%27%28%29*%2B%2C%2F%3A%3B%3D%3F%40%5B+%5D-_.%7E%25%5E%7B%7D%5C%22%7C%3C%3E%60%5C%5Cy
  52. //
  53. // postman
  54. //
  55. //
  56. // asp
  57. // http://127.0.0.1/index.asp?id=x!#$&name='()*+,/:;=?@[ ]-_.~%^{}\"|<>`\\y
  58. // http://127.0.0.1/index.asp?id=x%21%23%24&name=%27%28%29*%2B%2C%2F%3A%3B=?%40%5B%20%5D-_.%7E%25%5E%7B%7D%22%7C%3C%3E%60%5Cy
  59. // the character &=? can't be encoded, otherwise the result of queryString is wrong.
  60. // <%
  61. // id=request.queryString("id")
  62. // response.write "id=" & id
  63. // response.write "</br>"
  64. // name=request.queryString("name")
  65. // response.write "name=" & name
  66. // %>
  67. //
  68. //
  69. // http%+y%2f%2fwww.baidu.com%2fquery%3fkey%3dx!%23%24%26%27()*%2b%2c%2f%3a%3b%3d%3f%40%5b+%5d-_.%7e%25%5e%7b%7d%22%7c%3c%3e%60%+5
  70. //
  71. // C# System.Web.HttpUtility.UrlDecode
  72. // http% y//www.baidu.com/query?key=x!#$&'()*+,/:;=?@[ ]-_.~%^{}"|<>`% 5
  73. //
  74. static constexpr char unreserved_char[] = {
  75. //0 1 2 3 4 5 6 7 8 9 A B C D E F
  76. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
  77. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
  78. 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, // 2
  79. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, // 3
  80. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
  81. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 5
  82. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
  83. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 7
  84. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
  85. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
  86. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
  87. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
  88. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
  89. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
  90. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
  91. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F
  92. };
  93. template<
  94. class CharT = char,
  95. class Traits = std::char_traits<CharT>,
  96. class Allocator = std::allocator<CharT>
  97. >
  98. std::basic_string<CharT, Traits, Allocator> url_decode(std::string_view url)
  99. {
  100. using size_type = typename std::string_view::size_type;
  101. using value_type = typename std::string_view::value_type;
  102. using rvalue_type = typename std::basic_string<CharT, Traits, Allocator>::value_type;
  103. std::basic_string<CharT, Traits, Allocator> r;
  104. r.reserve(url.size());
  105. if (url.empty())
  106. return r;
  107. for (size_type i = 0; i < url.size(); ++i)
  108. {
  109. value_type c = url[i];
  110. if (c == '%')
  111. {
  112. if (i + 3 <= url.size())
  113. {
  114. value_type h = url[i + 1];
  115. value_type l = url[i + 2];
  116. bool f1 = false, f2 = false;
  117. if /**/ (h >= '0' && h <= '9') { f1 = true; h = value_type(h - '0' ); }
  118. else if (h >= 'a' && h <= 'f') { f1 = true; h = value_type(h - 'a' + 10); }
  119. else if (h >= 'A' && h <= 'F') { f1 = true; h = value_type(h - 'A' + 10); }
  120. if /**/ (l >= '0' && l <= '9') { f2 = true; l = value_type(l - '0' ); }
  121. else if (l >= 'a' && l <= 'f') { f2 = true; l = value_type(l - 'a' + 10); }
  122. else if (l >= 'A' && l <= 'F') { f2 = true; l = value_type(l - 'A' + 10); }
  123. if (f1 && f2)
  124. {
  125. r += static_cast<rvalue_type>(h * 16 + l);
  126. i += 2;
  127. }
  128. else
  129. {
  130. r += static_cast<rvalue_type>(c);
  131. }
  132. }
  133. else
  134. {
  135. r += static_cast<rvalue_type>(c);
  136. }
  137. }
  138. else if (c == '+')
  139. {
  140. r += static_cast<rvalue_type>(' ');
  141. }
  142. else
  143. {
  144. r += static_cast<rvalue_type>(c);
  145. }
  146. }
  147. return r;
  148. }
  149. template<
  150. class CharT = char,
  151. class Traits = std::char_traits<CharT>,
  152. class Allocator = std::allocator<CharT>
  153. >
  154. std::basic_string<CharT, Traits, Allocator> url_encode(std::string_view url, std::size_t offset = 0)
  155. {
  156. using size_type [[maybe_unused]] = typename std::string_view::size_type;
  157. using value_type [[maybe_unused]] = typename std::string_view::value_type;
  158. using rvalue_type [[maybe_unused]] = typename std::basic_string<CharT, Traits, Allocator>::value_type;
  159. std::basic_string<CharT, Traits, Allocator> r;
  160. r.reserve(url.size() * 2);
  161. if (url.empty())
  162. return r;
  163. size_type i = 0;
  164. if (offset == 0)
  165. {
  166. http::parses::http_parser_url u;
  167. if (0 == http::parses::http_parser_parse_url(url.data(), url.size(), 0, std::addressof(u)))
  168. {
  169. if /**/ (u.field_set & (1 << (int)http::parses::url_fields::UF_PATH))
  170. {
  171. i = u.field_data[(int)http::parses::url_fields::UF_PATH].off + 1;
  172. }
  173. else if (u.field_set & (1 << (int)http::parses::url_fields::UF_PORT))
  174. {
  175. i = u.field_data[(int)http::parses::url_fields::UF_PORT].off +
  176. u.field_data[(int)http::parses::url_fields::UF_PORT].len;
  177. }
  178. else if (u.field_set & (1 << (int)http::parses::url_fields::UF_HOST))
  179. {
  180. i = u.field_data[(int)http::parses::url_fields::UF_HOST].off +
  181. u.field_data[(int)http::parses::url_fields::UF_HOST].len;
  182. }
  183. if constexpr (std::is_same_v<CharT, char>)
  184. {
  185. r += std::string_view{ url.data(), i };
  186. }
  187. else
  188. {
  189. for (size_type n = 0; n < i; ++n)
  190. {
  191. r += static_cast<rvalue_type>(url[n]);
  192. }
  193. }
  194. }
  195. }
  196. else
  197. {
  198. r += url.substr(0, offset);
  199. i = offset;
  200. }
  201. for (; i < url.size(); ++i)
  202. {
  203. unsigned char c = static_cast<unsigned char>(url[i]);
  204. if (/*std::isalnum(c) || */unreserved_char[c])
  205. {
  206. r += static_cast<rvalue_type>(c);
  207. }
  208. else
  209. {
  210. r += static_cast<rvalue_type>('%');
  211. rvalue_type h = rvalue_type(c >> 4);
  212. r += h > rvalue_type(9) ? rvalue_type(h + 55) : rvalue_type(h + 48);
  213. rvalue_type l = rvalue_type(c % 16);
  214. r += l > rvalue_type(9) ? rvalue_type(l + 55) : rvalue_type(l + 48);
  215. }
  216. }
  217. return r;
  218. }
  219. template<typename = void>
  220. bool has_unencode_char(std::string_view url, std::size_t offset = 0) noexcept
  221. {
  222. using size_type = typename std::string_view::size_type;
  223. using value_type = typename std::string_view::value_type;
  224. if (url.empty())
  225. return false;
  226. size_type i = 0;
  227. if (offset == 0)
  228. {
  229. http::parses::http_parser_url u;
  230. if (0 == http::parses::http_parser_parse_url(url.data(), url.size(), 0, std::addressof(u)))
  231. {
  232. if /**/ (u.field_set & (1 << (int)http::parses::url_fields::UF_PATH))
  233. {
  234. i = u.field_data[(int)http::parses::url_fields::UF_PATH].off + 1;
  235. }
  236. else if (u.field_set & (1 << (int)http::parses::url_fields::UF_PORT))
  237. {
  238. i = u.field_data[(int)http::parses::url_fields::UF_PORT].off +
  239. u.field_data[(int)http::parses::url_fields::UF_PORT].len;
  240. }
  241. else if (u.field_set & (1 << (int)http::parses::url_fields::UF_HOST))
  242. {
  243. i = u.field_data[(int)http::parses::url_fields::UF_HOST].off +
  244. u.field_data[(int)http::parses::url_fields::UF_HOST].len;
  245. }
  246. }
  247. }
  248. else
  249. {
  250. i = offset;
  251. }
  252. for (; i < url.size(); ++i)
  253. {
  254. unsigned char c = static_cast<unsigned char>(url[i]);
  255. if (c == static_cast<unsigned char>('%'))
  256. {
  257. if (i + 3 <= url.size())
  258. {
  259. value_type h = url[i + 1];
  260. value_type l = url[i + 2];
  261. if /**/ (h >= '0' && h <= '9') {}
  262. else if (h >= 'a' && h <= 'f') {}
  263. else if (h >= 'A' && h <= 'F') {}
  264. else { return true; }
  265. if /**/ (l >= '0' && l <= '9') {}
  266. else if (l >= 'a' && l <= 'f') {}
  267. else if (l >= 'A' && l <= 'F') {}
  268. else { return true; }
  269. i += 2;
  270. }
  271. else
  272. {
  273. return true;
  274. }
  275. }
  276. else if (!unreserved_char[c])
  277. {
  278. return true;
  279. }
  280. }
  281. return false;
  282. }
  283. template<typename = void>
  284. bool has_undecode_char(std::string_view url, std::size_t offset = 0) noexcept
  285. {
  286. using size_type = typename std::string_view::size_type;
  287. using value_type = typename std::string_view::value_type;
  288. if (url.empty())
  289. return false;
  290. for (size_type i = offset; i < url.size(); ++i)
  291. {
  292. value_type c = url[i];
  293. if (c == '%')
  294. {
  295. if (i + 3 <= url.size())
  296. {
  297. value_type h = url[i + 1];
  298. value_type l = url[i + 2];
  299. bool f1 = false, f2 = false;
  300. if /**/ (h >= '0' && h <= '9') { f1 = true; }
  301. else if (h >= 'a' && h <= 'f') { f1 = true; }
  302. else if (h >= 'A' && h <= 'F') { f1 = true; }
  303. if /**/ (l >= '0' && l <= '9') { f2 = true; }
  304. else if (l >= 'a' && l <= 'f') { f2 = true; }
  305. else if (l >= 'A' && l <= 'F') { f2 = true; }
  306. if (f1 && f2)
  307. {
  308. return true;
  309. }
  310. }
  311. }
  312. else if (c == '+')
  313. {
  314. return true;
  315. }
  316. }
  317. return false;
  318. }
  319. template<typename = void>
  320. std::string_view url_to_host(std::string_view url)
  321. {
  322. if (url.empty())
  323. return std::string_view{};
  324. http::parses::http_parser_url u;
  325. if (0 != http::parses::http_parser_parse_url(url.data(), url.size(), 0, std::addressof(u)))
  326. return std::string_view{};
  327. if (!(u.field_set & (1 << (int)http::parses::url_fields::UF_HOST)))
  328. return std::string_view{};
  329. return std::string_view{ &url[
  330. u.field_data[(int)http::parses::url_fields::UF_HOST].off],
  331. u.field_data[(int)http::parses::url_fields::UF_HOST].len };
  332. }
  333. template<typename = void>
  334. std::string_view url_to_port(std::string_view url)
  335. {
  336. if (url.empty())
  337. return std::string_view{};
  338. http::parses::http_parser_url u;
  339. if (0 != http::parses::http_parser_parse_url(url.data(), url.size(), 0, std::addressof(u)))
  340. return std::string_view{};
  341. if (u.field_set & (1 << (int)http::parses::url_fields::UF_PORT))
  342. return std::string_view{ &url[
  343. u.field_data[(int)http::parses::url_fields::UF_PORT].off],
  344. u.field_data[(int)http::parses::url_fields::UF_PORT].len };
  345. if (u.field_set & (1 << (int)http::parses::url_fields::UF_SCHEMA))
  346. {
  347. std::string_view schema(&url[
  348. u.field_data[(int)http::parses::url_fields::UF_SCHEMA].off],
  349. u.field_data[(int)http::parses::url_fields::UF_SCHEMA].len);
  350. if (asio2::iequals(schema, "http"))
  351. return std::string_view{ "80" };
  352. if (asio2::iequals(schema, "https"))
  353. return std::string_view{ "443" };
  354. }
  355. return std::string_view{ "80" };
  356. }
  357. template<typename = void>
  358. std::string_view url_to_path(std::string_view url)
  359. {
  360. if (url.empty())
  361. return std::string_view{};
  362. http::parses::http_parser_url u;
  363. if (0 != http::parses::http_parser_parse_url(url.data(), url.size(), 0, std::addressof(u)))
  364. return std::string_view{};
  365. if (!(u.field_set & (1 << (int)http::parses::url_fields::UF_PATH)))
  366. return std::string_view{ "/" };
  367. return std::string_view{ &url[
  368. u.field_data[(int)http::parses::url_fields::UF_PATH].off],
  369. u.field_data[(int)http::parses::url_fields::UF_PATH].len };
  370. }
  371. template<typename = void>
  372. std::string_view url_to_query(std::string_view url)
  373. {
  374. if (url.empty())
  375. return std::string_view{};
  376. http::parses::http_parser_url u;
  377. if (0 != http::parses::http_parser_parse_url(url.data(), url.size(), 0, std::addressof(u)))
  378. return std::string_view{};
  379. if (!(u.field_set & (1 << (int)http::parses::url_fields::UF_QUERY)))
  380. return std::string_view{};
  381. return std::string_view{ &url[
  382. u.field_data[(int)http::parses::url_fields::UF_QUERY].off],
  383. u.field_data[(int)http::parses::url_fields::UF_QUERY].len };
  384. }
  385. template<typename = void>
  386. inline bool url_match(std::string_view pattern, std::string_view url)
  387. {
  388. if (pattern == "*" || pattern == "/*")
  389. return true;
  390. if (url.empty())
  391. return false;
  392. std::vector<std::string_view> fragments = asio2::split(pattern, "*");
  393. std::size_t index = 0;
  394. while (!url.empty())
  395. {
  396. if (index == fragments.size())
  397. return (pattern.back() == '*');
  398. std::string_view fragment = fragments[index++];
  399. if (fragment.empty())
  400. continue;
  401. while (fragment.size() > static_cast<std::string_view::size_type>(1) && fragment.back() == '/')
  402. {
  403. fragment.remove_suffix(1);
  404. }
  405. std::size_t pos = url.find(fragment);
  406. if (pos == std::string_view::npos)
  407. return false;
  408. url = url.substr(pos + fragment.size());
  409. }
  410. return true;
  411. }
  412. template<class StringT = std::string_view>
  413. inline std::string make_error_page(http::status result, StringT&& desc = std::string_view{})
  414. {
  415. std::string_view reason = http::obsolete_reason(result);
  416. std::string_view descrb = asio2::detail::to_string_view(desc);
  417. std::string content;
  418. if (descrb.empty())
  419. content.reserve(reason.size() * 2 + 67);
  420. else
  421. content.reserve(reason.size() * 2 + 67 + descrb.size() + 21);
  422. content += "<html><head><title>";
  423. content += reason;
  424. content += "</title></head><body><h1>";
  425. content += std::to_string(asio2::detail::to_underlying(result));
  426. content += " ";
  427. content += reason;
  428. content += "</h1>";
  429. if (!descrb.empty())
  430. {
  431. content += "<p>Description : ";
  432. content += descrb;
  433. content += "</p>";
  434. }
  435. content += "</body></html>";
  436. return content;
  437. }
  438. template<class StringT = std::string_view>
  439. inline std::string error_page(http::status result, StringT&& desc = std::string_view{})
  440. {
  441. return make_error_page(result, std::forward<StringT>(desc));
  442. }
  443. /**
  444. * @brief Returns `true` if the HTTP message's Content-Type is "multipart/form-data";
  445. */
  446. template<class HttpMessage>
  447. inline bool has_multipart(const HttpMessage& msg) noexcept
  448. {
  449. return (asio2::ifind(msg[http::field::content_type], "multipart/form-data") != std::string_view::npos);
  450. }
  451. /**
  452. * @brief Get the "multipart/form-data" body content.
  453. */
  454. template<class HttpMessage, class String = std::string>
  455. inline basic_multipart_fields<String> get_multipart(const HttpMessage& msg)
  456. {
  457. return multipart_parser_execute(msg);
  458. }
  459. /**
  460. * @brief Get the "multipart/form-data" body content. same as get_multipart
  461. */
  462. template<class HttpMessage, class String = std::string>
  463. inline basic_multipart_fields<String> multipart(const HttpMessage& msg)
  464. {
  465. return get_multipart(msg);
  466. }
  467. }
  468. // /boost_1_80_0/libs/beast/example/doc/http_examples.hpp
  469. template<bool isRequest, class SyncReadStream, class DynamicBuffer, class HeaderCallback, class BodyCallback>
  470. void read_large_body(SyncReadStream& stream, DynamicBuffer& buffer, HeaderCallback&& cbh, BodyCallback&& cbb)
  471. {
  472. error_code& ec = asio2::get_last_error();
  473. // Declare the parser with an empty body since
  474. // we plan on capturing the chunks ourselves.
  475. http::parser<isRequest, http::string_body> hp;
  476. hp.body_limit((std::numeric_limits<std::uint64_t>::max)());
  477. // First read the complete header
  478. http::read_header(stream, buffer, hp, ec);
  479. if (ec)
  480. return;
  481. cbh(hp.get());
  482. // should we check the http reponse status?
  483. // should we handle the http range? 301 302 ?
  484. //if (hp.get().result() != http::status::ok)
  485. //{
  486. // ec = http::error::bad_status;
  487. // return;
  488. //}
  489. // if the http response has no body, returned with error.
  490. if (hp.is_done())
  491. {
  492. ec = http::error::end_of_stream;
  493. return;
  494. }
  495. http::parser<isRequest, http::buffer_body> p(std::move(hp));
  496. if (p.get().chunked())
  497. {
  498. // This container will hold the extensions for each chunk
  499. http::chunk_extensions ce;
  500. // This string will hold the body of each chunk
  501. //std::string chunk;
  502. // Declare our chunk header callback This is invoked
  503. // after each chunk header and also after the last chunk.
  504. auto header_cb = [&](
  505. std::uint64_t size, // Size of the chunk, or zero for the last chunk
  506. std::string_view extensions, // The raw chunk-extensions string. Already validated.
  507. error_code& ev) // We can set this to indicate an error
  508. {
  509. // Parse the chunk extensions so we can access them easily
  510. ce.parse(extensions, ev);
  511. if (ev)
  512. return;
  513. // See if the chunk is too big
  514. if (size > (std::numeric_limits<std::size_t>::max)())
  515. {
  516. ev = http::error::body_limit;
  517. return;
  518. }
  519. // Make sure we have enough storage, and
  520. // reset the container for the upcoming chunk
  521. //chunk.reserve(static_cast<std::size_t>(size));
  522. //chunk.clear();
  523. };
  524. // Set the callback. The function requires a non-const reference so we
  525. // use a local variable, since temporaries can only bind to const refs.
  526. p.on_chunk_header(header_cb);
  527. bool continued = true;
  528. // Declare the chunk body callback. This is called one or
  529. // more times for each piece of a chunk body.
  530. auto body_cb = [&](
  531. std::uint64_t remain, // The number of bytes left in this chunk
  532. std::string_view body, // A buffer holding chunk body data
  533. error_code& ev) // We can set this to indicate an error
  534. {
  535. // If this is the last piece of the chunk body,
  536. // set the error so that the call to `read` returns
  537. // and we can process the chunk.
  538. if (remain == body.size())
  539. ev = http::error::end_of_chunk;
  540. // Append this piece to our container
  541. //chunk.append(body.data(), body.size());
  542. continued = cbb(body);
  543. // The return value informs the parser of how much of the body we
  544. // consumed. We will indicate that we consumed everything passed in.
  545. return body.size();
  546. };
  547. p.on_chunk_body(body_cb);
  548. while (continued && !p.is_done())
  549. {
  550. // Read as much as we can. When we reach the end of the chunk, the chunk
  551. // body callback will make the read return with the end_of_chunk error.
  552. http::read(stream, buffer, p, ec);
  553. if (!ec)
  554. continue;
  555. else if (ec != http::error::end_of_chunk)
  556. return;
  557. else
  558. ec = {};
  559. }
  560. if (!continued && !p.is_done())
  561. {
  562. ec = asio::error::operation_aborted;
  563. }
  564. }
  565. else
  566. {
  567. std::array<char, 512> buf;
  568. while (!p.is_done())
  569. {
  570. p.get().body().data = buf.data();
  571. p.get().body().size = buf.size();
  572. std::size_t bytes_read = http::read(stream, buffer, p, ec);
  573. if (ec == http::error::need_buffer)
  574. ec = {};
  575. if (ec)
  576. return;
  577. ASIO2_ASSERT(bytes_read == buf.size() - p.get().body().size);
  578. if (!cbb(std::string_view(buf.data(), bytes_read)))
  579. {
  580. ec = asio::error::operation_aborted;
  581. break;
  582. }
  583. }
  584. }
  585. }
  586. template<bool isRequest, class Body, class Fields>
  587. inline void try_prepare_payload(http::message<isRequest, Body, Fields>& msg)
  588. {
  589. try
  590. {
  591. msg.prepare_payload();
  592. }
  593. catch (const std::exception&)
  594. {
  595. asio2::set_last_error(asio::error::invalid_argument);
  596. }
  597. }
  598. template<typename, typename = void>
  599. struct is_http_message : std::false_type {};
  600. template<typename T>
  601. struct is_http_message<T, std::void_t<typename T::header_type, typename T::body_type,
  602. typename std::enable_if_t<std::is_same_v<T, http::message<
  603. T::header_type::is_request::value,
  604. typename T::body_type,
  605. typename T::header_type::fields_type>>>>> : std::true_type {};
  606. template<class T>
  607. inline constexpr bool is_http_message_v = is_http_message<T>::value;
  608. }
  609. #ifdef ASIO2_HEADER_ONLY
  610. namespace bho::beast::websocket
  611. #else
  612. namespace boost::beast::websocket
  613. #endif
  614. {
  615. enum class frame : std::uint8_t
  616. {
  617. ///
  618. unknown,
  619. /// A message frame was received
  620. message,
  621. /// A ping frame was received
  622. ping,
  623. /// A pong frame was received
  624. pong,
  625. /// http is upgrade to websocket
  626. open,
  627. /// A close frame was received
  628. close
  629. };
  630. }
  631. #endif // !__ASIO2_HTTP_UTIL_HPP__