basic_parser.ipp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  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_BASIC_PARSER_IPP
  10. #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
  11. #include <boost/beast/http/basic_parser.hpp>
  12. #include <boost/beast/http/error.hpp>
  13. #include <boost/beast/http/rfc7230.hpp>
  14. #include <boost/beast/core/buffer_traits.hpp>
  15. #include <boost/beast/core/detail/clamp.hpp>
  16. #include <boost/beast/core/detail/config.hpp>
  17. #include <boost/beast/core/detail/string.hpp>
  18. #include <boost/asio/buffer.hpp>
  19. #include <algorithm>
  20. #include <utility>
  21. namespace boost {
  22. namespace beast {
  23. namespace http {
  24. template<bool isRequest>
  25. bool
  26. basic_parser<isRequest>::
  27. keep_alive() const
  28. {
  29. BOOST_ASSERT(is_header_done());
  30. if(f_ & flagHTTP11)
  31. {
  32. if(f_ & flagConnectionClose)
  33. return false;
  34. }
  35. else
  36. {
  37. if(! (f_ & flagConnectionKeepAlive))
  38. return false;
  39. }
  40. return (f_ & flagNeedEOF) == 0;
  41. }
  42. template<bool isRequest>
  43. boost::optional<std::uint64_t>
  44. basic_parser<isRequest>::
  45. content_length() const
  46. {
  47. BOOST_ASSERT(is_header_done());
  48. return content_length_unchecked();
  49. }
  50. template<bool isRequest>
  51. boost::optional<std::uint64_t>
  52. basic_parser<isRequest>::
  53. content_length_remaining() const
  54. {
  55. BOOST_ASSERT(is_header_done());
  56. if(! (f_ & flagContentLength))
  57. return boost::none;
  58. return len_;
  59. }
  60. template<bool isRequest>
  61. void
  62. basic_parser<isRequest>::
  63. skip(bool v)
  64. {
  65. BOOST_ASSERT(! got_some());
  66. if(v)
  67. f_ |= flagSkipBody;
  68. else
  69. f_ &= ~flagSkipBody;
  70. }
  71. template<bool isRequest>
  72. std::size_t
  73. basic_parser<isRequest>::
  74. put(net::const_buffer buffer,
  75. error_code& ec)
  76. {
  77. BOOST_ASSERT(state_ != state::complete);
  78. auto p = static_cast<char const*>(buffer.data());
  79. auto n = buffer.size();
  80. auto const p0 = p;
  81. auto const p1 = p0 + n;
  82. ec = {};
  83. loop:
  84. switch(state_)
  85. {
  86. case state::nothing_yet:
  87. if(n == 0)
  88. {
  89. ec = error::need_more;
  90. return 0;
  91. }
  92. state_ = state::start_line;
  93. BOOST_FALLTHROUGH;
  94. case state::start_line:
  95. {
  96. maybe_need_more(p, n, ec);
  97. if(ec)
  98. goto done;
  99. parse_start_line(p, p + (std::min<std::size_t>)(
  100. header_limit_, n), ec, is_request{});
  101. if(ec)
  102. {
  103. if(ec == error::need_more)
  104. {
  105. if(n >= header_limit_)
  106. {
  107. ec = error::header_limit;
  108. goto done;
  109. }
  110. if(p + 3 <= p1)
  111. skip_ = static_cast<
  112. std::size_t>(p1 - p - 3);
  113. }
  114. goto done;
  115. }
  116. BOOST_ASSERT(! is_done());
  117. n = static_cast<std::size_t>(p1 - p);
  118. if(p >= p1)
  119. {
  120. ec = error::need_more;
  121. goto done;
  122. }
  123. BOOST_FALLTHROUGH;
  124. }
  125. case state::fields:
  126. maybe_need_more(p, n, ec);
  127. if(ec)
  128. goto done;
  129. parse_fields(p, p + (std::min<std::size_t>)(
  130. header_limit_, n), ec);
  131. if(ec)
  132. {
  133. if(ec == error::need_more)
  134. {
  135. if(n >= header_limit_)
  136. {
  137. ec = error::header_limit;
  138. goto done;
  139. }
  140. if(p + 3 <= p1)
  141. skip_ = static_cast<
  142. std::size_t>(p1 - p - 3);
  143. }
  144. goto done;
  145. }
  146. finish_header(ec, is_request{});
  147. break;
  148. case state::body0:
  149. BOOST_ASSERT(! skip_);
  150. this->on_body_init_impl(content_length(), ec);
  151. if(ec)
  152. goto done;
  153. state_ = state::body;
  154. BOOST_FALLTHROUGH;
  155. case state::body:
  156. BOOST_ASSERT(! skip_);
  157. parse_body(p, n, ec);
  158. if(ec)
  159. goto done;
  160. break;
  161. case state::body_to_eof0:
  162. BOOST_ASSERT(! skip_);
  163. this->on_body_init_impl(content_length(), ec);
  164. if(ec)
  165. goto done;
  166. state_ = state::body_to_eof;
  167. BOOST_FALLTHROUGH;
  168. case state::body_to_eof:
  169. BOOST_ASSERT(! skip_);
  170. parse_body_to_eof(p, n, ec);
  171. if(ec)
  172. goto done;
  173. break;
  174. case state::chunk_header0:
  175. this->on_body_init_impl(content_length(), ec);
  176. if(ec)
  177. goto done;
  178. state_ = state::chunk_header;
  179. BOOST_FALLTHROUGH;
  180. case state::chunk_header:
  181. parse_chunk_header(p, n, ec);
  182. if(ec)
  183. goto done;
  184. break;
  185. case state::chunk_body:
  186. parse_chunk_body(p, n, ec);
  187. if(ec)
  188. goto done;
  189. break;
  190. case state::complete:
  191. ec = {};
  192. goto done;
  193. }
  194. if(p < p1 && ! is_done() && eager())
  195. {
  196. n = static_cast<std::size_t>(p1 - p);
  197. goto loop;
  198. }
  199. done:
  200. return static_cast<std::size_t>(p - p0);
  201. }
  202. template<bool isRequest>
  203. void
  204. basic_parser<isRequest>::
  205. put_eof(error_code& ec)
  206. {
  207. BOOST_ASSERT(got_some());
  208. if( state_ == state::start_line ||
  209. state_ == state::fields)
  210. {
  211. ec = error::partial_message;
  212. return;
  213. }
  214. if(f_ & (flagContentLength | flagChunked))
  215. {
  216. if(state_ != state::complete)
  217. {
  218. ec = error::partial_message;
  219. return;
  220. }
  221. ec = {};
  222. return;
  223. }
  224. ec = {};
  225. this->on_finish_impl(ec);
  226. if(ec)
  227. return;
  228. state_ = state::complete;
  229. }
  230. template<bool isRequest>
  231. void
  232. basic_parser<isRequest>::
  233. maybe_need_more(
  234. char const* p, std::size_t n,
  235. error_code& ec)
  236. {
  237. if(skip_ == 0)
  238. return;
  239. if( n > header_limit_)
  240. n = header_limit_;
  241. if(n < skip_ + 4)
  242. {
  243. ec = error::need_more;
  244. return;
  245. }
  246. auto const term =
  247. find_eom(p + skip_, p + n);
  248. if(! term)
  249. {
  250. skip_ = n - 3;
  251. if(skip_ + 4 > header_limit_)
  252. {
  253. ec = error::header_limit;
  254. return;
  255. }
  256. ec = error::need_more;
  257. return;
  258. }
  259. skip_ = 0;
  260. }
  261. template<bool isRequest>
  262. void
  263. basic_parser<isRequest>::
  264. parse_start_line(
  265. char const*& in, char const* last,
  266. error_code& ec, std::true_type)
  267. {
  268. /*
  269. request-line = method SP request-target SP HTTP-version CRLF
  270. method = token
  271. */
  272. auto p = in;
  273. string_view method;
  274. parse_method(p, last, method, ec);
  275. if(ec)
  276. return;
  277. string_view target;
  278. parse_target(p, last, target, ec);
  279. if(ec)
  280. return;
  281. int version = 0;
  282. parse_version(p, last, version, ec);
  283. if(ec)
  284. return;
  285. if(version < 10 || version > 11)
  286. {
  287. ec = error::bad_version;
  288. return;
  289. }
  290. if(p + 2 > last)
  291. {
  292. ec = error::need_more;
  293. return;
  294. }
  295. if(p[0] != '\r' || p[1] != '\n')
  296. {
  297. ec = error::bad_version;
  298. return;
  299. }
  300. p += 2;
  301. if(version >= 11)
  302. f_ |= flagHTTP11;
  303. this->on_request_impl(string_to_verb(method),
  304. method, target, version, ec);
  305. if(ec)
  306. return;
  307. in = p;
  308. state_ = state::fields;
  309. }
  310. template<bool isRequest>
  311. void
  312. basic_parser<isRequest>::
  313. parse_start_line(
  314. char const*& in, char const* last,
  315. error_code& ec, std::false_type)
  316. {
  317. /*
  318. status-line = HTTP-version SP status-code SP reason-phrase CRLF
  319. status-code = 3*DIGIT
  320. reason-phrase = *( HTAB / SP / VCHAR / obs-text )
  321. */
  322. auto p = in;
  323. int version = 0;
  324. parse_version(p, last, version, ec);
  325. if(ec)
  326. return;
  327. if(version < 10 || version > 11)
  328. {
  329. ec = error::bad_version;
  330. return;
  331. }
  332. // SP
  333. if(p + 1 > last)
  334. {
  335. ec = error::need_more;
  336. return;
  337. }
  338. if(*p++ != ' ')
  339. {
  340. ec = error::bad_version;
  341. return;
  342. }
  343. parse_status(p, last, status_, ec);
  344. if(ec)
  345. return;
  346. // parse reason CRLF
  347. string_view reason;
  348. parse_reason(p, last, reason, ec);
  349. if(ec)
  350. return;
  351. if(version >= 11)
  352. f_ |= flagHTTP11;
  353. this->on_response_impl(
  354. status_, reason, version, ec);
  355. if(ec)
  356. return;
  357. in = p;
  358. state_ = state::fields;
  359. }
  360. template<bool isRequest>
  361. void
  362. basic_parser<isRequest>::
  363. parse_fields(char const*& in,
  364. char const* last, error_code& ec)
  365. {
  366. string_view name;
  367. string_view value;
  368. // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
  369. beast::detail::char_buffer<max_obs_fold> buf;
  370. auto p = in;
  371. for(;;)
  372. {
  373. if(p + 2 > last)
  374. {
  375. ec = error::need_more;
  376. return;
  377. }
  378. if(p[0] == '\r')
  379. {
  380. if(p[1] != '\n')
  381. ec = error::bad_line_ending;
  382. in = p + 2;
  383. return;
  384. }
  385. parse_field(p, last, name, value, buf, ec);
  386. if(ec)
  387. return;
  388. auto const f = string_to_field(name);
  389. do_field(f, value, ec);
  390. if(ec)
  391. return;
  392. this->on_field_impl(f, name, value, ec);
  393. if(ec)
  394. return;
  395. in = p;
  396. }
  397. }
  398. template<bool isRequest>
  399. void
  400. basic_parser<isRequest>::
  401. finish_header(error_code& ec, std::true_type)
  402. {
  403. // RFC 7230 section 3.3
  404. // https://tools.ietf.org/html/rfc7230#section-3.3
  405. if(f_ & flagSkipBody)
  406. {
  407. state_ = state::complete;
  408. }
  409. else if(f_ & flagContentLength)
  410. {
  411. if(len_ > body_limit_)
  412. {
  413. ec = error::body_limit;
  414. return;
  415. }
  416. if(len_ > 0)
  417. {
  418. f_ |= flagHasBody;
  419. state_ = state::body0;
  420. }
  421. else
  422. {
  423. state_ = state::complete;
  424. }
  425. }
  426. else if(f_ & flagChunked)
  427. {
  428. f_ |= flagHasBody;
  429. state_ = state::chunk_header0;
  430. }
  431. else
  432. {
  433. len_ = 0;
  434. len0_ = 0;
  435. state_ = state::complete;
  436. }
  437. ec = {};
  438. this->on_header_impl(ec);
  439. if(ec)
  440. return;
  441. if(state_ == state::complete)
  442. {
  443. this->on_finish_impl(ec);
  444. if(ec)
  445. return;
  446. }
  447. }
  448. template<bool isRequest>
  449. void
  450. basic_parser<isRequest>::
  451. finish_header(error_code& ec, std::false_type)
  452. {
  453. // RFC 7230 section 3.3
  454. // https://tools.ietf.org/html/rfc7230#section-3.3
  455. if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
  456. status_ / 100 == 1 || // 1xx e.g. Continue
  457. status_ == 204 || // No Content
  458. status_ == 304) // Not Modified
  459. {
  460. // VFALCO Content-Length may be present, but we
  461. // treat the message as not having a body.
  462. // https://github.com/boostorg/beast/issues/692
  463. state_ = state::complete;
  464. }
  465. else if(f_ & flagContentLength)
  466. {
  467. if(len_ > 0)
  468. {
  469. f_ |= flagHasBody;
  470. state_ = state::body0;
  471. if(len_ > body_limit_)
  472. {
  473. ec = error::body_limit;
  474. return;
  475. }
  476. }
  477. else
  478. {
  479. state_ = state::complete;
  480. }
  481. }
  482. else if(f_ & flagChunked)
  483. {
  484. f_ |= flagHasBody;
  485. state_ = state::chunk_header0;
  486. }
  487. else
  488. {
  489. f_ |= flagHasBody;
  490. f_ |= flagNeedEOF;
  491. state_ = state::body_to_eof0;
  492. }
  493. ec = {};
  494. this->on_header_impl(ec);
  495. if(ec)
  496. return;
  497. if(state_ == state::complete)
  498. {
  499. this->on_finish_impl(ec);
  500. if(ec)
  501. return;
  502. }
  503. }
  504. template<bool isRequest>
  505. void
  506. basic_parser<isRequest>::
  507. parse_body(char const*& p,
  508. std::size_t n, error_code& ec)
  509. {
  510. ec = {};
  511. n = this->on_body_impl(string_view{p,
  512. beast::detail::clamp(len_, n)}, ec);
  513. p += n;
  514. len_ -= n;
  515. if(ec)
  516. return;
  517. if(len_ > 0)
  518. return;
  519. this->on_finish_impl(ec);
  520. if(ec)
  521. return;
  522. state_ = state::complete;
  523. }
  524. template<bool isRequest>
  525. void
  526. basic_parser<isRequest>::
  527. parse_body_to_eof(char const*& p,
  528. std::size_t n, error_code& ec)
  529. {
  530. if(n > body_limit_)
  531. {
  532. ec = error::body_limit;
  533. return;
  534. }
  535. body_limit_ = body_limit_ - n;
  536. ec = {};
  537. n = this->on_body_impl(string_view{p, n}, ec);
  538. p += n;
  539. if(ec)
  540. return;
  541. }
  542. template<bool isRequest>
  543. void
  544. basic_parser<isRequest>::
  545. parse_chunk_header(char const*& p0,
  546. std::size_t n, error_code& ec)
  547. {
  548. /*
  549. chunked-body = *chunk last-chunk trailer-part CRLF
  550. chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
  551. last-chunk = 1*("0") [ chunk-ext ] CRLF
  552. trailer-part = *( header-field CRLF )
  553. chunk-size = 1*HEXDIG
  554. chunk-data = 1*OCTET ; a sequence of chunk-size octets
  555. chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
  556. chunk-ext-name = token
  557. chunk-ext-val = token / quoted-string
  558. */
  559. auto p = p0;
  560. auto const pend = p + n;
  561. char const* eol;
  562. if(! (f_ & flagFinalChunk))
  563. {
  564. if(n < skip_ + 2)
  565. {
  566. ec = error::need_more;
  567. return;
  568. }
  569. if(f_ & flagExpectCRLF)
  570. {
  571. // Treat the last CRLF in a chunk as
  572. // part of the next chunk, so p can
  573. // be parsed in one call instead of two.
  574. if(! parse_crlf(p))
  575. {
  576. ec = error::bad_chunk;
  577. return;
  578. }
  579. }
  580. eol = find_eol(p0 + skip_, pend, ec);
  581. if(ec)
  582. return;
  583. if(! eol)
  584. {
  585. ec = error::need_more;
  586. skip_ = n - 1;
  587. return;
  588. }
  589. skip_ = static_cast<
  590. std::size_t>(eol - 2 - p0);
  591. std::uint64_t size;
  592. if(! parse_hex(p, size))
  593. {
  594. ec = error::bad_chunk;
  595. return;
  596. }
  597. if(size != 0)
  598. {
  599. if(size > body_limit_)
  600. {
  601. ec = error::body_limit;
  602. return;
  603. }
  604. body_limit_ -= size;
  605. auto const start = p;
  606. parse_chunk_extensions(p, pend, ec);
  607. if(ec)
  608. return;
  609. if(p != eol -2 )
  610. {
  611. ec = error::bad_chunk_extension;
  612. return;
  613. }
  614. auto const ext = make_string(start, p);
  615. this->on_chunk_header_impl(size, ext, ec);
  616. if(ec)
  617. return;
  618. len_ = size;
  619. skip_ = 2;
  620. p0 = eol;
  621. f_ |= flagExpectCRLF;
  622. state_ = state::chunk_body;
  623. return;
  624. }
  625. f_ |= flagFinalChunk;
  626. }
  627. else
  628. {
  629. BOOST_ASSERT(n >= 5);
  630. if(f_ & flagExpectCRLF)
  631. BOOST_VERIFY(parse_crlf(p));
  632. std::uint64_t size;
  633. BOOST_VERIFY(parse_hex(p, size));
  634. eol = find_eol(p, pend, ec);
  635. BOOST_ASSERT(! ec);
  636. }
  637. auto eom = find_eom(p0 + skip_, pend);
  638. if(! eom)
  639. {
  640. BOOST_ASSERT(n >= 3);
  641. skip_ = n - 3;
  642. ec = error::need_more;
  643. return;
  644. }
  645. auto const start = p;
  646. parse_chunk_extensions(p, pend, ec);
  647. if(ec)
  648. return;
  649. if(p != eol - 2)
  650. {
  651. ec = error::bad_chunk_extension;
  652. return;
  653. }
  654. auto const ext = make_string(start, p);
  655. this->on_chunk_header_impl(0, ext, ec);
  656. if(ec)
  657. return;
  658. p = eol;
  659. parse_fields(p, eom, ec);
  660. if(ec)
  661. return;
  662. BOOST_ASSERT(p == eom);
  663. p0 = eom;
  664. this->on_finish_impl(ec);
  665. if(ec)
  666. return;
  667. state_ = state::complete;
  668. }
  669. template<bool isRequest>
  670. void
  671. basic_parser<isRequest>::
  672. parse_chunk_body(char const*& p,
  673. std::size_t n, error_code& ec)
  674. {
  675. ec = {};
  676. n = this->on_chunk_body_impl(
  677. len_, string_view{p,
  678. beast::detail::clamp(len_, n)}, ec);
  679. p += n;
  680. len_ -= n;
  681. if(len_ == 0)
  682. state_ = state::chunk_header;
  683. }
  684. template<bool isRequest>
  685. void
  686. basic_parser<isRequest>::
  687. do_field(field f,
  688. string_view value, error_code& ec)
  689. {
  690. using namespace beast::detail::string_literals;
  691. // Connection
  692. if(f == field::connection ||
  693. f == field::proxy_connection)
  694. {
  695. auto const list = opt_token_list{value};
  696. if(! validate_list(list))
  697. {
  698. // VFALCO Should this be a field specific error?
  699. ec = error::bad_value;
  700. return;
  701. }
  702. for(auto const& s : list)
  703. {
  704. if(beast::iequals("close"_sv, s))
  705. {
  706. f_ |= flagConnectionClose;
  707. continue;
  708. }
  709. if(beast::iequals("keep-alive"_sv, s))
  710. {
  711. f_ |= flagConnectionKeepAlive;
  712. continue;
  713. }
  714. if(beast::iequals("upgrade"_sv, s))
  715. {
  716. f_ |= flagConnectionUpgrade;
  717. continue;
  718. }
  719. }
  720. ec = {};
  721. return;
  722. }
  723. // Content-Length
  724. if(f == field::content_length)
  725. {
  726. auto bad_content_length = [&ec]
  727. {
  728. ec = error::bad_content_length;
  729. };
  730. // conflicting field
  731. if(f_ & flagChunked)
  732. return bad_content_length();
  733. // Content-length may be a comma-separated list of integers
  734. auto tokens_unprocessed = 1 +
  735. std::count(value.begin(), value.end(), ',');
  736. auto tokens = opt_token_list(value);
  737. if (tokens.begin() == tokens.end() ||
  738. !validate_list(tokens))
  739. return bad_content_length();
  740. auto existing = this->content_length_unchecked();
  741. for (auto tok : tokens)
  742. {
  743. std::uint64_t v;
  744. if (!parse_dec(tok, v))
  745. return bad_content_length();
  746. --tokens_unprocessed;
  747. if (existing.has_value())
  748. {
  749. if (v != *existing)
  750. return bad_content_length();
  751. }
  752. else
  753. {
  754. existing = v;
  755. }
  756. }
  757. if (tokens_unprocessed)
  758. return bad_content_length();
  759. BOOST_ASSERT(existing.has_value());
  760. ec = {};
  761. len_ = *existing;
  762. len0_ = *existing;
  763. f_ |= flagContentLength;
  764. return;
  765. }
  766. // Transfer-Encoding
  767. if(f == field::transfer_encoding)
  768. {
  769. if(f_ & flagChunked)
  770. {
  771. // duplicate
  772. ec = error::bad_transfer_encoding;
  773. return;
  774. }
  775. if(f_ & flagContentLength)
  776. {
  777. // conflicting field
  778. ec = error::bad_transfer_encoding;
  779. return;
  780. }
  781. ec = {};
  782. auto const v = token_list{value};
  783. auto const p = std::find_if(v.begin(), v.end(),
  784. [&](string_view const& s)
  785. {
  786. return beast::iequals("chunked"_sv, s);
  787. });
  788. if(p == v.end())
  789. return;
  790. if(std::next(p) != v.end())
  791. return;
  792. len_ = 0;
  793. f_ |= flagChunked;
  794. return;
  795. }
  796. // Upgrade
  797. if(f == field::upgrade)
  798. {
  799. ec = {};
  800. f_ |= flagUpgrade;
  801. return;
  802. }
  803. ec = {};
  804. }
  805. #ifdef BOOST_BEAST_SOURCE
  806. template class http::basic_parser<true>;
  807. template class http::basic_parser<false>;
  808. #endif
  809. } // http
  810. } // beast
  811. } // boost
  812. #endif