Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:43:59

0001 //
0002 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // Official repository: https://github.com/boostorg/beast
0008 //
0009 
0010 #ifndef BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_IPP
0011 #define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_IPP
0012 
0013 #include <boost/beast/http/detail/basic_parser.hpp>
0014 #include <limits>
0015 
0016 namespace boost {
0017 namespace beast {
0018 namespace http {
0019 namespace detail {
0020 
0021 char const*
0022 basic_parser_base::
0023 trim_front(char const* it, char const* end)
0024 {
0025     while(it != end)
0026     {
0027         if(*it != ' ' && *it != '\t')
0028             break;
0029         ++it;
0030     }
0031     return it;
0032 }
0033 
0034 char const*
0035 basic_parser_base::
0036 trim_back(
0037     char const* it, char const* first)
0038 {
0039     while(it != first)
0040     {
0041         auto const c = it[-1];
0042         if(c != ' ' && c != '\t')
0043             break;
0044         --it;
0045     }
0046     return it;
0047 }
0048 
0049 bool
0050 basic_parser_base::
0051 is_pathchar(char c)
0052 {
0053     // VFALCO This looks the same as the one below...
0054 
0055     // TEXT = <any OCTET except CTLs, and excluding LWS>
0056     static bool constexpr tab[256] = {
0057         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //   0
0058         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //  16
0059         0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //  32
0060         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //  48
0061         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //  64
0062         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //  80
0063         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //  96
0064         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
0065         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
0066         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
0067         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
0068         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
0069         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
0070         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
0071         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
0072         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1  // 240
0073     };
0074     return tab[static_cast<unsigned char>(c)];
0075 }
0076 
0077 bool
0078 basic_parser_base::
0079 unhex(unsigned char& d, char c)
0080 {
0081     static signed char constexpr tab[256] = {
0082         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //   0
0083         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //  16
0084         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //  32
0085             0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, //  48
0086         -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, //  64
0087         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //  80
0088         -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, //  96
0089         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 112
0090 
0091         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 128
0092         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 144
0093         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 160
0094         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 176
0095         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 192
0096         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 208
0097         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 224
0098         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1  // 240
0099     };
0100     d = static_cast<unsigned char>(
0101         tab[static_cast<unsigned char>(c)]);
0102     return d != static_cast<unsigned char>(-1);
0103 }
0104 
0105 //--------------------------------------------------------------------------
0106 
0107 std::pair<char const*, bool>
0108 basic_parser_base::
0109 find_fast(
0110     char const* buf,
0111     char const* buf_end,
0112     char const* ranges,
0113     size_t ranges_size)
0114 {
0115     bool found = false;
0116     boost::ignore_unused(buf_end, ranges, ranges_size);
0117     return {buf, found};
0118 }
0119 
0120 // VFALCO Can SIMD help this?
0121 char const*
0122 basic_parser_base::
0123 find_eol(
0124     char const* it, char const* last,
0125         error_code& ec)
0126 {
0127     for(;;)
0128     {
0129         if(it == last)
0130         {
0131             ec = {};
0132             return nullptr;
0133         }
0134         if(*it == '\r')
0135         {
0136             if(++it == last)
0137             {
0138                 ec = {};
0139                 return nullptr;
0140             }
0141             if(*it != '\n')
0142             {
0143                 BOOST_BEAST_ASSIGN_EC(ec, error::bad_line_ending);
0144                 return nullptr;
0145             }
0146             ec = {};
0147             return ++it;
0148         }
0149         // VFALCO Should we handle the legacy case
0150         // for lines terminated with a single '\n'?
0151         ++it;
0152     }
0153 }
0154 
0155 bool
0156 basic_parser_base::
0157 parse_dec(
0158     string_view s,
0159     std::uint64_t& v)
0160 {
0161     char const* it = s.data();
0162     char const* last = it + s.size();
0163     if(it == last)
0164         return false;
0165     std::uint64_t tmp = 0;
0166     do
0167     {
0168         if((! is_digit(*it)) ||
0169             tmp > (std::numeric_limits<std::uint64_t>::max)() / 10)
0170             return false;
0171         tmp *= 10;
0172         std::uint64_t const d = *it - '0';
0173         if((std::numeric_limits<std::uint64_t>::max)() - tmp < d)
0174             return false;
0175         tmp += d;
0176     }
0177     while(++it != last);
0178     v = tmp;
0179     return true;
0180 }
0181 
0182 bool
0183 basic_parser_base::
0184 parse_hex(char const*& it, std::uint64_t& v)
0185 {
0186     unsigned char d;
0187     if(! unhex(d, *it))
0188         return false;
0189     std::uint64_t tmp = 0;
0190     do
0191     {
0192         if(tmp > (std::numeric_limits<std::uint64_t>::max)() / 16)
0193             return false;
0194         tmp *= 16;
0195         if((std::numeric_limits<std::uint64_t>::max)() - tmp < d)
0196             return false;
0197         tmp += d;
0198     }
0199     while(unhex(d, *++it));
0200     v = tmp;
0201     return true;
0202 }
0203 
0204 //--------------------------------------------------------------------------
0205 
0206 char const*
0207 basic_parser_base::
0208 parse_token_to_eol(
0209     char const* p,
0210     char const* last,
0211     char const*& token_last,
0212     error_code& ec)
0213 {
0214     for(;; ++p)
0215     {
0216         if(p >= last)
0217         {
0218             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0219             return p;
0220         }
0221         if(BOOST_UNLIKELY(! is_print(*p)))
0222             if((BOOST_LIKELY(static_cast<
0223                     unsigned char>(*p) < '\040') &&
0224                 BOOST_LIKELY(*p != 9)) ||
0225                 BOOST_UNLIKELY(*p == 127))
0226                 goto found_control;
0227     }
0228 found_control:
0229     if(BOOST_LIKELY(*p == '\r'))
0230     {
0231         if(++p >= last)
0232         {
0233             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0234             return last;
0235         }
0236         if(*p++ != '\n')
0237         {
0238             BOOST_BEAST_ASSIGN_EC(ec, error::bad_line_ending);
0239             return last;
0240         }
0241         token_last = p - 2;
0242     }
0243 #if 0
0244     // VFALCO This allows `\n` by itself
0245     //        to terminate a line
0246     else if(*p == '\n')
0247     {
0248         token_last = p;
0249         ++p;
0250     }
0251 #endif
0252     else
0253     {
0254         // invalid character
0255         return nullptr;
0256     }
0257     return p;
0258 }
0259 
0260 bool
0261 basic_parser_base::
0262 parse_crlf(char const*& it)
0263 {
0264     if( it[0] != '\r' || it[1] != '\n')
0265         return false;
0266     it += 2;
0267     return true;
0268 }
0269 
0270 void
0271 basic_parser_base::
0272 parse_method(
0273     char const*& it, char const* last,
0274     string_view& result, error_code& ec)
0275 {
0276     // parse token SP
0277     auto const first = it;
0278     for(;; ++it)
0279     {
0280         if(it + 1 > last)
0281         {
0282             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0283             return;
0284         }
0285         if(! detail::is_token_char(*it))
0286             break;
0287     }
0288     if(it + 1 > last)
0289     {
0290         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0291         return;
0292     }
0293     if(*it != ' ')
0294     {
0295         BOOST_BEAST_ASSIGN_EC(ec, error::bad_method);
0296         return;
0297     }
0298     if(it == first)
0299     {
0300         // cannot be empty
0301         BOOST_BEAST_ASSIGN_EC(ec, error::bad_method);
0302         return;
0303     }
0304     result = make_string(first, it++);
0305 }
0306 
0307 void
0308 basic_parser_base::
0309 parse_target(
0310     char const*& it, char const* last,
0311     string_view& result, error_code& ec)
0312 {
0313     // parse target SP
0314     auto const first = it;
0315     for(;; ++it)
0316     {
0317         if(it + 1 > last)
0318         {
0319             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0320             return;
0321         }
0322         if(! is_pathchar(*it))
0323             break;
0324     }
0325     if(it + 1 > last)
0326     {
0327         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0328         return;
0329     }
0330     if(*it != ' ')
0331     {
0332         BOOST_BEAST_ASSIGN_EC(ec, error::bad_target);
0333         return;
0334     }
0335     if(it == first)
0336     {
0337         // cannot be empty
0338         BOOST_BEAST_ASSIGN_EC(ec, error::bad_target);
0339         return;
0340     }
0341     result = make_string(first, it++);
0342 }
0343 
0344 void
0345 basic_parser_base::
0346 parse_version(
0347     char const*& it, char const* last,
0348     int& result, error_code& ec)
0349 {
0350     if(it + 8 > last)
0351     {
0352         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0353         return;
0354     }
0355     if(*it++ != 'H')
0356     {
0357         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0358         return;
0359     }
0360     if(*it++ != 'T')
0361     {
0362         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0363         return;
0364     }
0365     if(*it++ != 'T')
0366     {
0367         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0368         return;
0369     }
0370     if(*it++ != 'P')
0371     {
0372         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0373         return;
0374     }
0375     if(*it++ != '/')
0376     {
0377         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0378         return;
0379     }
0380     if(! is_digit(*it))
0381     {
0382         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0383         return;
0384     }
0385     result = 10 * (*it++ - '0');
0386     if(*it++ != '.')
0387     {
0388         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0389         return;
0390     }
0391     if(! is_digit(*it))
0392     {
0393         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0394         return;
0395     }
0396     result += *it++ - '0';
0397 }
0398 
0399 void
0400 basic_parser_base::
0401 parse_status(
0402     char const*& it, char const* last,
0403     unsigned short& result, error_code& ec)
0404 {
0405     // parse 3(digit) SP
0406     if(it + 4 > last)
0407     {
0408         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0409         return;
0410     }
0411     if(! is_digit(*it))
0412     {
0413         BOOST_BEAST_ASSIGN_EC(ec, error::bad_status);
0414         return;
0415     }
0416     result = 100 * (*it++ - '0');
0417     if(! is_digit(*it))
0418     {
0419         BOOST_BEAST_ASSIGN_EC(ec, error::bad_status);
0420         return;
0421     }
0422     result += 10 * (*it++ - '0');
0423     if(! is_digit(*it))
0424     {
0425         BOOST_BEAST_ASSIGN_EC(ec, error::bad_status);
0426         return;
0427     }
0428     result += *it++ - '0';
0429     if(*it++ != ' ')
0430     {
0431         BOOST_BEAST_ASSIGN_EC(ec, error::bad_status);
0432         return;
0433     }
0434 }
0435 
0436 void
0437 basic_parser_base::
0438 parse_reason(
0439     char const*& it, char const* last,
0440     string_view& result, error_code& ec)
0441 {
0442     auto const first = it;
0443     char const* token_last = nullptr;
0444     auto p = parse_token_to_eol(
0445         it, last, token_last, ec);
0446     if(ec)
0447         return;
0448     if(! p)
0449     {
0450         BOOST_BEAST_ASSIGN_EC(ec, error::bad_reason);
0451         return;
0452     }
0453     result = make_string(first, token_last);
0454     it = p;
0455 }
0456 
0457 void
0458 basic_parser_base::
0459 parse_field(
0460     char const*& p,
0461     char const* last,
0462     string_view& name,
0463     string_view& value,
0464     beast::detail::char_buffer<max_obs_fold>& buf,
0465     error_code& ec)
0466 {
0467 /*  header-field    = field-name ":" OWS field-value OWS
0468 
0469     field-name      = token
0470     field-value     = *( field-content / obs-fold )
0471     field-content   = field-vchar [ 1*( SP / HTAB ) field-vchar ]
0472     field-vchar     = VCHAR / obs-text
0473 
0474     obs-fold        = CRLF 1*( SP / HTAB )
0475                     ; obsolete line folding
0476                     ; see Section 3.2.4
0477 
0478     token           = 1*<any CHAR except CTLs or separators>
0479     CHAR            = <any US-ASCII character (octets 0 - 127)>
0480     sep             = "(" | ")" | "<" | ">" | "@"
0481                     | "," | ";" | ":" | "\" | <">
0482                     | "/" | "[" | "]" | "?" | "="
0483                     | "{" | "}" | SP | HT
0484 */
0485     static char const* is_token =
0486         "\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"
0487         "\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"
0488         "\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"
0489         "\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"
0490         "\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"
0491         "\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"
0492         "\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"
0493         "\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";
0494 
0495     // name
0496     BOOST_ALIGNMENT(16) static const char ranges1[] =
0497         "\x00 "  /* control chars and up to SP */
0498         "\"\""   /* 0x22 */
0499         "()"     /* 0x28,0x29 */
0500         ",,"     /* 0x2c */
0501         "//"     /* 0x2f */
0502         ":@"     /* 0x3a-0x40 */
0503         "[]"     /* 0x5b-0x5d */
0504         "{\377"; /* 0x7b-0xff */
0505     auto first = p;
0506     bool found;
0507     std::tie(p, found) = find_fast(
0508         p, last, ranges1, sizeof(ranges1)-1);
0509     if(! found && p >= last)
0510     {
0511         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0512         return;
0513     }
0514     for(;;)
0515     {
0516         if(*p == ':')
0517             break;
0518         if(! is_token[static_cast<
0519             unsigned char>(*p)])
0520         {
0521             BOOST_BEAST_ASSIGN_EC(ec, error::bad_field);
0522             return;
0523         }
0524         ++p;
0525         if(p >= last)
0526         {
0527             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0528             return;
0529         }
0530     }
0531     if(p == first)
0532     {
0533         // empty name
0534         BOOST_BEAST_ASSIGN_EC(ec, error::bad_field);
0535         return;
0536     }
0537     name = make_string(first, p);
0538     ++p; // eat ':'
0539     char const* token_last = nullptr;
0540     for(;;)
0541     {
0542         // eat leading ' ' and '\t'
0543         for(;;++p)
0544         {
0545             if(p + 1 > last)
0546             {
0547                 BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0548                 return;
0549             }
0550             if(! (*p == ' ' || *p == '\t'))
0551                 break;
0552         }
0553         // parse to CRLF
0554         first = p;
0555         p = parse_token_to_eol(p, last, token_last, ec);
0556         if(ec)
0557             return;
0558         if(! p)
0559         {
0560             BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
0561             return;
0562         }
0563         // Look 1 char past the CRLF to handle obs-fold.
0564         if(p + 1 > last)
0565         {
0566             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0567             return;
0568         }
0569         token_last =
0570             trim_back(token_last, first);
0571         if(*p != ' ' && *p != '\t')
0572         {
0573             value = make_string(first, token_last);
0574             return;
0575         }
0576         ++p;
0577         if(token_last != first)
0578             break;
0579     }
0580     buf.clear();
0581     if (!buf.try_append(first, token_last))
0582     {
0583         BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
0584         return;
0585     }
0586 
0587     BOOST_ASSERT(! buf.empty());
0588     for(;;)
0589     {
0590         // eat leading ' ' and '\t'
0591         for(;;++p)
0592         {
0593             if(p + 1 > last)
0594             {
0595                 BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0596                 return;
0597             }
0598             if(! (*p == ' ' || *p == '\t'))
0599                 break;
0600         }
0601         // parse to CRLF
0602         first = p;
0603         p = parse_token_to_eol(p, last, token_last, ec);
0604         if(ec)
0605             return;
0606         if(! p)
0607         {
0608             BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
0609             return;
0610         }
0611         // Look 1 char past the CRLF to handle obs-fold.
0612         if(p + 1 > last)
0613         {
0614             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0615             return;
0616         }
0617         token_last = trim_back(token_last, first);
0618         if(first != token_last)
0619         {
0620             if (!buf.try_push_back(' ') ||
0621                 !buf.try_append(first, token_last))
0622             {
0623                 BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
0624                 return;
0625             }
0626         }
0627         if(*p != ' ' && *p != '\t')
0628         {
0629             value = {buf.data(), buf.size()};
0630             return;
0631         }
0632         ++p;
0633     }
0634 }
0635 
0636 
0637 void
0638 basic_parser_base::
0639 parse_chunk_extensions(
0640     char const*& it,
0641     char const* last,
0642     error_code& ec)
0643 {
0644 /*
0645     chunk-ext       = *( BWS  ";" BWS chunk-ext-name [ BWS  "=" BWS chunk-ext-val ] )
0646     BWS             = *( SP / HTAB ) ; "Bad White Space"
0647     chunk-ext-name  = token
0648     chunk-ext-val   = token / quoted-string
0649     token           = 1*tchar
0650     quoted-string   = DQUOTE *( qdtext / quoted-pair ) DQUOTE
0651     qdtext          = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
0652     quoted-pair     = "\" ( HTAB / SP / VCHAR / obs-text )
0653     obs-text        = %x80-FF
0654 
0655     https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
0656 */
0657 loop:
0658     if(it == last)
0659     {
0660         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0661         return;
0662     }
0663     if(*it != ' ' && *it != '\t' && *it != ';')
0664         return;
0665     // BWS
0666     if(*it == ' ' || *it == '\t')
0667     {
0668         for(;;)
0669         {
0670             ++it;
0671             if(it == last)
0672             {
0673                 BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0674                 return;
0675             }
0676             if(*it != ' ' && *it != '\t')
0677                 break;
0678         }
0679     }
0680     // ';'
0681     if(*it != ';')
0682     {
0683         BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
0684         return;
0685     }
0686 semi:
0687     ++it; // skip ';'
0688     // BWS
0689     for(;;)
0690     {
0691         if(it == last)
0692         {
0693             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0694             return;
0695         }
0696         if(*it != ' ' && *it != '\t')
0697             break;
0698         ++it;
0699     }
0700     // chunk-ext-name
0701     if(! detail::is_token_char(*it))
0702     {
0703         ec = error::bad_chunk_extension;
0704         return;
0705     }
0706     for(;;)
0707     {
0708         ++it;
0709         if(it == last)
0710         {
0711             ec = error::need_more;
0712             return;
0713         }
0714         if(! detail::is_token_char(*it))
0715             break;
0716     }
0717     // BWS [ ";" / "=" ]
0718     {
0719         bool bws;
0720         if(*it == ' ' || *it == '\t')
0721         {
0722             for(;;)
0723             {
0724                 ++it;
0725                 if(it == last)
0726                 {
0727                     ec = error::need_more;
0728                     return;
0729                 }
0730                 if(*it != ' ' && *it != '\t')
0731                     break;
0732             }
0733             bws = true;
0734         }
0735         else
0736         {
0737             bws = false;
0738         }
0739         if(*it == ';')
0740             goto semi;
0741         if(*it != '=')
0742         {
0743             if(bws)
0744                 ec = error::bad_chunk_extension;
0745             return;
0746         }
0747         ++it; // skip '='
0748     }
0749     // BWS
0750     for(;;)
0751     {
0752         if(it == last)
0753         {
0754             ec = error::need_more;
0755             return;
0756         }
0757         if(*it != ' ' && *it != '\t')
0758             break;
0759         ++it;
0760     }
0761     // chunk-ext-val
0762     if(*it != '"')
0763     {
0764         // token
0765         if(! detail::is_token_char(*it))
0766         {
0767             ec = error::bad_chunk_extension;
0768             return;
0769         }
0770         for(;;)
0771         {
0772             ++it;
0773             if(it == last)
0774             {
0775                 ec = error::need_more;
0776                 return;
0777             }
0778             if(! detail::is_token_char(*it))
0779                 break;
0780         }
0781     }
0782     else
0783     {
0784         // quoted-string
0785         for(;;)
0786         {
0787             ++it;
0788             if(it == last)
0789             {
0790                 ec = error::need_more;
0791                 return;
0792             }
0793             if(*it == '"')
0794                 break;
0795             if(*it == '\\')
0796             {
0797                 ++it;
0798                 if(it == last)
0799                 {
0800                     ec = error::need_more;
0801                     return;
0802                 }
0803             }
0804         }
0805         ++it;
0806     }
0807     goto loop;
0808 }
0809 
0810 } // detail
0811 } // http
0812 } // beast
0813 } // boost
0814 
0815 #endif