basic_parser.hpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  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_DETAIL_BASIC_PARSER_HPP
  10. #define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
  11. #include <boost/beast/core/static_string.hpp>
  12. #include <boost/beast/core/string.hpp>
  13. #include <boost/beast/core/detail/cpu_info.hpp>
  14. #include <boost/beast/http/error.hpp>
  15. #include <boost/beast/http/detail/rfc7230.hpp>
  16. #include <boost/config.hpp>
  17. #include <boost/version.hpp>
  18. #include <algorithm>
  19. #include <cstddef>
  20. #include <limits>
  21. #include <utility>
  22. namespace boost {
  23. namespace beast {
  24. namespace http {
  25. namespace detail {
  26. struct basic_parser_base
  27. {
  28. // limit on the size of the obs-fold buffer
  29. //
  30. // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
  31. //
  32. static std::size_t constexpr max_obs_fold = 4096;
  33. enum class state
  34. {
  35. nothing_yet = 0,
  36. start_line,
  37. fields,
  38. body0,
  39. body,
  40. body_to_eof0,
  41. body_to_eof,
  42. chunk_header0,
  43. chunk_header,
  44. chunk_body,
  45. complete
  46. };
  47. static
  48. bool
  49. is_pathchar(char c)
  50. {
  51. // VFALCO This looks the same as the one below...
  52. // TEXT = <any OCTET except CTLs, and excluding LWS>
  53. static bool constexpr tab[256] = {
  54. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
  55. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
  56. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
  57. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
  58. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
  59. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
  60. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
  61. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
  62. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
  63. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
  64. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
  65. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
  66. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
  67. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
  68. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
  69. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
  70. };
  71. return tab[static_cast<unsigned char>(c)];
  72. }
  73. static
  74. inline
  75. bool
  76. unhex(unsigned char& d, char c)
  77. {
  78. static signed char constexpr tab[256] = {
  79. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0
  80. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16
  81. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32
  82. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48
  83. -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64
  84. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80
  85. -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96
  86. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 112
  87. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 128
  88. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 144
  89. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 160
  90. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 176
  91. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 192
  92. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 208
  93. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 224
  94. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240
  95. };
  96. d = static_cast<unsigned char>(
  97. tab[static_cast<unsigned char>(c)]);
  98. return d != static_cast<unsigned char>(-1);
  99. }
  100. static
  101. bool
  102. is_digit(char c)
  103. {
  104. return static_cast<unsigned char>(c-'0') < 10;
  105. }
  106. static
  107. bool
  108. is_print(char c)
  109. {
  110. return static_cast<unsigned char>(c-32) < 95;
  111. }
  112. template<class FwdIt>
  113. static
  114. FwdIt
  115. trim_front(FwdIt it, FwdIt const& end)
  116. {
  117. while(it != end)
  118. {
  119. if(*it != ' ' && *it != '\t')
  120. break;
  121. ++it;
  122. }
  123. return it;
  124. }
  125. template<class RanIt>
  126. static
  127. RanIt
  128. trim_back(
  129. RanIt it, RanIt const& first)
  130. {
  131. while(it != first)
  132. {
  133. auto const c = it[-1];
  134. if(c != ' ' && c != '\t')
  135. break;
  136. --it;
  137. }
  138. return it;
  139. }
  140. static
  141. string_view
  142. make_string(char const* first, char const* last)
  143. {
  144. return {first, static_cast<
  145. std::size_t>(last - first)};
  146. }
  147. //--------------------------------------------------------------------------
  148. static
  149. std::pair<char const*, bool>
  150. find_fast(
  151. char const* buf,
  152. char const* buf_end,
  153. char const* ranges,
  154. size_t ranges_size)
  155. {
  156. bool found = false;
  157. boost::ignore_unused(buf_end, ranges, ranges_size);
  158. return {buf, found};
  159. }
  160. // VFALCO Can SIMD help this?
  161. static
  162. char const*
  163. find_eol(
  164. char const* it, char const* last,
  165. error_code& ec)
  166. {
  167. for(;;)
  168. {
  169. if(it == last)
  170. {
  171. ec.assign(0, ec.category());
  172. return nullptr;
  173. }
  174. if(*it == '\r')
  175. {
  176. if(++it == last)
  177. {
  178. ec.assign(0, ec.category());
  179. return nullptr;
  180. }
  181. if(*it != '\n')
  182. {
  183. ec = error::bad_line_ending;
  184. return nullptr;
  185. }
  186. ec.assign(0, ec.category());
  187. return ++it;
  188. }
  189. // VFALCO Should we handle the legacy case
  190. // for lines terminated with a single '\n'?
  191. ++it;
  192. }
  193. }
  194. static
  195. char const*
  196. find_eom(char const* p, char const* last)
  197. {
  198. for(;;)
  199. {
  200. if(p + 4 > last)
  201. return nullptr;
  202. if(p[3] != '\n')
  203. {
  204. if(p[3] == '\r')
  205. ++p;
  206. else
  207. p += 4;
  208. }
  209. else if(p[2] != '\r')
  210. {
  211. p += 4;
  212. }
  213. else if(p[1] != '\n')
  214. {
  215. p += 2;
  216. }
  217. else if(p[0] != '\r')
  218. {
  219. p += 2;
  220. }
  221. else
  222. {
  223. return p + 4;
  224. }
  225. }
  226. }
  227. //--------------------------------------------------------------------------
  228. static
  229. char const*
  230. parse_token_to_eol(
  231. char const* p,
  232. char const* last,
  233. char const*& token_last,
  234. error_code& ec)
  235. {
  236. for(;; ++p)
  237. {
  238. if(p >= last)
  239. {
  240. ec = error::need_more;
  241. return p;
  242. }
  243. if(BOOST_UNLIKELY(! is_print(*p)))
  244. if((BOOST_LIKELY(static_cast<
  245. unsigned char>(*p) < '\040') &&
  246. BOOST_LIKELY(*p != '\011')) ||
  247. BOOST_UNLIKELY(*p == '\177'))
  248. goto found_control;
  249. }
  250. found_control:
  251. if(BOOST_LIKELY(*p == '\r'))
  252. {
  253. if(++p >= last)
  254. {
  255. ec = error::need_more;
  256. return last;
  257. }
  258. if(*p++ != '\n')
  259. {
  260. ec = error::bad_line_ending;
  261. return last;
  262. }
  263. token_last = p - 2;
  264. }
  265. #if 0
  266. // VFALCO This allows `\n` by itself
  267. // to terminate a line
  268. else if(*p == '\n')
  269. {
  270. token_last = p;
  271. ++p;
  272. }
  273. #endif
  274. else
  275. {
  276. // invalid character
  277. return nullptr;
  278. }
  279. return p;
  280. }
  281. template<class Iter, class T>
  282. static
  283. typename std::enable_if<
  284. std::numeric_limits<T>::is_integer &&
  285. ! std::numeric_limits<T>::is_signed, bool>::type
  286. parse_dec(Iter it, Iter last, T& v)
  287. {
  288. if(it == last)
  289. return false;
  290. T tmp = 0;
  291. do
  292. {
  293. if((! is_digit(*it)) ||
  294. tmp > (std::numeric_limits<T>::max)() / 10)
  295. return false;
  296. tmp *= 10;
  297. T const d = *it - '0';
  298. if((std::numeric_limits<T>::max)() - tmp < d)
  299. return false;
  300. tmp += d;
  301. }
  302. while(++it != last);
  303. v = tmp;
  304. return true;
  305. }
  306. template<class Iter, class Unsigned>
  307. static
  308. bool
  309. parse_hex(Iter& it, Unsigned& v)
  310. {
  311. unsigned char d;
  312. if(! unhex(d, *it))
  313. return false;
  314. v = d;
  315. for(;;)
  316. {
  317. if(! unhex(d, *++it))
  318. break;
  319. auto const v0 = v;
  320. v = 16 * v + d;
  321. if(v < v0)
  322. return false;
  323. }
  324. return true;
  325. }
  326. static
  327. bool
  328. parse_crlf(char const*& it)
  329. {
  330. if( it[0] != '\r' || it[1] != '\n')
  331. return false;
  332. it += 2;
  333. return true;
  334. }
  335. static
  336. void
  337. parse_method(
  338. char const*& it, char const* last,
  339. string_view& result, error_code& ec)
  340. {
  341. // parse token SP
  342. auto const first = it;
  343. for(;; ++it)
  344. {
  345. if(it + 1 > last)
  346. {
  347. ec = error::need_more;
  348. return;
  349. }
  350. if(! detail::is_token_char(*it))
  351. break;
  352. }
  353. if(it + 1 > last)
  354. {
  355. ec = error::need_more;
  356. return;
  357. }
  358. if(*it != ' ')
  359. {
  360. ec = error::bad_method;
  361. return;
  362. }
  363. if(it == first)
  364. {
  365. // cannot be empty
  366. ec = error::bad_method;
  367. return;
  368. }
  369. result = make_string(first, it++);
  370. }
  371. static
  372. void
  373. parse_target(
  374. char const*& it, char const* last,
  375. string_view& result, error_code& ec)
  376. {
  377. // parse target SP
  378. auto const first = it;
  379. for(;; ++it)
  380. {
  381. if(it + 1 > last)
  382. {
  383. ec = error::need_more;
  384. return;
  385. }
  386. if(! is_pathchar(*it))
  387. break;
  388. }
  389. if(it + 1 > last)
  390. {
  391. ec = error::need_more;
  392. return;
  393. }
  394. if(*it != ' ')
  395. {
  396. ec = error::bad_target;
  397. return;
  398. }
  399. if(it == first)
  400. {
  401. // cannot be empty
  402. ec = error::bad_target;
  403. return;
  404. }
  405. result = make_string(first, it++);
  406. }
  407. static
  408. void
  409. parse_version(
  410. char const*& it, char const* last,
  411. int& result, error_code& ec)
  412. {
  413. if(it + 8 > last)
  414. {
  415. ec = error::need_more;
  416. return;
  417. }
  418. if(*it++ != 'H')
  419. {
  420. ec = error::bad_version;
  421. return;
  422. }
  423. if(*it++ != 'T')
  424. {
  425. ec = error::bad_version;
  426. return;
  427. }
  428. if(*it++ != 'T')
  429. {
  430. ec = error::bad_version;
  431. return;
  432. }
  433. if(*it++ != 'P')
  434. {
  435. ec = error::bad_version;
  436. return;
  437. }
  438. if(*it++ != '/')
  439. {
  440. ec = error::bad_version;
  441. return;
  442. }
  443. if(! is_digit(*it))
  444. {
  445. ec = error::bad_version;
  446. return;
  447. }
  448. result = 10 * (*it++ - '0');
  449. if(*it++ != '.')
  450. {
  451. ec = error::bad_version;
  452. return;
  453. }
  454. if(! is_digit(*it))
  455. {
  456. ec = error::bad_version;
  457. return;
  458. }
  459. result += *it++ - '0';
  460. }
  461. static
  462. void
  463. parse_status(
  464. char const*& it, char const* last,
  465. unsigned short& result, error_code& ec)
  466. {
  467. // parse 3(digit) SP
  468. if(it + 4 > last)
  469. {
  470. ec = error::need_more;
  471. return;
  472. }
  473. if(! is_digit(*it))
  474. {
  475. ec = error::bad_status;
  476. return;
  477. }
  478. result = 100 * (*it++ - '0');
  479. if(! is_digit(*it))
  480. {
  481. ec = error::bad_status;
  482. return;
  483. }
  484. result += 10 * (*it++ - '0');
  485. if(! is_digit(*it))
  486. {
  487. ec = error::bad_status;
  488. return;
  489. }
  490. result += *it++ - '0';
  491. if(*it++ != ' ')
  492. {
  493. ec = error::bad_status;
  494. return;
  495. }
  496. }
  497. void
  498. parse_reason(
  499. char const*& it, char const* last,
  500. string_view& result, error_code& ec)
  501. {
  502. auto const first = it;
  503. char const* token_last = nullptr;
  504. auto p = parse_token_to_eol(
  505. it, last, token_last, ec);
  506. if(ec)
  507. return;
  508. if(! p)
  509. {
  510. ec = error::bad_reason;
  511. return;
  512. }
  513. result = make_string(first, token_last);
  514. it = p;
  515. }
  516. template<std::size_t N>
  517. void
  518. parse_field(
  519. char const*& p,
  520. char const* last,
  521. string_view& name,
  522. string_view& value,
  523. static_string<N>& buf,
  524. error_code& ec)
  525. {
  526. /* header-field = field-name ":" OWS field-value OWS
  527. field-name = token
  528. field-value = *( field-content / obs-fold )
  529. field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
  530. field-vchar = VCHAR / obs-text
  531. obs-fold = CRLF 1*( SP / HTAB )
  532. ; obsolete line folding
  533. ; see Section 3.2.4
  534. token = 1*<any CHAR except CTLs or separators>
  535. CHAR = <any US-ASCII character (octets 0 - 127)>
  536. sep = "(" | ")" | "<" | ">" | "@"
  537. | "," | ";" | ":" | "\" | <">
  538. | "/" | "[" | "]" | "?" | "="
  539. | "{" | "}" | SP | HT
  540. */
  541. static char const* is_token =
  542. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  543. "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
  544. "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
  545. "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
  546. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  547. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  548. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  549. "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
  550. // name
  551. BOOST_ALIGNMENT(16) static const char ranges1[] =
  552. "\x00 " /* control chars and up to SP */
  553. "\"\"" /* 0x22 */
  554. "()" /* 0x28,0x29 */
  555. ",," /* 0x2c */
  556. "//" /* 0x2f */
  557. ":@" /* 0x3a-0x40 */
  558. "[]" /* 0x5b-0x5d */
  559. "{\377"; /* 0x7b-0xff */
  560. auto first = p;
  561. bool found;
  562. std::tie(p, found) = find_fast(
  563. p, last, ranges1, sizeof(ranges1)-1);
  564. if(! found && p >= last)
  565. {
  566. ec = error::need_more;
  567. return;
  568. }
  569. for(;;)
  570. {
  571. if(*p == ':')
  572. break;
  573. if(! is_token[static_cast<
  574. unsigned char>(*p)])
  575. {
  576. ec = error::bad_field;
  577. return;
  578. }
  579. ++p;
  580. if(p >= last)
  581. {
  582. ec = error::need_more;
  583. return;
  584. }
  585. }
  586. if(p == first)
  587. {
  588. // empty name
  589. ec = error::bad_field;
  590. return;
  591. }
  592. name = make_string(first, p);
  593. ++p; // eat ':'
  594. char const* token_last = nullptr;
  595. for(;;)
  596. {
  597. // eat leading ' ' and '\t'
  598. for(;;++p)
  599. {
  600. if(p + 1 > last)
  601. {
  602. ec = error::need_more;
  603. return;
  604. }
  605. if(! (*p == ' ' || *p == '\t'))
  606. break;
  607. }
  608. // parse to CRLF
  609. first = p;
  610. p = parse_token_to_eol(p, last, token_last, ec);
  611. if(ec)
  612. return;
  613. if(! p)
  614. {
  615. ec = error::bad_value;
  616. return;
  617. }
  618. // Look 1 char past the CRLF to handle obs-fold.
  619. if(p + 1 > last)
  620. {
  621. ec = error::need_more;
  622. return;
  623. }
  624. token_last =
  625. trim_back(token_last, first);
  626. if(*p != ' ' && *p != '\t')
  627. {
  628. value = make_string(first, token_last);
  629. return;
  630. }
  631. ++p;
  632. if(token_last != first)
  633. break;
  634. }
  635. buf.resize(0);
  636. buf.append(first, token_last);
  637. BOOST_ASSERT(! buf.empty());
  638. try
  639. {
  640. for(;;)
  641. {
  642. // eat leading ' ' and '\t'
  643. for(;;++p)
  644. {
  645. if(p + 1 > last)
  646. {
  647. ec = error::need_more;
  648. return;
  649. }
  650. if(! (*p == ' ' || *p == '\t'))
  651. break;
  652. }
  653. // parse to CRLF
  654. first = p;
  655. p = parse_token_to_eol(p, last, token_last, ec);
  656. if(ec)
  657. return;
  658. if(! p)
  659. {
  660. ec = error::bad_value;
  661. return;
  662. }
  663. // Look 1 char past the CRLF to handle obs-fold.
  664. if(p + 1 > last)
  665. {
  666. ec = error::need_more;
  667. return;
  668. }
  669. token_last = trim_back(token_last, first);
  670. if(first != token_last)
  671. {
  672. buf.push_back(' ');
  673. buf.append(first, token_last);
  674. }
  675. if(*p != ' ' && *p != '\t')
  676. {
  677. value = {buf.data(), buf.size()};
  678. return;
  679. }
  680. ++p;
  681. }
  682. }
  683. catch(std::length_error const&)
  684. {
  685. ec = error::header_limit;
  686. return;
  687. }
  688. }
  689. void
  690. parse_chunk_extensions(
  691. char const*& it,
  692. char const* last,
  693. error_code& ec)
  694. {
  695. /*
  696. chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
  697. BWS = *( SP / HTAB ) ; "Bad White Space"
  698. chunk-ext-name = token
  699. chunk-ext-val = token / quoted-string
  700. token = 1*tchar
  701. quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
  702. qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
  703. quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
  704. obs-text = %x80-FF
  705. https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
  706. */
  707. loop:
  708. if(it == last)
  709. {
  710. ec = error::need_more;
  711. return;
  712. }
  713. if(*it != ' ' && *it != '\t' && *it != ';')
  714. return;
  715. // BWS
  716. if(*it == ' ' || *it == '\t')
  717. {
  718. for(;;)
  719. {
  720. ++it;
  721. if(it == last)
  722. {
  723. ec = error::need_more;
  724. return;
  725. }
  726. if(*it != ' ' && *it != '\t')
  727. break;
  728. }
  729. }
  730. // ';'
  731. if(*it != ';')
  732. {
  733. ec = error::bad_chunk_extension;
  734. return;
  735. }
  736. semi:
  737. ++it; // skip ';'
  738. // BWS
  739. for(;;)
  740. {
  741. if(it == last)
  742. {
  743. ec = error::need_more;
  744. return;
  745. }
  746. if(*it != ' ' && *it != '\t')
  747. break;
  748. ++it;
  749. }
  750. // chunk-ext-name
  751. if(! detail::is_token_char(*it))
  752. {
  753. ec = error::bad_chunk_extension;
  754. return;
  755. }
  756. for(;;)
  757. {
  758. ++it;
  759. if(it == last)
  760. {
  761. ec = error::need_more;
  762. return;
  763. }
  764. if(! detail::is_token_char(*it))
  765. break;
  766. }
  767. // BWS [ ";" / "=" ]
  768. {
  769. bool bws;
  770. if(*it == ' ' || *it == '\t')
  771. {
  772. for(;;)
  773. {
  774. ++it;
  775. if(it == last)
  776. {
  777. ec = error::need_more;
  778. return;
  779. }
  780. if(*it != ' ' && *it != '\t')
  781. break;
  782. }
  783. bws = true;
  784. }
  785. else
  786. {
  787. bws = false;
  788. }
  789. if(*it == ';')
  790. goto semi;
  791. if(*it != '=')
  792. {
  793. if(bws)
  794. ec = error::bad_chunk_extension;
  795. return;
  796. }
  797. ++it; // skip '='
  798. }
  799. // BWS
  800. for(;;)
  801. {
  802. if(it == last)
  803. {
  804. ec = error::need_more;
  805. return;
  806. }
  807. if(*it != ' ' && *it != '\t')
  808. break;
  809. ++it;
  810. }
  811. // chunk-ext-val
  812. if(*it != '"')
  813. {
  814. // token
  815. if(! detail::is_token_char(*it))
  816. {
  817. ec = error::bad_chunk_extension;
  818. return;
  819. }
  820. for(;;)
  821. {
  822. ++it;
  823. if(it == last)
  824. {
  825. ec = error::need_more;
  826. return;
  827. }
  828. if(! detail::is_token_char(*it))
  829. break;
  830. }
  831. }
  832. else
  833. {
  834. // quoted-string
  835. for(;;)
  836. {
  837. ++it;
  838. if(it == last)
  839. {
  840. ec = error::need_more;
  841. return;
  842. }
  843. if(*it == '"')
  844. break;
  845. if(*it == '\\')
  846. {
  847. ++it;
  848. if(it == last)
  849. {
  850. ec = error::need_more;
  851. return;
  852. }
  853. }
  854. }
  855. ++it;
  856. }
  857. goto loop;
  858. }
  859. };
  860. } // detail
  861. } // http
  862. } // beast
  863. } // boost
  864. #endif