multipart.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * https://www.ietf.org/rfc/rfc1867.txt
  8. * https://tools.ietf.org/html/rfc1867#section-7
  9. *
  10. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  11. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  12. */
  13. #ifndef __ASIO2_HTTP_MULTIPART_HPP__
  14. #define __ASIO2_HTTP_MULTIPART_HPP__
  15. #include <asio2/base/detail/push_options.hpp>
  16. #include <stdlib.h>
  17. #include <ctype.h>
  18. #include <stdio.h>
  19. #include <stdarg.h>
  20. #include <string.h>
  21. #include <list>
  22. #include <map>
  23. #include <string>
  24. #include <string_view>
  25. #include <algorithm>
  26. #include <type_traits>
  27. #include <asio2/external/asio.hpp>
  28. #include <asio2/external/beast.hpp>
  29. #include <asio2/external/throw_exception.hpp>
  30. #include <asio2/util/string.hpp>
  31. #ifdef ASIO2_HEADER_ONLY
  32. namespace bho::beast::http
  33. #else
  34. namespace boost::beast::http
  35. #endif
  36. {
  37. #define LF '\n'
  38. #define CR '\r'
  39. #define CRLF "\r\n"
  40. template<class String>
  41. class basic_multipart_field
  42. {
  43. public:
  44. /// Constructor
  45. basic_multipart_field() = default;
  46. /// Constructor
  47. basic_multipart_field(basic_multipart_field&&) noexcept = default;
  48. /// Constructor
  49. basic_multipart_field(basic_multipart_field const&) = default;
  50. /// Assignment
  51. basic_multipart_field& operator=(basic_multipart_field&&) noexcept = default;
  52. /// Assignment
  53. basic_multipart_field& operator=(basic_multipart_field const&) = default;
  54. inline const String& content_disposition () { return content_disposition_ ; }
  55. inline const String& name () { return name_ ; }
  56. inline const String& value () { return value_ ; }
  57. inline const String& content_type () { return content_type_ ; }
  58. inline const String& filename () { return filename_ ; }
  59. inline const String& content_transfer_encoding() { return content_transfer_encoding_; }
  60. inline const String& get_content_disposition () { return content_disposition_ ; }
  61. inline const String& get_name () { return name_ ; }
  62. inline const String& get_value () { return value_ ; }
  63. inline const String& get_content_type () { return content_type_ ; }
  64. inline const String& get_filename () { return filename_ ; }
  65. inline const String& get_content_transfer_encoding() { return content_transfer_encoding_; }
  66. inline const String& content_disposition () const { return content_disposition_ ; }
  67. inline const String& name () const { return name_ ; }
  68. inline const String& value () const { return value_ ; }
  69. inline const String& content_type () const { return content_type_ ; }
  70. inline const String& filename () const { return filename_ ; }
  71. inline const String& content_transfer_encoding() const { return content_transfer_encoding_; }
  72. inline const String& get_content_disposition () const { return content_disposition_ ; }
  73. inline const String& get_name () const { return name_ ; }
  74. inline const String& get_value () const { return value_ ; }
  75. inline const String& get_content_type () const { return content_type_ ; }
  76. inline const String& get_filename () const { return filename_ ; }
  77. inline const String& get_content_transfer_encoding() const { return content_transfer_encoding_; }
  78. template<class Str> inline basic_multipart_field& content_disposition (Str&& v) { content_disposition_ = std::forward<Str>(v); return (*this); }
  79. template<class Str> inline basic_multipart_field& name (Str&& v) { name_ = std::forward<Str>(v); return (*this); }
  80. template<class Str> inline basic_multipart_field& value (Str&& v) { value_ = std::forward<Str>(v); return (*this); }
  81. template<class Str> inline basic_multipart_field& content_type (Str&& v) { content_type_ = std::forward<Str>(v); return (*this); }
  82. template<class Str> inline basic_multipart_field& filename (Str&& v) { filename_ = std::forward<Str>(v); return (*this); }
  83. template<class Str> inline basic_multipart_field& content_transfer_encoding(Str&& v) { content_transfer_encoding_ = std::forward<Str>(v); return (*this); }
  84. template<class Str> inline basic_multipart_field& set_content_disposition (Str&& v) { content_disposition_ = std::forward<Str>(v); return (*this); }
  85. template<class Str> inline basic_multipart_field& set_name (Str&& v) { name_ = std::forward<Str>(v); return (*this); }
  86. template<class Str> inline basic_multipart_field& set_value (Str&& v) { value_ = std::forward<Str>(v); return (*this); }
  87. template<class Str> inline basic_multipart_field& set_content_type (Str&& v) { content_type_ = std::forward<Str>(v); return (*this); }
  88. template<class Str> inline basic_multipart_field& set_filename (Str&& v) { filename_ = std::forward<Str>(v); return (*this); }
  89. template<class Str> inline basic_multipart_field& set_content_transfer_encoding(Str&& v) { content_transfer_encoding_ = std::forward<Str>(v); return (*this); }
  90. inline bool is_empty() const
  91. {
  92. return (
  93. content_disposition_ .empty() &&
  94. name_ .empty() &&
  95. value_ .empty() &&
  96. content_type_ .empty() &&
  97. filename_ .empty() &&
  98. content_transfer_encoding_.empty() );
  99. }
  100. inline bool empty() const
  101. {
  102. return this->is_empty();
  103. }
  104. protected:
  105. String content_disposition_;
  106. String name_;
  107. String value_;
  108. String content_type_;
  109. String filename_;
  110. String content_transfer_encoding_;
  111. };
  112. using multipart_field = basic_multipart_field<std::string>;
  113. template<class String>
  114. class basic_multipart_fields
  115. {
  116. public:
  117. /// Constructor
  118. basic_multipart_fields() = default;
  119. /// Constructor
  120. basic_multipart_fields(basic_multipart_fields&&) = default;
  121. /// Constructor
  122. basic_multipart_fields(basic_multipart_fields const&) = default;
  123. /// Assignment
  124. basic_multipart_fields& operator=(basic_multipart_fields&&) = default;
  125. /// Assignment
  126. basic_multipart_fields& operator=(basic_multipart_fields const&) = default;
  127. using const_iterator = typename std::list<basic_multipart_field<String>>::const_iterator;
  128. using iterator = typename std::list<basic_multipart_field<String>>::iterator;
  129. inline const String& boundary() { return boundary_; }
  130. inline const String& boundary() const { return boundary_; }
  131. inline const String& get_boundary() { return boundary_; }
  132. inline const String& get_boundary() const { return boundary_; }
  133. template<class Str> inline basic_multipart_fields& boundary(Str&& v) { boundary_ = std::forward<Str>(v); return (*this); }
  134. template<class Str> inline basic_multipart_fields& set_boundary(Str&& v) { boundary_ = std::forward<Str>(v); return (*this); }
  135. /** Returns the value for a field, or throws an exception.
  136. If more than one field with the specified name exists, the
  137. first field defined by insertion order is returned.
  138. @throws std::out_of_range if the field is not found.
  139. */
  140. inline const basic_multipart_field<String>& at(const String& name)
  141. {
  142. auto it = find(name);
  143. if (it == cend())
  144. ASIO2_THROW_EXCEPTION(std::out_of_range{ "field not found" });
  145. return (*it);
  146. }
  147. /*
  148. * Returns the value by name, or `""` if it does not exist.
  149. */
  150. inline const basic_multipart_field<String>& operator[](const String& name)
  151. {
  152. auto it = find(name);
  153. if (it == cend())
  154. return dummy_;
  155. return (*it);
  156. }
  157. /// Return a const iterator to the beginning of the field sequence.
  158. inline iterator begin() noexcept
  159. {
  160. return list_.begin();
  161. }
  162. /// Return a const iterator to the end of the field sequence.
  163. inline iterator end() noexcept
  164. {
  165. return list_.end();
  166. }
  167. /// Return a const iterator to the beginning of the field sequence.
  168. inline const_iterator begin() const noexcept
  169. {
  170. return list_.begin();
  171. }
  172. /// Return a const iterator to the end of the field sequence.
  173. inline const_iterator end() const noexcept
  174. {
  175. return list_.end();
  176. }
  177. /// Return a const iterator to the beginning of the field sequence.
  178. inline const_iterator cbegin() const noexcept
  179. {
  180. return list_.cbegin();
  181. }
  182. /// Return a const iterator to the end of the field sequence.
  183. inline const_iterator cend() const noexcept
  184. {
  185. return list_.cend();
  186. }
  187. /*
  188. * Remove all fields from the container
  189. */
  190. inline void clear() noexcept
  191. {
  192. set_.clear();
  193. list_.clear();
  194. }
  195. /*
  196. * Insert a field.
  197. */
  198. template<class String1, class String2>
  199. inline iterator insert(String1&& name, String2&& value)
  200. {
  201. basic_multipart_field<String> field;
  202. field.name (std::forward<String1>(name ));
  203. field.value(std::forward<String2>(value));
  204. auto iter = set_.emplace(field.name(), std::addressof(field));
  205. auto itel = list_.insert(std::next(list_.begin(), std::distance(set_.begin(), iter)), std::move(field));
  206. iter->second = itel.operator->();
  207. return itel;
  208. }
  209. /*
  210. * Insert a field.
  211. */
  212. inline iterator insert(basic_multipart_field<String> field)
  213. {
  214. auto iter = set_.emplace(field.name(), std::addressof(field));
  215. auto itel = list_.insert(std::next(list_.begin(), std::distance(set_.begin(), iter)), std::move(field));
  216. iter->second = itel.operator->();
  217. return itel;
  218. }
  219. /*
  220. * Set a field value, removing any other instances of that field.
  221. */
  222. template<class String1, class String2>
  223. inline iterator set(String1&& name, String2&& value)
  224. {
  225. basic_multipart_field<String> field;
  226. field.name (std::forward<String1>(name ));
  227. field.value(std::forward<String2>(value));
  228. erase(field.name());
  229. auto iter = set_.emplace(field.name(), std::addressof(field));
  230. auto itel = list_.insert(std::next(list_.begin(), std::distance(set_.begin(), iter)), std::move(field));
  231. iter->second = itel.operator->();
  232. return itel;
  233. }
  234. /*
  235. * Remove a field.
  236. */
  237. inline const_iterator erase(const_iterator pos)
  238. {
  239. auto next = pos;
  240. auto iter = set_.erase(std::next(set_.begin(), std::distance(list_.cbegin(), pos)));
  241. auto itel = list_.erase(pos);
  242. return (++next);
  243. }
  244. /*
  245. * Remove all fields with the specified name.
  246. */
  247. inline std::size_t erase(const String& name)
  248. {
  249. auto result = set_.equal_range(name);
  250. if (result.first == result.second)
  251. return std::size_t(0);
  252. list_.erase(std::next(list_.begin(), std::distance(set_.begin(), result.first)),
  253. std::next(list_.begin(), std::distance(set_.begin(), result.second)));
  254. set_.erase(result.first, result.second);
  255. return std::distance(result.first, result.second);
  256. }
  257. //--------------------------------------------------------------------------
  258. //
  259. // Lookup
  260. //
  261. //--------------------------------------------------------------------------
  262. /*
  263. * Return the number of fields with the specified name.
  264. */
  265. inline std::size_t count(const String& name) const
  266. {
  267. return set_.count(name);
  268. }
  269. /*
  270. * Find the multipart field iterator by name.
  271. */
  272. inline iterator find(const String& name)
  273. {
  274. auto it = set_.find(name);
  275. if (it == set_.end())
  276. return list_.end();
  277. return std::next(list_.begin(), std::distance(set_.begin(), it));
  278. }
  279. /*
  280. * Find the multipart field iterator by name.
  281. */
  282. inline const_iterator find(const String& name) const
  283. {
  284. auto it = set_.find(name);
  285. if (it == set_.end())
  286. return list_.cend();
  287. return std::next(list_.cbegin(), std::distance(set_.begin(), it));
  288. }
  289. /*
  290. * Returns a range of iterators to the fields with the specified name.
  291. */
  292. inline std::pair<const_iterator, const_iterator> equal_range(const String& name) const
  293. {
  294. auto result = set_.equal_range(name);
  295. if (result.first == result.second)
  296. return { list_.cend(), list_.cend() };
  297. return {
  298. std::next(list_.cbegin(),std::distance(set_.begin(),result.first)),
  299. std::next(list_.cbegin(),std::distance(set_.begin(),result.second)) };
  300. }
  301. protected:
  302. String boundary_;
  303. std::list<basic_multipart_field<String>> list_;
  304. std::multimap<String, basic_multipart_field<String>*> set_;
  305. inline static basic_multipart_field<String> dummy_{};
  306. };
  307. using multipart_fields = basic_multipart_fields<std::string>;
  308. /*
  309. * Convert a multipart fields to a string.
  310. */
  311. template<class String>
  312. inline std::string to_string(const basic_multipart_fields<String>& fields)
  313. {
  314. std::string body;
  315. std::string::size_type size = 0;
  316. for (auto it = fields.begin(); it != fields.end(); ++it)
  317. {
  318. size += fields.boundary().size() + 2 + 2;
  319. size += it->content_disposition ().size() + 22;
  320. size += it->name ().size() + 10;
  321. size += it->value ().size() + 4;
  322. size += it->content_type ().size() + (it->content_type ().empty() ? 0 : 16);
  323. size += it->filename ().size() + (it->filename ().empty() ? 0 : 12);
  324. size += it->content_transfer_encoding().size() + (it->content_transfer_encoding().empty() ? 0 : 29);
  325. }
  326. body.reserve(size + fields.boundary().size() + 2 + 2 + 2);
  327. for (auto it = fields.begin(); it != fields.end(); ++it)
  328. {
  329. body += "--";
  330. body += fields.boundary();
  331. body += CRLF;
  332. body += "Content-Disposition: ";
  333. body += it->content_disposition();
  334. if (!it->name().empty())
  335. {
  336. body += "; ";
  337. body += "name=\"";
  338. body += it->name();
  339. body += "\"";
  340. }
  341. if (!it->filename().empty())
  342. {
  343. body += "; ";
  344. body += "filename=\"";
  345. body += it->filename();
  346. body += "\"";
  347. }
  348. body += CRLF;
  349. if (!it->content_type().empty())
  350. {
  351. body += "Content-Type: ";
  352. body += it->content_type();
  353. body += CRLF;
  354. }
  355. if (!it->content_transfer_encoding().empty())
  356. {
  357. body += "Content-Transfer-Encoding: ";
  358. body += it->content_transfer_encoding();
  359. body += CRLF;
  360. }
  361. body += CRLF;
  362. if (!it->value().empty())
  363. {
  364. body += it->value();
  365. }
  366. body += CRLF;
  367. }
  368. body += "--";
  369. body += fields.boundary();
  370. body += "--";
  371. body += CRLF;
  372. return body;
  373. }
  374. /// Write the text for a multipart fields to an output stream.
  375. template<class String>
  376. inline std::ostream& operator<<(std::ostream& os, const basic_multipart_fields<String>& fields)
  377. {
  378. return os << to_string(fields);
  379. }
  380. namespace multipart_parser
  381. {
  382. template<class String>
  383. inline bool parse_field(basic_multipart_field<String>& field, std::string_view content)
  384. {
  385. // 8 == "\r\n" "\r\n\r\n" "\r\n"
  386. if (content.size() < 8)
  387. return false;
  388. // first 2 bytes must be "\r\n"
  389. if (content.substr(0, 2) != CRLF)
  390. return false;
  391. // last 2 bytes must be "\r\n"
  392. if (content.substr(content.size() - 2) != CRLF)
  393. return false;
  394. // remove the first "\r\n" and the last "\r\n"
  395. content = content.substr(2, content.size() - 4);
  396. // find the split of header and value
  397. auto split = content.find("\r\n\r\n");
  398. if (split == std::string_view::npos)
  399. return false;
  400. std::string_view header = content.substr(0, split);
  401. std::string_view value = content.substr(split + 4);
  402. std::string_view::size_type pos_row_1 = static_cast<std::string_view::size_type>( 0);
  403. std::string_view::size_type pos_row_2 = static_cast<std::string_view::size_type>(-2);
  404. for(;;)
  405. {
  406. pos_row_1 = pos_row_2 + 2;
  407. pos_row_2 = header.find("\r\n", pos_row_1);
  408. std::string_view header_row = header.substr(pos_row_1,
  409. pos_row_2 == std::string_view::npos ? pos_row_2 : pos_row_2 - pos_row_1);
  410. // find the header type name.
  411. auto pos1 = header_row.find(':');
  412. if (pos1 == std::string_view::npos)
  413. return false;
  414. // get the header type value.
  415. std::string_view type = header_row.substr(0, pos1);
  416. asio2::trim_both(type);
  417. ++pos1;
  418. if /**/(beast::iequals(type, "Content-Disposition"))
  419. {
  420. auto pos2 = header_row.find(';', pos1);
  421. std::string_view disposition = header_row.substr(pos1, pos2 == std::string_view::npos ? pos2 : pos2 - pos1);
  422. asio2::trim_both(disposition);
  423. field.content_disposition(disposition);
  424. if (pos2 != std::string_view::npos)
  425. {
  426. std::string_view kvs = header_row.substr(pos2 + 1);
  427. for (pos2 = 0;;)
  428. {
  429. auto pos3 = kvs.find(';', pos2);
  430. std::string_view kv = kvs.substr(pos2, pos3 == std::string_view::npos ? pos3 : pos3 - pos2);
  431. auto pos4 = kv.find('=');
  432. if (pos4 == std::string_view::npos)
  433. return false;
  434. std::string_view k = kv.substr(0, pos4);
  435. std::string_view v = kv.substr(pos4 + 1);
  436. asio2::trim_both(k);
  437. asio2::trim_both(v);
  438. if (!v.empty() && v.front() == '\"') v.remove_prefix(1);
  439. if (!v.empty() && v.back() == '\"') v.remove_suffix(1);
  440. if /**/ (beast::iequals(k, "name"))
  441. field.name(v);
  442. else if (beast::iequals(k, "filename"))
  443. field.filename(v);
  444. if (pos3 == std::string_view::npos)
  445. break;
  446. pos2 = pos3 + 1;
  447. }
  448. }
  449. }
  450. else if (beast::iequals(type, "Content-Type"))
  451. {
  452. field.content_type(header_row.substr(pos1 + 1));
  453. }
  454. else if (beast::iequals(type, "Content-Transfer-Encoding"))
  455. {
  456. field.content_transfer_encoding(header_row.substr(pos1 + 1));
  457. }
  458. else
  459. {
  460. ASIO2_ASSERT(false);
  461. }
  462. if (pos_row_2 == std::string_view::npos)
  463. break;
  464. }
  465. field.value(value);
  466. return true;
  467. }
  468. }
  469. template<class String = std::string>
  470. basic_multipart_fields<String> multipart_parser_execute(std::string_view body, std::string_view boundary)
  471. {
  472. using namespace multipart_parser;
  473. basic_multipart_fields<String> fields{};
  474. fields.boundary(boundary);
  475. std::string full_boundary{ "--" }; full_boundary += boundary;
  476. std::string_view bound = full_boundary;
  477. for (std::size_t i = 0; i < body.size();)
  478. {
  479. basic_multipart_field<String> field{};
  480. // find first boundary
  481. if (body.substr(i, bound.size()) != bound)
  482. break;
  483. i += bound.size();
  484. // check whether is the end.
  485. std::string_view tail = body.substr(i + 0, 2);
  486. if (tail == "--")
  487. {
  488. i += 2;
  489. if (i < body.size())
  490. {
  491. if (body[i] != CR)
  492. break;
  493. ++i;
  494. }
  495. if (i < body.size())
  496. {
  497. if (body[i] != LF)
  498. break;
  499. ++i;
  500. }
  501. break;
  502. }
  503. // find next boundary
  504. auto next = body.find(bound, i);
  505. if (next == std::string_view::npos)
  506. break;
  507. // field.
  508. std::string_view field_content = body.substr(i, next - i);
  509. if (!parse_field(field, field_content))
  510. break;
  511. fields.insert(std::move(field));
  512. i = next;
  513. }
  514. return fields;
  515. }
  516. template<bool isRequest, class Body, class Fields, class String = std::string>
  517. basic_multipart_fields<String> multipart_parser_execute(const http::message<isRequest, Body, Fields>& msg)
  518. {
  519. std::string_view type = msg[http::field::content_type];
  520. std::size_t pos1 = asio2::ifind(type, "multipart/form-data");
  521. if (pos1 == std::string_view::npos)
  522. return {};
  523. pos1 += 19; // std::strlen("multipart/form-data");
  524. pos1 = asio2::ifind(type, "boundary", pos1);
  525. if (pos1 == std::string_view::npos)
  526. return {};
  527. pos1 += 8; // std::strlen("boundary");
  528. pos1 = type.find('=', pos1);
  529. if (pos1 == std::string_view::npos)
  530. return {};
  531. pos1 += 1;
  532. std::size_t pos2 = type.find_first_of("\r;", pos1);
  533. std::string_view boundary = type.substr(pos1, pos2 == std::string_view::npos ? pos2 : pos2 - pos1);
  534. return multipart_parser_execute<String>(msg.body(), boundary);
  535. }
  536. #undef CRLF
  537. #undef LF
  538. #undef CR
  539. }
  540. #include <asio2/base/detail/pop_options.hpp>
  541. #endif