chunk_encode.ipp 16 KB

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