rfc7230.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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_RFC7230_HPP
  10. #define BOOST_BEAST_HTTP_DETAIL_RFC7230_HPP
  11. #include <boost/beast/core/string.hpp>
  12. #include <iterator>
  13. #include <utility>
  14. namespace boost {
  15. namespace beast {
  16. namespace http {
  17. namespace detail {
  18. inline
  19. bool
  20. is_digit(char c)
  21. {
  22. return c >= '0' && c <= '9';
  23. }
  24. inline
  25. char
  26. is_alpha(char c)
  27. {
  28. static char constexpr tab[] = {
  29. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
  30. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
  31. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32
  32. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48
  33. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
  34. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80
  35. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
  36. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112
  37. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
  38. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
  39. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
  40. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
  41. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
  42. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
  43. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
  44. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
  45. };
  46. BOOST_STATIC_ASSERT(sizeof(tab) == 256);
  47. return tab[static_cast<unsigned char>(c)];
  48. }
  49. inline
  50. char
  51. is_text(char c)
  52. {
  53. // TEXT = <any OCTET except CTLs, but including LWS>
  54. static char constexpr tab[] = {
  55. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
  56. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
  57. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
  58. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
  59. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
  60. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
  61. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
  62. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
  63. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
  64. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
  65. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
  66. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
  67. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
  68. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
  69. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
  70. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
  71. };
  72. BOOST_STATIC_ASSERT(sizeof(tab) == 256);
  73. return tab[static_cast<unsigned char>(c)];
  74. }
  75. inline
  76. char
  77. is_token_char(char c)
  78. {
  79. /*
  80. tchar = "!" | "#" | "$" | "%" | "&" |
  81. "'" | "*" | "+" | "-" | "." |
  82. "^" | "_" | "`" | "|" | "~" |
  83. DIGIT | ALPHA
  84. */
  85. static char constexpr tab[] = {
  86. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
  87. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
  88. 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
  89. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
  90. 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
  91. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
  92. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
  93. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
  94. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
  95. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
  96. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
  97. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
  98. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
  99. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
  100. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
  101. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
  102. };
  103. BOOST_STATIC_ASSERT(sizeof(tab) == 256);
  104. return tab[static_cast<unsigned char>(c)];
  105. }
  106. inline
  107. char
  108. is_qdchar(char c)
  109. {
  110. /*
  111. qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
  112. */
  113. static char constexpr tab[] = {
  114. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
  115. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
  116. 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
  117. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
  118. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
  119. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 80
  120. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
  121. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
  122. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
  123. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
  124. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
  125. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
  126. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
  127. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
  128. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
  129. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
  130. };
  131. BOOST_STATIC_ASSERT(sizeof(tab) == 256);
  132. return tab[static_cast<unsigned char>(c)];
  133. }
  134. inline
  135. char
  136. is_qpchar(char c)
  137. {
  138. /*
  139. quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
  140. obs-text = %x80-FF
  141. */
  142. static char constexpr tab[] = {
  143. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
  144. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
  145. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
  146. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
  147. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
  148. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
  149. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
  150. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
  151. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
  152. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
  153. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
  154. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
  155. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
  156. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
  157. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
  158. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
  159. };
  160. BOOST_STATIC_ASSERT(sizeof(tab) == 256);
  161. return tab[static_cast<unsigned char>(c)];
  162. }
  163. // converts to lower case,
  164. // returns 0 if not a valid text char
  165. //
  166. inline
  167. char
  168. to_value_char(char c)
  169. {
  170. // TEXT = <any OCTET except CTLs, but including LWS>
  171. static unsigned char constexpr tab[] = {
  172. 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
  173. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
  174. 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
  175. 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
  176. 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
  177. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
  178. 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
  179. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
  180. 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
  181. 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
  182. 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
  183. 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
  184. 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
  185. 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
  186. 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
  187. 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
  188. };
  189. BOOST_STATIC_ASSERT(sizeof(tab) == 256);
  190. return static_cast<char>(tab[static_cast<unsigned char>(c)]);
  191. }
  192. // VFALCO TODO Make this return unsigned?
  193. inline
  194. std::int8_t
  195. unhex(char c)
  196. {
  197. static signed char constexpr tab[] = {
  198. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
  199. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
  200. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
  201. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
  202. -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
  203. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
  204. -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
  205. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112
  206. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128
  207. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144
  208. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160
  209. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176
  210. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192
  211. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208
  212. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224
  213. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240
  214. };
  215. BOOST_STATIC_ASSERT(sizeof(tab) == 256);
  216. return tab[static_cast<unsigned char>(c)];
  217. }
  218. template<class FwdIt>
  219. inline
  220. void
  221. skip_ows(FwdIt& it, FwdIt const& end)
  222. {
  223. while(it != end)
  224. {
  225. if(*it != ' ' && *it != '\t')
  226. break;
  227. ++it;
  228. }
  229. }
  230. template<class RanIt>
  231. inline
  232. void
  233. skip_ows_rev(
  234. RanIt& it, RanIt const& first)
  235. {
  236. while(it != first)
  237. {
  238. auto const c = it[-1];
  239. if(c != ' ' && c != '\t')
  240. break;
  241. --it;
  242. }
  243. }
  244. // obs-fold = CRLF 1*( SP / HTAB )
  245. // return `false` on parse error
  246. //
  247. template<class FwdIt>
  248. inline
  249. bool
  250. skip_obs_fold(
  251. FwdIt& it, FwdIt const& last)
  252. {
  253. for(;;)
  254. {
  255. if(*it != '\r')
  256. return true;
  257. if(++it == last)
  258. return false;
  259. if(*it != '\n')
  260. return false;
  261. if(++it == last)
  262. return false;
  263. if(*it != ' ' && *it != '\t')
  264. return false;
  265. for(;;)
  266. {
  267. if(++it == last)
  268. return true;
  269. if(*it != ' ' && *it != '\t')
  270. return true;
  271. }
  272. }
  273. }
  274. template<class FwdIt>
  275. void
  276. skip_token(FwdIt& it, FwdIt const& last)
  277. {
  278. while(it != last && is_token_char(*it))
  279. ++it;
  280. }
  281. inline
  282. string_view
  283. trim(string_view s)
  284. {
  285. auto first = s.begin();
  286. auto last = s.end();
  287. skip_ows(first, last);
  288. while(first != last)
  289. {
  290. auto const c = *std::prev(last);
  291. if(c != ' ' && c != '\t')
  292. break;
  293. --last;
  294. }
  295. if(first == last)
  296. return {};
  297. return {&*first,
  298. static_cast<std::size_t>(last - first)};
  299. }
  300. struct param_iter
  301. {
  302. using iter_type = string_view::const_iterator;
  303. iter_type it;
  304. iter_type first;
  305. iter_type last;
  306. std::pair<string_view, string_view> v;
  307. bool
  308. empty() const
  309. {
  310. return first == it;
  311. }
  312. template<class = void>
  313. void
  314. increment();
  315. };
  316. template<class>
  317. void
  318. param_iter::
  319. increment()
  320. {
  321. /*
  322. param-list = *( OWS ";" OWS param )
  323. param = token OWS [ "=" OWS ( token / quoted-string ) ]
  324. quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
  325. qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
  326. quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
  327. obs-text = %x80-FF
  328. */
  329. auto const err =
  330. [&]
  331. {
  332. it = first;
  333. };
  334. v.first = {};
  335. v.second = {};
  336. detail::skip_ows(it, last);
  337. first = it;
  338. if(it == last)
  339. return err();
  340. if(*it != ';')
  341. return err();
  342. ++it;
  343. detail::skip_ows(it, last);
  344. if(it == last)
  345. return err();
  346. // param
  347. if(! detail::is_token_char(*it))
  348. return err();
  349. auto const p0 = it;
  350. skip_token(++it, last);
  351. auto const p1 = it;
  352. v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
  353. detail::skip_ows(it, last);
  354. if(it == last)
  355. return;
  356. if(*it == ';')
  357. return;
  358. if(*it != '=')
  359. return err();
  360. ++it;
  361. detail::skip_ows(it, last);
  362. if(it == last)
  363. return;
  364. if(*it == '"')
  365. {
  366. // quoted-string
  367. auto const p2 = it;
  368. ++it;
  369. for(;;)
  370. {
  371. if(it == last)
  372. return err();
  373. auto c = *it++;
  374. if(c == '"')
  375. break;
  376. if(detail::is_qdchar(c))
  377. continue;
  378. if(c != '\\')
  379. return err();
  380. if(it == last)
  381. return err();
  382. c = *it++;
  383. if(! detail::is_qpchar(c))
  384. return err();
  385. }
  386. v.second = { &*p2, static_cast<std::size_t>(it - p2) };
  387. }
  388. else
  389. {
  390. // token
  391. if(! detail::is_token_char(*it))
  392. return err();
  393. auto const p2 = it;
  394. skip_token(++it, last);
  395. v.second = { &*p2, static_cast<std::size_t>(it - p2) };
  396. }
  397. }
  398. /*
  399. #token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
  400. */
  401. struct opt_token_list_policy
  402. {
  403. using value_type = string_view;
  404. bool
  405. operator()(value_type& v,
  406. char const*& it, string_view s) const
  407. {
  408. v = {};
  409. auto need_comma = it != s.begin();
  410. for(;;)
  411. {
  412. detail::skip_ows(it, s.end());
  413. if(it == s.end())
  414. {
  415. it = nullptr;
  416. return true;
  417. }
  418. auto const c = *it;
  419. if(detail::is_token_char(c))
  420. {
  421. if(need_comma)
  422. return false;
  423. auto const p0 = it;
  424. for(;;)
  425. {
  426. ++it;
  427. if(it == s.end())
  428. break;
  429. if(! detail::is_token_char(*it))
  430. break;
  431. }
  432. v = string_view{&*p0,
  433. static_cast<std::size_t>(it - p0)};
  434. return true;
  435. }
  436. if(c != ',')
  437. return false;
  438. need_comma = false;
  439. ++it;
  440. }
  441. }
  442. };
  443. } // detail
  444. } // http
  445. } // beast
  446. } // boost
  447. #endif