Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:29:29

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_IMPL_BASIC_PARSER_IPP
0011 #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
0012 
0013 #include <boost/beast/http/basic_parser.hpp>
0014 #include <boost/beast/http/error.hpp>
0015 #include <boost/beast/http/rfc7230.hpp>
0016 #include <boost/beast/core/buffer_traits.hpp>
0017 #include <boost/beast/core/detail/clamp.hpp>
0018 #include <boost/beast/core/detail/config.hpp>
0019 #include <boost/beast/core/detail/string.hpp>
0020 #include <boost/asio/buffer.hpp>
0021 #include <algorithm>
0022 #include <utility>
0023 
0024 namespace boost {
0025 namespace beast {
0026 namespace http {
0027 
0028 template<bool isRequest>
0029 bool
0030 basic_parser<isRequest>::
0031 keep_alive() const
0032 {
0033     BOOST_ASSERT(is_header_done());
0034     if(f_ & flagHTTP11)
0035     {
0036         if(f_ & flagConnectionClose)
0037             return false;
0038     }
0039     else
0040     {
0041         if(! (f_ & flagConnectionKeepAlive))
0042             return false;
0043     }
0044     return (f_ & flagNeedEOF) == 0;
0045 }
0046 
0047 template<bool isRequest>
0048 boost::optional<std::uint64_t>
0049 basic_parser<isRequest>::
0050 content_length() const
0051 {
0052     BOOST_ASSERT(is_header_done());
0053     return content_length_unchecked();
0054 }
0055 
0056 template<bool isRequest>
0057 boost::optional<std::uint64_t>
0058 basic_parser<isRequest>::
0059 content_length_remaining() const
0060 {
0061     BOOST_ASSERT(is_header_done());
0062     if(! (f_ & flagContentLength))
0063         return boost::none;
0064     return len_;
0065 }
0066 
0067 template<bool isRequest>
0068 void
0069 basic_parser<isRequest>::
0070 skip(bool v)
0071 {
0072     BOOST_ASSERT(! got_some());
0073     if(v)
0074         f_ |= flagSkipBody;
0075     else
0076         f_ &= ~flagSkipBody;
0077 }
0078 
0079 template<bool isRequest>
0080 std::size_t
0081 basic_parser<isRequest>::
0082 put(net::const_buffer buffer,
0083     error_code& ec)
0084 {
0085     // If this goes off you have tried to parse more data after the parser
0086     // has completed. A common cause of this is re-using a parser, which is
0087     // not supported. If you need to re-use a parser, consider storing it
0088     // in an optional. Then reset() and emplace() prior to parsing each new
0089     // message.
0090     BOOST_ASSERT(!is_done());
0091     if (is_done())
0092     {
0093         BOOST_BEAST_ASSIGN_EC(ec, error::stale_parser);
0094         return 0;
0095     }
0096     auto p = static_cast<char const*>(buffer.data());
0097     auto n = buffer.size();
0098     auto const p0 = p;
0099     auto const p1 = p0 + n;
0100     ec = {};
0101 loop:
0102     switch(state_)
0103     {
0104     case state::nothing_yet:
0105         if(n == 0)
0106         {
0107             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0108             return 0;
0109         }
0110         state_ = state::start_line;
0111         BOOST_FALLTHROUGH;
0112 
0113     case state::start_line:
0114     {
0115         maybe_need_more(p, n, ec);
0116         if(ec)
0117             goto done;
0118         parse_start_line(p, p + (std::min<std::size_t>)(
0119             header_limit_, n), ec, is_request{});
0120         if(ec)
0121         {
0122             if(ec == error::need_more)
0123             {
0124                 if(n >= header_limit_)
0125                 {
0126                     BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
0127                     goto done;
0128                 }
0129                 if(p + 3 <= p1)
0130                     skip_ = static_cast<
0131                         std::size_t>(p1 - p - 3);
0132             }
0133             goto done;
0134         }
0135         BOOST_ASSERT(! is_done());
0136         n = static_cast<std::size_t>(p1 - p);
0137         if(p >= p1)
0138         {
0139             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0140             goto done;
0141         }
0142         BOOST_FALLTHROUGH;
0143     }
0144 
0145     case state::fields:
0146         maybe_need_more(p, n, ec);
0147         if(ec)
0148             goto done;
0149         parse_fields(p, p + (std::min<std::size_t>)(
0150             header_limit_, n), ec);
0151         if(ec)
0152         {
0153             if(ec == error::need_more)
0154             {
0155                 if(n >= header_limit_)
0156                 {
0157                     BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
0158                     goto done;
0159                 }
0160                 if(p + 3 <= p1)
0161                     skip_ = static_cast<
0162                         std::size_t>(p1 - p - 3);
0163             }
0164             goto done;
0165         }
0166         finish_header(ec, is_request{});
0167         if(ec)
0168             goto done;
0169         break;
0170 
0171     case state::body0:
0172         BOOST_ASSERT(! skip_);
0173         this->on_body_init_impl(content_length(), ec);
0174         if(ec)
0175             goto done;
0176         state_ = state::body;
0177         BOOST_FALLTHROUGH;
0178 
0179     case state::body:
0180         BOOST_ASSERT(! skip_);
0181         parse_body(p, n, ec);
0182         if(ec)
0183             goto done;
0184         break;
0185 
0186     case state::body_to_eof0:
0187         BOOST_ASSERT(! skip_);
0188         this->on_body_init_impl(content_length(), ec);
0189         if(ec)
0190             goto done;
0191         state_ = state::body_to_eof;
0192         BOOST_FALLTHROUGH;
0193 
0194     case state::body_to_eof:
0195         BOOST_ASSERT(! skip_);
0196         parse_body_to_eof(p, n, ec);
0197         if(ec)
0198             goto done;
0199         break;
0200 
0201     case state::chunk_header0:
0202         this->on_body_init_impl(content_length(), ec);
0203         if(ec)
0204             goto done;
0205         state_ = state::chunk_header;
0206         BOOST_FALLTHROUGH;
0207 
0208     case state::chunk_header:
0209         parse_chunk_header(p, n, ec);
0210         if(ec)
0211             goto done;
0212         break;
0213 
0214     case state::chunk_body:
0215         parse_chunk_body(p, n, ec);
0216         if(ec)
0217             goto done;
0218         break;
0219 
0220     case state::complete:
0221         ec = {};
0222         goto done;
0223     }
0224     if(p < p1 && ! is_done() && eager())
0225     {
0226         n = static_cast<std::size_t>(p1 - p);
0227         goto loop;
0228     }
0229 done:
0230     return static_cast<std::size_t>(p - p0);
0231 }
0232 
0233 template<bool isRequest>
0234 void
0235 basic_parser<isRequest>::
0236 put_eof(error_code& ec)
0237 {
0238     BOOST_ASSERT(got_some());
0239     if( state_ == state::start_line ||
0240         state_ == state::fields)
0241     {
0242         BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
0243         return;
0244     }
0245     if(f_ & (flagContentLength | flagChunked))
0246     {
0247         if(state_ != state::complete)
0248         {
0249             BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
0250             return;
0251         }
0252         ec = {};
0253         return;
0254     }
0255     ec = {};
0256     this->on_finish_impl(ec);
0257     if(ec)
0258         return;
0259     state_ = state::complete;
0260 }
0261 
0262 template<bool isRequest>
0263 void
0264 basic_parser<isRequest>::
0265 maybe_need_more(
0266     char const* p, std::size_t n,
0267         error_code& ec)
0268 {
0269     if(skip_ == 0)
0270         return;
0271     if( n > header_limit_)
0272         n = header_limit_;
0273     if(n < skip_ + 4)
0274     {
0275         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0276         return;
0277     }
0278     auto const term =
0279         find_eom(p + skip_, p + n);
0280     if(! term)
0281     {
0282         skip_ = n - 3;
0283         if(skip_ + 4 > header_limit_)
0284         {
0285             BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
0286             return;
0287         }
0288         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0289         return;
0290     }
0291     skip_ = 0;
0292 }
0293 
0294 template<bool isRequest>
0295 void
0296 basic_parser<isRequest>::
0297 parse_start_line(
0298     char const*& in, char const* last,
0299     error_code& ec, std::true_type)
0300 {
0301 /*
0302     request-line   = method SP request-target SP HTTP-version CRLF
0303     method         = token
0304 */
0305     auto p = in;
0306 
0307     string_view method;
0308     parse_method(p, last, method, ec);
0309     if(ec)
0310         return;
0311 
0312     string_view target;
0313     parse_target(p, last, target, ec);
0314     if(ec)
0315         return;
0316 
0317     int version = 0;
0318     parse_version(p, last, version, ec);
0319     if(ec)
0320         return;
0321     if(version < 10 || version > 11)
0322     {
0323         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0324         return;
0325     }
0326 
0327     if(p + 2 > last)
0328     {
0329         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0330         return;
0331     }
0332     if(p[0] != '\r' || p[1] != '\n')
0333     {
0334         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0335         return;
0336     }
0337     p += 2;
0338 
0339     if(version >= 11)
0340         f_ |= flagHTTP11;
0341 
0342     this->on_request_impl(string_to_verb(method),
0343         method, target, version, ec);
0344     if(ec)
0345         return;
0346 
0347     in = p;
0348     state_ = state::fields;
0349 }
0350 
0351 template<bool isRequest>
0352 void
0353 basic_parser<isRequest>::
0354 parse_start_line(
0355     char const*& in, char const* last,
0356     error_code& ec, std::false_type)
0357 {
0358 /*
0359      status-line    = HTTP-version SP status-code SP reason-phrase CRLF
0360      status-code    = 3*DIGIT
0361      reason-phrase  = *( HTAB / SP / VCHAR / obs-text )
0362 */
0363     auto p = in;
0364 
0365     int version = 0;
0366     parse_version(p, last, version, ec);
0367     if(ec)
0368         return;
0369     if(version < 10 || version > 11)
0370     {
0371         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0372         return;
0373     }
0374 
0375     // SP
0376     if(p + 1 > last)
0377     {
0378         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0379         return;
0380     }
0381     if(*p++ != ' ')
0382     {
0383         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0384         return;
0385     }
0386 
0387     parse_status(p, last, status_, ec);
0388     if(ec)
0389         return;
0390 
0391     // parse reason CRLF
0392     string_view reason;
0393     parse_reason(p, last, reason, ec);
0394     if(ec)
0395         return;
0396 
0397     if(version >= 11)
0398         f_ |= flagHTTP11;
0399 
0400     this->on_response_impl(
0401         status_, reason, version, ec);
0402     if(ec)
0403         return;
0404 
0405     in = p;
0406     state_ = state::fields;
0407 }
0408 
0409 template<bool isRequest>
0410 void
0411 basic_parser<isRequest>::
0412 parse_fields(char const*& in,
0413     char const* last, error_code& ec)
0414 {
0415     string_view name;
0416     string_view value;
0417     // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
0418     beast::detail::char_buffer<max_obs_fold> buf;
0419     auto p = in;
0420     for(;;)
0421     {
0422         if(p + 2 > last)
0423         {
0424             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0425             return;
0426         }
0427         if(p[0] == '\r')
0428         {
0429             if(p[1] != '\n')
0430             {
0431                 BOOST_BEAST_ASSIGN_EC(ec, error::bad_line_ending);
0432             }
0433             in = p + 2;
0434             return;
0435         }
0436         parse_field(p, last, name, value, buf, ec);
0437         if(ec)
0438             return;
0439         auto const f = string_to_field(name);
0440         do_field(f, value, ec);
0441         if(ec)
0442             return;
0443         this->on_field_impl(f, name, value, ec);
0444         if(ec)
0445             return;
0446         in = p;
0447     }
0448 }
0449 
0450 template<bool isRequest>
0451 void
0452 basic_parser<isRequest>::
0453 finish_header(error_code& ec, std::true_type)
0454 {
0455     // RFC 7230 section 3.3
0456     // https://tools.ietf.org/html/rfc7230#section-3.3
0457 
0458     if(f_ & flagSkipBody)
0459     {
0460         state_ = state::complete;
0461     }
0462     else if(f_ & flagContentLength)
0463     {
0464         if(body_limit_.has_value() &&
0465            len_ > body_limit_)
0466         {
0467             BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
0468             return;
0469         }
0470         if(len_ > 0)
0471         {
0472             f_ |= flagHasBody;
0473             state_ = state::body0;
0474         }
0475         else
0476         {
0477             state_ = state::complete;
0478         }
0479     }
0480     else if(f_ & flagChunked)
0481     {
0482         f_ |= flagHasBody;
0483         state_ = state::chunk_header0;
0484     }
0485     else
0486     {
0487         len_ = 0;
0488         len0_ = 0;
0489         state_ = state::complete;
0490     }
0491 
0492     ec = {};
0493     this->on_header_impl(ec);
0494     if(ec)
0495         return;
0496     if(state_ == state::complete)
0497     {
0498         this->on_finish_impl(ec);
0499         if(ec)
0500             return;
0501     }
0502 }
0503 
0504 template<bool isRequest>
0505 void
0506 basic_parser<isRequest>::
0507 finish_header(error_code& ec, std::false_type)
0508 {
0509     // RFC 7230 section 3.3
0510     // https://tools.ietf.org/html/rfc7230#section-3.3
0511 
0512     if( (f_ & flagSkipBody) ||  // e.g. response to a HEAD request
0513         status_  / 100 == 1 ||   // 1xx e.g. Continue
0514         status_ == 204 ||        // No Content
0515         status_ == 304)          // Not Modified
0516     {
0517         // VFALCO Content-Length may be present, but we
0518         //        treat the message as not having a body.
0519         //        https://github.com/boostorg/beast/issues/692
0520         state_ = state::complete;
0521     }
0522     else if(f_ & flagContentLength)
0523     {
0524         if(len_ > 0)
0525         {
0526             f_ |= flagHasBody;
0527             state_ = state::body0;
0528 
0529             if(body_limit_.has_value() &&
0530                len_ > body_limit_)
0531             {
0532                 BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
0533                 return;
0534             }
0535         }
0536         else
0537         {
0538             state_ = state::complete;
0539         }
0540     }
0541     else if(f_ & flagChunked)
0542     {
0543         f_ |= flagHasBody;
0544         state_ = state::chunk_header0;
0545     }
0546     else
0547     {
0548         f_ |= flagHasBody;
0549         f_ |= flagNeedEOF;
0550         state_ = state::body_to_eof0;
0551     }
0552 
0553     ec = {};
0554     this->on_header_impl(ec);
0555     if(ec)
0556         return;
0557     if(state_ == state::complete)
0558     {
0559         this->on_finish_impl(ec);
0560         if(ec)
0561             return;
0562     }
0563 }
0564 
0565 template<bool isRequest>
0566 void
0567 basic_parser<isRequest>::
0568 parse_body(char const*& p,
0569     std::size_t n, error_code& ec)
0570 {
0571     ec = {};
0572     n = this->on_body_impl(string_view{p,
0573         beast::detail::clamp(len_, n)}, ec);
0574     p += n;
0575     len_ -= n;
0576     if(ec)
0577         return;
0578     if(len_ > 0)
0579         return;
0580     this->on_finish_impl(ec);
0581     if(ec)
0582         return;
0583     state_ = state::complete;
0584 }
0585 
0586 template<bool isRequest>
0587 void
0588 basic_parser<isRequest>::
0589 parse_body_to_eof(char const*& p,
0590     std::size_t n, error_code& ec)
0591 {
0592     if(body_limit_.has_value())
0593     {
0594         if (n > *body_limit_)
0595         {
0596             BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
0597             return;
0598         }
0599         *body_limit_ -= n;
0600     }
0601     ec = {};
0602     n = this->on_body_impl(string_view{p, n}, ec);
0603     p += n;
0604     if(ec)
0605         return;
0606 }
0607 
0608 template<bool isRequest>
0609 void
0610 basic_parser<isRequest>::
0611 parse_chunk_header(char const*& p0,
0612     std::size_t n, error_code& ec)
0613 {
0614 /*
0615     chunked-body   = *chunk last-chunk trailer-part CRLF
0616 
0617     chunk          = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
0618     last-chunk     = 1*("0") [ chunk-ext ] CRLF
0619     trailer-part   = *( header-field CRLF )
0620 
0621     chunk-size     = 1*HEXDIG
0622     chunk-data     = 1*OCTET ; a sequence of chunk-size octets
0623     chunk-ext      = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
0624     chunk-ext-name = token
0625     chunk-ext-val  = token / quoted-string
0626 */
0627 
0628     auto p = p0;
0629     auto const pend = p + n;
0630     char const* eol;
0631 
0632     if(! (f_ & flagFinalChunk))
0633     {
0634         if(n < skip_ + 2)
0635         {
0636             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0637             return;
0638         }
0639         if(f_ & flagExpectCRLF)
0640         {
0641             // Treat the last CRLF in a chunk as
0642             // part of the next chunk, so p can
0643             // be parsed in one call instead of two.
0644             if(! parse_crlf(p))
0645             {
0646                 BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
0647                 return;
0648             }
0649         }
0650         eol = find_eol(p0 + skip_, pend, ec);
0651         if(ec)
0652             return;
0653         if(! eol)
0654         {
0655             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0656             skip_ = n - 1;
0657             return;
0658         }
0659         skip_ = static_cast<
0660             std::size_t>(eol - 2 - p0);
0661 
0662         std::uint64_t size;
0663         if(! parse_hex(p, size))
0664         {
0665             BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
0666             return;
0667         }
0668         if(size != 0)
0669         {
0670             if (body_limit_.has_value())
0671             {
0672                 if (size > *body_limit_)
0673                 {
0674                     BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
0675                     return;
0676                 }
0677                 *body_limit_ -= size;
0678             }
0679             auto const start = p;
0680             parse_chunk_extensions(p, pend, ec);
0681             if(ec)
0682                 return;
0683             if(p != eol -2 )
0684             {
0685                 BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
0686                 return;
0687             }
0688             auto const ext = make_string(start, p);
0689             this->on_chunk_header_impl(size, ext, ec);
0690             if(ec)
0691                 return;
0692             len_ = size;
0693             skip_ = 2;
0694             p0 = eol;
0695             f_ |= flagExpectCRLF;
0696             state_ = state::chunk_body;
0697             return;
0698         }
0699 
0700         f_ |= flagFinalChunk;
0701     }
0702     else
0703     {
0704         BOOST_ASSERT(n >= 5);
0705         if(f_ & flagExpectCRLF)
0706             BOOST_VERIFY(parse_crlf(p));
0707         std::uint64_t size;
0708         BOOST_VERIFY(parse_hex(p, size));
0709         eol = find_eol(p, pend, ec);
0710         BOOST_ASSERT(! ec);
0711     }
0712 
0713     auto eom = find_eom(p0 + skip_, pend);
0714     if(! eom)
0715     {
0716         BOOST_ASSERT(n >= 3);
0717         skip_ = n - 3;
0718         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0719         return;
0720     }
0721 
0722     auto const start = p;
0723     parse_chunk_extensions(p, pend, ec);
0724     if(ec)
0725         return;
0726     if(p != eol - 2)
0727     {
0728         BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
0729         return;
0730     }
0731     auto const ext = make_string(start, p);
0732     this->on_chunk_header_impl(0, ext, ec);
0733     if(ec)
0734         return;
0735     p = eol;
0736     parse_fields(p, eom, ec);
0737     if(ec)
0738         return;
0739     BOOST_ASSERT(p == eom);
0740     p0 = eom;
0741 
0742     this->on_finish_impl(ec);
0743     if(ec)
0744         return;
0745     state_ = state::complete;
0746 }
0747 
0748 template<bool isRequest>
0749 void
0750 basic_parser<isRequest>::
0751 parse_chunk_body(char const*& p,
0752     std::size_t n, error_code& ec)
0753 {
0754     ec = {};
0755     n = this->on_chunk_body_impl(
0756         len_, string_view{p,
0757             beast::detail::clamp(len_, n)}, ec);
0758     p += n;
0759     len_ -= n;
0760     if(len_ == 0)
0761         state_ = state::chunk_header;
0762 }
0763 
0764 template<bool isRequest>
0765 void
0766 basic_parser<isRequest>::
0767 do_field(field f,
0768     string_view value, error_code& ec)
0769 {
0770     using namespace beast::detail::string_literals;
0771     // Connection
0772     if(f == field::connection ||
0773         f == field::proxy_connection)
0774     {
0775         auto const list = opt_token_list{value};
0776         if(! validate_list(list))
0777         {
0778             // VFALCO Should this be a field specific error?
0779             BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
0780             return;
0781         }
0782         for(auto const& s : list)
0783         {
0784             if(beast::iequals("close"_sv, s))
0785             {
0786                 f_ |= flagConnectionClose;
0787                 continue;
0788             }
0789 
0790             if(beast::iequals("keep-alive"_sv, s))
0791             {
0792                 f_ |= flagConnectionKeepAlive;
0793                 continue;
0794             }
0795 
0796             if(beast::iequals("upgrade"_sv, s))
0797             {
0798                 f_ |= flagConnectionUpgrade;
0799                 continue;
0800             }
0801         }
0802         ec = {};
0803         return;
0804     }
0805 
0806     // Content-Length
0807     if(f == field::content_length)
0808     {
0809         auto bad_content_length = [&ec]
0810         {
0811             BOOST_BEAST_ASSIGN_EC(ec, error::bad_content_length);
0812         };
0813 
0814         auto multiple_content_length = [&ec]
0815         {
0816             BOOST_BEAST_ASSIGN_EC(ec, error::multiple_content_length);
0817         };
0818 
0819         // conflicting field
0820         if(f_ & flagChunked)
0821             return bad_content_length();
0822 
0823         // Content-length may be a comma-separated list of integers
0824         auto tokens_unprocessed = 1 +
0825             std::count(value.begin(), value.end(), ',');
0826         auto tokens = opt_token_list(value);
0827         if (tokens.begin() == tokens.end() ||
0828             !validate_list(tokens))
0829                 return bad_content_length();
0830 
0831         auto existing = this->content_length_unchecked();
0832         for (auto tok : tokens)
0833         {
0834             std::uint64_t v;
0835             if (!parse_dec(tok, v))
0836                 return bad_content_length();
0837             --tokens_unprocessed;
0838             if (existing.has_value())
0839             {
0840                 if (v != *existing)
0841                     return multiple_content_length();
0842             }
0843             else
0844             {
0845                 existing = v;
0846             }
0847         }
0848 
0849         if (tokens_unprocessed)
0850             return bad_content_length();
0851 
0852         BOOST_ASSERT(existing.has_value());
0853         ec = {};
0854         len_ = *existing;
0855         len0_ = *existing;
0856         f_ |= flagContentLength;
0857         return;
0858     }
0859 
0860     // Transfer-Encoding
0861     if(f == field::transfer_encoding)
0862     {
0863         if(f_ & flagChunked)
0864         {
0865             // duplicate
0866             BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
0867             return;
0868         }
0869 
0870         if(f_ & flagContentLength)
0871         {
0872             // conflicting field
0873             BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
0874             return;
0875         }
0876 
0877         ec = {};
0878         auto const v = token_list{value};
0879         auto const p = std::find_if(v.begin(), v.end(),
0880             [&](string_view const& s)
0881             {
0882                 return beast::iequals("chunked"_sv, s);
0883             });
0884         if(p == v.end())
0885             return;
0886         if(std::next(p) != v.end())
0887             return;
0888         len_ = 0;
0889         f_ |= flagChunked;
0890         return;
0891     }
0892 
0893     // Upgrade
0894     if(f == field::upgrade)
0895     {
0896         ec = {};
0897         f_ |= flagUpgrade;
0898         return;
0899     }
0900 
0901     ec = {};
0902 }
0903 
0904 #ifdef BOOST_BEAST_SOURCE
0905 template class http::basic_parser<true>;
0906 template class http::basic_parser<false>;
0907 #endif
0908 
0909 } // http
0910 } // beast
0911 } // boost
0912 
0913 #endif