basic_parser.ipp 18 KB

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