chunk_encode.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. //
  2. // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // Official repository: https://github.com/boostorg/beast
  8. //
  9. #ifndef BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP
  10. #define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP
  11. #include <boost/beast/core/buffer_traits.hpp>
  12. #include <boost/beast/core/detail/varint.hpp>
  13. #include <boost/beast/http/error.hpp>
  14. #include <boost/beast/http/detail/rfc7230.hpp>
  15. #include <algorithm>
  16. namespace boost {
  17. namespace beast {
  18. namespace http {
  19. inline
  20. chunk_header::
  21. chunk_header(std::size_t size)
  22. : view_(
  23. size,
  24. net::const_buffer{nullptr, 0},
  25. chunk_crlf{})
  26. {
  27. BOOST_ASSERT(size > 0);
  28. }
  29. inline
  30. chunk_header::
  31. chunk_header(
  32. std::size_t size,
  33. string_view extensions)
  34. : view_(
  35. size,
  36. net::const_buffer{
  37. extensions.data(), extensions.size()},
  38. chunk_crlf{})
  39. {
  40. BOOST_ASSERT(size > 0);
  41. }
  42. template<class ChunkExtensions, class>
  43. chunk_header::
  44. chunk_header(
  45. std::size_t size,
  46. ChunkExtensions&& extensions)
  47. : exts_(std::make_shared<detail::chunk_extensions_impl<
  48. typename std::decay<ChunkExtensions>::type>>(
  49. std::forward<ChunkExtensions>(extensions)))
  50. , view_(
  51. size,
  52. exts_->str(),
  53. chunk_crlf{})
  54. {
  55. static_assert(
  56. detail::is_chunk_extensions<ChunkExtensions>::value,
  57. "ChunkExtensions requirements not met");
  58. BOOST_ASSERT(size > 0);
  59. }
  60. template<class ChunkExtensions, class Allocator, class>
  61. chunk_header::
  62. chunk_header(
  63. std::size_t size,
  64. ChunkExtensions&& extensions,
  65. Allocator const& allocator)
  66. : exts_(std::allocate_shared<detail::chunk_extensions_impl<
  67. typename std::decay<ChunkExtensions>::type>>(allocator,
  68. std::forward<ChunkExtensions>(extensions)))
  69. , view_(
  70. size,
  71. exts_->str(),
  72. chunk_crlf{})
  73. {
  74. static_assert(
  75. detail::is_chunk_extensions<ChunkExtensions>::value,
  76. "ChunkExtensions requirements not met");
  77. BOOST_ASSERT(size > 0);
  78. }
  79. //------------------------------------------------------------------------------
  80. template<class ConstBufferSequence>
  81. chunk_body<ConstBufferSequence>::
  82. chunk_body(ConstBufferSequence const& buffers)
  83. : view_(
  84. buffer_bytes(buffers),
  85. net::const_buffer{nullptr, 0},
  86. chunk_crlf{},
  87. buffers,
  88. chunk_crlf{})
  89. {
  90. }
  91. template<class ConstBufferSequence>
  92. chunk_body<ConstBufferSequence>::
  93. chunk_body(
  94. ConstBufferSequence const& buffers,
  95. string_view extensions)
  96. : view_(
  97. buffer_bytes(buffers),
  98. net::const_buffer{
  99. extensions.data(), extensions.size()},
  100. chunk_crlf{},
  101. buffers,
  102. chunk_crlf{})
  103. {
  104. }
  105. template<class ConstBufferSequence>
  106. template<class ChunkExtensions, class>
  107. chunk_body<ConstBufferSequence>::
  108. chunk_body(
  109. ConstBufferSequence const& buffers,
  110. ChunkExtensions&& extensions)
  111. : exts_(std::make_shared<detail::chunk_extensions_impl<
  112. typename std::decay<ChunkExtensions>::type>>(
  113. std::forward<ChunkExtensions>(extensions)))
  114. , view_(
  115. buffer_bytes(buffers),
  116. exts_->str(),
  117. chunk_crlf{},
  118. buffers,
  119. chunk_crlf{})
  120. {
  121. }
  122. template<class ConstBufferSequence>
  123. template<class ChunkExtensions, class Allocator, class>
  124. chunk_body<ConstBufferSequence>::
  125. chunk_body(
  126. ConstBufferSequence const& buffers,
  127. ChunkExtensions&& extensions,
  128. Allocator const& allocator)
  129. : exts_(std::allocate_shared<detail::chunk_extensions_impl<
  130. typename std::decay<ChunkExtensions>::type>>(allocator,
  131. std::forward<ChunkExtensions>(extensions)))
  132. , view_(
  133. buffer_bytes(buffers),
  134. exts_->str(),
  135. chunk_crlf{},
  136. buffers,
  137. chunk_crlf{})
  138. {
  139. }
  140. //------------------------------------------------------------------------------
  141. template<class Trailer>
  142. template<class Allocator>
  143. auto
  144. chunk_last<Trailer>::
  145. prepare(Trailer const& trailer, Allocator const& allocator) ->
  146. buffers_type
  147. {
  148. auto sp = std::allocate_shared<typename
  149. Trailer::writer>(allocator, trailer);
  150. sp_ = sp;
  151. return sp->get();
  152. }
  153. template<class Trailer>
  154. auto
  155. chunk_last<Trailer>::
  156. prepare(Trailer const& trailer, std::true_type) ->
  157. buffers_type
  158. {
  159. auto sp = std::make_shared<
  160. typename Trailer::writer>(trailer);
  161. sp_ = sp;
  162. return sp->get();
  163. }
  164. template<class Trailer>
  165. auto
  166. chunk_last<Trailer>::
  167. prepare(Trailer const& trailer, std::false_type) ->
  168. buffers_type
  169. {
  170. return trailer;
  171. }
  172. template<class Trailer>
  173. chunk_last<Trailer>::
  174. chunk_last()
  175. : view_(
  176. detail::chunk_size0{},
  177. Trailer{})
  178. {
  179. }
  180. template<class Trailer>
  181. chunk_last<Trailer>::
  182. chunk_last(Trailer const& trailer)
  183. : view_(
  184. detail::chunk_size0{},
  185. prepare(trailer, is_fields<Trailer>{}))
  186. {
  187. }
  188. template<class Trailer>
  189. template<class DeducedTrailer, class Allocator, class>
  190. chunk_last<Trailer>::
  191. chunk_last(
  192. DeducedTrailer const& trailer, Allocator const& allocator)
  193. : view_(
  194. detail::chunk_size0{},
  195. prepare(trailer, allocator))
  196. {
  197. }
  198. //------------------------------------------------------------------------------
  199. template<class Allocator>
  200. class basic_chunk_extensions<Allocator>::const_iterator
  201. {
  202. friend class basic_chunk_extensions;
  203. using iter_type = char const*;
  204. iter_type it_;
  205. typename basic_chunk_extensions::value_type value_;
  206. explicit
  207. const_iterator(iter_type it)
  208. : it_(it)
  209. {
  210. }
  211. void
  212. increment();
  213. public:
  214. using value_type = typename
  215. basic_chunk_extensions::value_type;
  216. using pointer = value_type const*;
  217. using reference = value_type const&;
  218. using difference_type = std::ptrdiff_t;
  219. using iterator_category =
  220. std::forward_iterator_tag;
  221. const_iterator() = default;
  222. const_iterator(const_iterator&& other) = default;
  223. const_iterator(const_iterator const& other) = default;
  224. const_iterator& operator=(const_iterator&& other) = default;
  225. const_iterator& operator=(const_iterator const& other) = default;
  226. bool
  227. operator==(const_iterator const& other) const
  228. {
  229. return it_ == other.it_;
  230. }
  231. bool
  232. operator!=(const_iterator const& other) const
  233. {
  234. return !(*this == other);
  235. }
  236. reference
  237. operator*();
  238. pointer
  239. operator->()
  240. {
  241. return &(**this);
  242. }
  243. const_iterator&
  244. operator++()
  245. {
  246. increment();
  247. return *this;
  248. }
  249. const_iterator
  250. operator++(int)
  251. {
  252. auto temp = *this;
  253. increment();
  254. return temp;
  255. }
  256. };
  257. template<class Allocator>
  258. void
  259. basic_chunk_extensions<Allocator>::
  260. const_iterator::
  261. increment()
  262. {
  263. using beast::detail::varint_read;
  264. auto n = varint_read(it_);
  265. it_ += n;
  266. n = varint_read(it_);
  267. it_ += n;
  268. }
  269. template<class Allocator>
  270. auto
  271. basic_chunk_extensions<Allocator>::
  272. const_iterator::
  273. operator*() ->
  274. reference
  275. {
  276. using beast::detail::varint_read;
  277. auto it = it_;
  278. auto n = varint_read(it);
  279. value_.first = string_view{it, n};
  280. it += n;
  281. n = varint_read(it);
  282. value_.second = string_view{it, n};
  283. return value_;
  284. }
  285. //------------------------------------------------------------------------------
  286. template<class Allocator>
  287. template<class FwdIt>
  288. FwdIt
  289. basic_chunk_extensions<Allocator>::
  290. do_parse(FwdIt it, FwdIt last, error_code& ec)
  291. {
  292. /*
  293. chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
  294. BWS = *( SP / HTAB ) ; "Bad White Space"
  295. chunk-ext-name = token
  296. chunk-ext-val = token / quoted-string
  297. token = 1*tchar
  298. quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
  299. qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
  300. quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
  301. obs-text = %x80-FF
  302. https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
  303. */
  304. using beast::detail::varint_size;
  305. using beast::detail::varint_write;
  306. using CharT = char;
  307. using Traits = std::char_traits<CharT>;
  308. range_.reserve(static_cast<std::size_t>(
  309. std::distance(it, last) * 1.2));
  310. range_.resize(0);
  311. auto const emit_string =
  312. [this](FwdIt from, FwdIt to)
  313. {
  314. auto const len =
  315. std::distance(from, to);
  316. auto const offset = range_.size();
  317. range_.resize(
  318. offset +
  319. varint_size(len) +
  320. len);
  321. auto dest = &range_[offset];
  322. varint_write(dest, len);
  323. Traits::copy(dest, from, len);
  324. };
  325. auto const emit_string_plus_empty =
  326. [this](FwdIt from, FwdIt to)
  327. {
  328. auto const len =
  329. std::distance(from, to);
  330. auto const offset = range_.size();
  331. range_.resize(
  332. offset +
  333. varint_size(len) +
  334. len +
  335. varint_size(0));
  336. auto dest = &range_[offset];
  337. varint_write(dest, len);
  338. Traits::copy(dest, from, len);
  339. dest += len;
  340. varint_write(dest, 0);
  341. };
  342. auto const emit_empty_string =
  343. [this]
  344. {
  345. auto const offset = range_.size();
  346. range_.resize(offset + varint_size(0));
  347. auto dest = &range_[offset];
  348. varint_write(dest, 0);
  349. };
  350. loop:
  351. if(it == last)
  352. {
  353. ec = {};
  354. return it;
  355. }
  356. // BWS
  357. if(*it == ' ' || *it == '\t')
  358. {
  359. for(;;)
  360. {
  361. ++it;
  362. if(it == last)
  363. {
  364. ec = error::bad_chunk_extension;
  365. return it;
  366. }
  367. if(*it != ' ' && *it != '\t')
  368. break;
  369. }
  370. }
  371. // ';'
  372. if(*it != ';')
  373. {
  374. ec = error::bad_chunk_extension;
  375. return it;
  376. }
  377. semi:
  378. ++it; // skip ';'
  379. // BWS
  380. for(;;)
  381. {
  382. if(it == last)
  383. {
  384. ec = error::bad_chunk_extension;
  385. return it;
  386. }
  387. if(*it != ' ' && *it != '\t')
  388. break;
  389. ++it;
  390. }
  391. // chunk-ext-name
  392. {
  393. if(! detail::is_token_char(*it))
  394. {
  395. ec = error::bad_chunk_extension;
  396. return it;
  397. }
  398. auto const first = it;
  399. for(;;)
  400. {
  401. ++it;
  402. if(it == last)
  403. {
  404. emit_string_plus_empty(first, it);
  405. return it;
  406. }
  407. if(! detail::is_token_char(*it))
  408. break;
  409. }
  410. emit_string(first, it);
  411. }
  412. // BWS [ ";" / "=" ]
  413. for(;;)
  414. {
  415. if(*it != ' ' && *it != '\t')
  416. break;
  417. ++it;
  418. if(it == last)
  419. {
  420. ec = error::bad_chunk_extension;
  421. return it;
  422. }
  423. }
  424. if(*it == ';')
  425. {
  426. emit_empty_string();
  427. goto semi;
  428. }
  429. if(*it != '=')
  430. {
  431. ec = error::bad_chunk_extension;
  432. return it;
  433. }
  434. ++it; // skip '='
  435. // BWS
  436. for(;;)
  437. {
  438. if(it == last)
  439. {
  440. ec = error::bad_chunk_extension;
  441. return it;
  442. }
  443. if(*it != ' ' && *it != '\t')
  444. break;
  445. ++it;
  446. }
  447. // chunk-ext-val
  448. if(*it != '"')
  449. {
  450. // token
  451. if(! detail::is_token_char(*it))
  452. {
  453. ec = error::bad_chunk_extension;
  454. return it;
  455. }
  456. auto const first = it;
  457. for(;;)
  458. {
  459. ++it;
  460. if(it == last)
  461. break;
  462. if(! detail::is_token_char(*it))
  463. break;
  464. }
  465. emit_string(first, it);
  466. if(it == last)
  467. return it;
  468. }
  469. else
  470. {
  471. // quoted-string
  472. auto const first = ++it; // skip DQUOTE
  473. // first pass, count chars
  474. std::size_t len = 0;
  475. for(;;)
  476. {
  477. if(it == last)
  478. {
  479. ec = error::bad_chunk_extension;
  480. return it;
  481. }
  482. if(*it == '"')
  483. break;
  484. if(*it == '\\')
  485. {
  486. ++it;
  487. if(it == last)
  488. {
  489. ec = error::bad_chunk_extension;
  490. return it;
  491. }
  492. }
  493. ++len;
  494. ++it;
  495. }
  496. // now build the string
  497. auto const offset = range_.size();
  498. range_.resize(
  499. offset +
  500. varint_size(len) +
  501. len);
  502. auto dest = &range_[offset];
  503. varint_write(dest, len);
  504. it = first;
  505. for(;;)
  506. {
  507. BOOST_ASSERT(it != last);
  508. if(*it == '"')
  509. break;
  510. if(*it == '\\')
  511. {
  512. ++it;
  513. BOOST_ASSERT(it != last);
  514. }
  515. Traits::assign(*dest++, *it++);
  516. }
  517. ++it; // skip DQUOTE
  518. }
  519. goto loop;
  520. }
  521. template<class Allocator>
  522. void
  523. basic_chunk_extensions<Allocator>::
  524. do_insert(string_view name, string_view value)
  525. {
  526. /*
  527. chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
  528. chunk-ext-name = token
  529. chunk-ext-val = token / quoted-string
  530. token = 1*tchar
  531. quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
  532. qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
  533. quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
  534. obs-text = %x80-FF
  535. */
  536. if(value.empty())
  537. {
  538. s_.reserve(1 + name.size());
  539. s_.push_back(';');
  540. s_.append(name.data(), name.size());
  541. return;
  542. }
  543. bool is_token = true;
  544. for(auto const c : value)
  545. {
  546. if(! detail::is_token_char(c))
  547. {
  548. is_token = false;
  549. break;
  550. }
  551. }
  552. if(is_token)
  553. {
  554. // token
  555. s_.reserve(1 + name.size() + 1 + value.size());
  556. s_.push_back(';');
  557. s_.append(name.data(), name.size());
  558. s_.push_back('=');
  559. s_.append(value.data(), value.size());
  560. }
  561. else
  562. {
  563. // quoted-string
  564. s_.reserve(
  565. 1 + name.size() + 1 +
  566. 1 + value.size() + 20 + 1);
  567. s_.push_back(';');
  568. s_.append(name.data(), name.size());
  569. s_.append("=\"", 2);
  570. for(auto const c : value)
  571. {
  572. if(c == '\\')
  573. s_.append(R"(\\)", 2);
  574. else if(c == '\"')
  575. s_.append(R"(\")", 2);
  576. else
  577. s_.push_back(c);
  578. }
  579. s_.push_back('"');
  580. }
  581. }
  582. template<class Allocator>
  583. void
  584. basic_chunk_extensions<Allocator>::
  585. parse(string_view s, error_code& ec)
  586. {
  587. do_parse(s.data(), s.data() + s.size(), ec);
  588. if(! ec)
  589. {
  590. s_.clear();
  591. for(auto const& v : *this)
  592. do_insert(v.first, v.second);
  593. }
  594. }
  595. template<class Allocator>
  596. void
  597. basic_chunk_extensions<Allocator>::
  598. insert(string_view name)
  599. {
  600. do_insert(name, {});
  601. using beast::detail::varint_size;
  602. using beast::detail::varint_write;
  603. auto const offset = range_.size();
  604. range_.resize(
  605. offset +
  606. varint_size(name.size()) +
  607. name.size() +
  608. varint_size(0));
  609. auto dest = &range_[offset];
  610. varint_write(dest, name.size());
  611. std::memcpy(dest, name.data(), name.size());
  612. dest += name.size();
  613. varint_write(dest, 0);
  614. }
  615. template<class Allocator>
  616. void
  617. basic_chunk_extensions<Allocator>::
  618. insert(string_view name, string_view value)
  619. {
  620. do_insert(name, value);
  621. using beast::detail::varint_size;
  622. using beast::detail::varint_write;
  623. auto const offset = range_.size();
  624. range_.resize(
  625. offset +
  626. varint_size(name.size()) +
  627. name.size() +
  628. varint_size(value.size()) +
  629. value.size());
  630. auto dest = &range_[offset];
  631. varint_write(dest, name.size());
  632. std::memcpy(dest, name.data(), name.size());
  633. dest += name.size();
  634. varint_write(dest, value.size());
  635. std::memcpy(dest, value.data(), value.size());
  636. }
  637. template<class Allocator>
  638. auto
  639. basic_chunk_extensions<Allocator>::
  640. begin() const ->
  641. const_iterator
  642. {
  643. return const_iterator{range_.data()};
  644. }
  645. template<class Allocator>
  646. auto
  647. basic_chunk_extensions<Allocator>::
  648. end() const ->
  649. const_iterator
  650. {
  651. return const_iterator{
  652. range_.data() + range_.size()};
  653. }
  654. } // http
  655. } // beast
  656. } // boost
  657. #endif