Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-16 08:29:30

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         parse_start_line(p, n, ec);
0115         if(ec)
0116             goto done;
0117         BOOST_ASSERT(! is_done());
0118         n = static_cast<std::size_t>(p1 - p);
0119         BOOST_FALLTHROUGH;
0120 
0121     case state::fields:
0122         parse_fields(p, n, ec);
0123         if(ec)
0124             goto done;
0125         finish_header(ec, is_request{});
0126         if(ec)
0127             goto done;
0128         break;
0129 
0130     case state::body0:
0131         this->on_body_init_impl(content_length(), ec);
0132         if(ec)
0133             goto done;
0134         state_ = state::body;
0135         BOOST_FALLTHROUGH;
0136 
0137     case state::body:
0138         parse_body(p, n, ec);
0139         if(ec)
0140             goto done;
0141         break;
0142 
0143     case state::body_to_eof0:
0144         this->on_body_init_impl(content_length(), ec);
0145         if(ec)
0146             goto done;
0147         state_ = state::body_to_eof;
0148         BOOST_FALLTHROUGH;
0149 
0150     case state::body_to_eof:
0151         parse_body_to_eof(p, n, ec);
0152         if(ec)
0153             goto done;
0154         break;
0155 
0156     case state::chunk_header0:
0157         this->on_body_init_impl(content_length(), ec);
0158         if(ec)
0159             goto done;
0160         state_ = state::chunk_header;
0161         BOOST_FALLTHROUGH;
0162 
0163     case state::chunk_header:
0164         parse_chunk_header(p, n, ec);
0165         if(ec)
0166             goto done;
0167         if(state_ != state::trailer_fields)
0168             break;
0169         n = static_cast<std::size_t>(p1 - p);
0170         BOOST_FALLTHROUGH;
0171 
0172     case state::trailer_fields:
0173         parse_fields(p, n, ec);
0174         if(ec)
0175             goto done;
0176         state_ = state::complete;
0177         this->on_finish_impl(ec);
0178         goto done;
0179 
0180     case state::chunk_body:
0181         parse_chunk_body(p, n, ec);
0182         if(ec)
0183             goto done;
0184         break;
0185 
0186     case state::complete:
0187         ec = {};
0188         goto done;
0189     }
0190     if(p < p1 && ! is_done() && eager())
0191     {
0192         n = static_cast<std::size_t>(p1 - p);
0193         goto loop;
0194     }
0195 done:
0196     return static_cast<std::size_t>(p - p0);
0197 }
0198 
0199 template<bool isRequest>
0200 void
0201 basic_parser<isRequest>::
0202 put_eof(error_code& ec)
0203 {
0204     BOOST_ASSERT(got_some());
0205     if( state_ == state::start_line ||
0206         state_ == state::fields)
0207     {
0208         BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
0209         return;
0210     }
0211     if(f_ & (flagContentLength | flagChunked))
0212     {
0213         if(state_ != state::complete)
0214         {
0215             BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
0216             return;
0217         }
0218         ec = {};
0219         return;
0220     }
0221     state_ = state::complete;
0222     ec = {};
0223     this->on_finish_impl(ec);
0224 }
0225 
0226 template<bool isRequest>
0227 void
0228 basic_parser<isRequest>::
0229 inner_parse_start_line(
0230     char const*& in, char const* last,
0231     error_code& ec, std::true_type)
0232 {
0233 /*
0234     request-line   = method SP request-target SP HTTP-version CRLF
0235     method         = token
0236 */
0237     auto p = in;
0238 
0239     string_view method;
0240     parse_method(p, last, method, ec);
0241     if(ec)
0242         return;
0243 
0244     string_view target;
0245     parse_target(p, last, target, ec);
0246     if(ec)
0247         return;
0248 
0249     int version = 0;
0250     parse_version(p, last, version, ec);
0251     if(ec)
0252         return;
0253     if(version < 10 || version > 11)
0254     {
0255         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0256         return;
0257     }
0258 
0259     if(p + 2 > last)
0260     {
0261         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0262         return;
0263     }
0264     if(p[0] != '\r' || p[1] != '\n')
0265     {
0266         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0267         return;
0268     }
0269     p += 2;
0270 
0271     if(version >= 11)
0272         f_ |= flagHTTP11;
0273 
0274     this->on_request_impl(string_to_verb(method),
0275         method, target, version, ec);
0276     if(ec)
0277         return;
0278 
0279     in = p;
0280     state_ = state::fields;
0281 }
0282 
0283 template<bool isRequest>
0284 void
0285 basic_parser<isRequest>::
0286 inner_parse_start_line(
0287     char const*& in, char const* last,
0288     error_code& ec, std::false_type)
0289 {
0290 /*
0291      status-line    = HTTP-version SP status-code SP reason-phrase CRLF
0292      status-code    = 3*DIGIT
0293      reason-phrase  = *( HTAB / SP / VCHAR / obs-text )
0294 */
0295     auto p = in;
0296 
0297     int version = 0;
0298     parse_version(p, last, version, ec);
0299     if(ec)
0300         return;
0301     if(version < 10 || version > 11)
0302     {
0303         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0304         return;
0305     }
0306 
0307     // SP
0308     if(p + 1 > last)
0309     {
0310         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0311         return;
0312     }
0313     if(*p++ != ' ')
0314     {
0315         BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
0316         return;
0317     }
0318 
0319     parse_status(p, last, status_, ec);
0320     if(ec)
0321         return;
0322 
0323     // parse reason CRLF
0324     string_view reason;
0325     parse_reason(p, last, reason, ec);
0326     if(ec)
0327         return;
0328 
0329     if(version >= 11)
0330         f_ |= flagHTTP11;
0331 
0332     this->on_response_impl(
0333         status_, reason, version, ec);
0334     if(ec)
0335         return;
0336 
0337     in = p;
0338     state_ = state::fields;
0339 }
0340 
0341 template<bool isRequest>
0342 void
0343 basic_parser<isRequest>::
0344 parse_start_line(
0345     char const*& in, std::size_t n, error_code& ec)
0346 {
0347     auto const p0 = in;
0348 
0349     inner_parse_start_line(in, in + (std::min<std::size_t>)
0350         (n, header_limit_), ec, is_request{});
0351     if(ec == error::need_more && n >= header_limit_)
0352     {
0353         BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
0354     }
0355     header_limit_ -= static_cast<std::uint32_t>(in - p0);
0356 }
0357 
0358 template<bool isRequest>
0359 void
0360 basic_parser<isRequest>::
0361 inner_parse_fields(char const*& in,
0362     char const* last, error_code& ec)
0363 {
0364     string_view name;
0365     string_view value;
0366     // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
0367     beast::detail::char_buffer<max_obs_fold> buf;
0368     auto p = in;
0369     for(;;)
0370     {
0371         if(p + 2 > last)
0372         {
0373             BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0374             return;
0375         }
0376         if(p[0] == '\r')
0377         {
0378             if(p[1] != '\n')
0379             {
0380                 BOOST_BEAST_ASSIGN_EC(ec, error::bad_line_ending);
0381             }
0382             in = p + 2;
0383             return;
0384         }
0385         parse_field(p, last, name, value, buf, ec);
0386         if(ec)
0387             return;
0388         auto const f = string_to_field(name);
0389         do_field(f, value, ec);
0390         if(ec)
0391             return;
0392         this->on_field_impl(f, name, value, ec);
0393         if(ec)
0394             return;
0395         in = p;
0396     }
0397 }
0398 
0399 template<bool isRequest>
0400 void
0401 basic_parser<isRequest>::
0402 parse_fields(char const*& in, std::size_t n, error_code& ec)
0403 {
0404     auto const p0 = in;
0405 
0406     inner_parse_fields(in, in + (std::min<std::size_t>)
0407         (n, header_limit_), ec);
0408     if(ec == error::need_more && n >= header_limit_)
0409     {
0410         BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
0411     }
0412     header_limit_ -= static_cast<std::uint32_t>(in - p0);
0413 }
0414 
0415 template<bool isRequest>
0416 void
0417 basic_parser<isRequest>::
0418 finish_header(error_code& ec, std::true_type)
0419 {
0420     // RFC 7230 section 3.3
0421     // https://tools.ietf.org/html/rfc7230#section-3.3
0422 
0423     if(f_ & flagSkipBody)
0424     {
0425         state_ = state::complete;
0426     }
0427     else if(f_ & flagContentLength)
0428     {
0429         if(body_limit_.has_value() &&
0430            len_ > body_limit_)
0431         {
0432             BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
0433             return;
0434         }
0435         if(len_ > 0)
0436         {
0437             f_ |= flagHasBody;
0438             state_ = state::body0;
0439         }
0440         else
0441         {
0442             state_ = state::complete;
0443         }
0444     }
0445     else if(f_ & flagChunked)
0446     {
0447         f_ |= flagHasBody;
0448         state_ = state::chunk_header0;
0449     }
0450     else
0451     {
0452         len_ = 0;
0453         len0_ = 0;
0454         state_ = state::complete;
0455     }
0456 
0457     ec = {};
0458     this->on_header_impl(ec);
0459     if(ec)
0460         return;
0461     if(state_ == state::complete)
0462         this->on_finish_impl(ec);
0463 }
0464 
0465 template<bool isRequest>
0466 void
0467 basic_parser<isRequest>::
0468 finish_header(error_code& ec, std::false_type)
0469 {
0470     // RFC 7230 section 3.3
0471     // https://tools.ietf.org/html/rfc7230#section-3.3
0472 
0473     if( (f_ & flagSkipBody) ||  // e.g. response to a HEAD request
0474         status_  / 100 == 1 ||   // 1xx e.g. Continue
0475         status_ == 204 ||        // No Content
0476         status_ == 304)          // Not Modified
0477     {
0478         // VFALCO Content-Length may be present, but we
0479         //        treat the message as not having a body.
0480         //        https://github.com/boostorg/beast/issues/692
0481         state_ = state::complete;
0482     }
0483     else if(f_ & flagContentLength)
0484     {
0485         if(len_ > 0)
0486         {
0487             f_ |= flagHasBody;
0488             state_ = state::body0;
0489 
0490             if(body_limit_.has_value() &&
0491                len_ > body_limit_)
0492             {
0493                 BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
0494                 return;
0495             }
0496         }
0497         else
0498         {
0499             state_ = state::complete;
0500         }
0501     }
0502     else if(f_ & flagChunked)
0503     {
0504         f_ |= flagHasBody;
0505         state_ = state::chunk_header0;
0506     }
0507     else
0508     {
0509         f_ |= flagHasBody;
0510         f_ |= flagNeedEOF;
0511         state_ = state::body_to_eof0;
0512     }
0513 
0514     ec = {};
0515     this->on_header_impl(ec);
0516     if(ec)
0517         return;
0518     if(state_ == state::complete)
0519         this->on_finish_impl(ec);
0520 }
0521 
0522 template<bool isRequest>
0523 void
0524 basic_parser<isRequest>::
0525 parse_body(char const*& p,
0526     std::size_t n, error_code& ec)
0527 {
0528     ec = {};
0529     n = this->on_body_impl(string_view{p,
0530         beast::detail::clamp(len_, n)}, ec);
0531     p += n;
0532     len_ -= n;
0533     if(ec)
0534         return;
0535     if(len_ > 0)
0536         return;
0537     state_ = state::complete;
0538     this->on_finish_impl(ec);
0539 }
0540 
0541 template<bool isRequest>
0542 void
0543 basic_parser<isRequest>::
0544 parse_body_to_eof(char const*& p,
0545     std::size_t n, error_code& ec)
0546 {
0547     if(body_limit_.has_value())
0548     {
0549         if (n > *body_limit_)
0550         {
0551             BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
0552             return;
0553         }
0554         *body_limit_ -= n;
0555     }
0556     ec = {};
0557     n = this->on_body_impl(string_view{p, n}, ec);
0558     p += n;
0559     if(ec)
0560         return;
0561 }
0562 
0563 template<bool isRequest>
0564 void
0565 basic_parser<isRequest>::
0566 parse_chunk_header(char const*& in,
0567     std::size_t n, error_code& ec)
0568 {
0569 /*
0570     chunked-body   = *chunk last-chunk trailer-part CRLF
0571 
0572     chunk          = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
0573     last-chunk     = 1*("0") [ chunk-ext ] CRLF
0574     trailer-part   = *( header-field CRLF )
0575 
0576     chunk-size     = 1*HEXDIG
0577     chunk-data     = 1*OCTET ; a sequence of chunk-size octets
0578     chunk-ext      = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
0579     chunk-ext-name = token
0580     chunk-ext-val  = token / quoted-string
0581 */
0582 
0583     auto p = in;
0584     auto const pend = p + n;
0585 
0586     if(n < 2)
0587     {
0588         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0589         return;
0590     }
0591     if(f_ & flagExpectCRLF)
0592     {
0593         // Treat the last CRLF in a chunk as
0594         // part of the next chunk, so p can
0595         // be parsed in one call instead of two.
0596         if(! parse_crlf(p))
0597         {
0598             BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
0599             return;
0600         }
0601     }
0602 
0603     auto const eol = find_eol(p, pend, ec);
0604     if(ec)
0605         return;
0606     if(! eol)
0607     {
0608         BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
0609         return;
0610     }
0611 
0612     std::uint64_t size;
0613     if(! parse_hex(p, size))
0614     {
0615         BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
0616         return;
0617     }
0618     if (body_limit_.has_value())
0619     {
0620         if (size > *body_limit_)
0621         {
0622             BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
0623             return;
0624         }
0625         *body_limit_ -= size;
0626     }
0627 
0628     auto const start = p;
0629     parse_chunk_extensions(p, pend, ec);
0630     if(ec)
0631         return;
0632     if(p != eol - 2)
0633     {
0634         BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
0635         return;
0636     }
0637     auto const ext = make_string(start, p);
0638     this->on_chunk_header_impl(size, ext, ec);
0639     if(ec)
0640         return;
0641 
0642     len_ = size;
0643     in = eol;
0644     f_ |= flagExpectCRLF;
0645     if(size != 0)
0646     {
0647         state_ = state::chunk_body;
0648         return;
0649     }
0650     state_ = state::trailer_fields;
0651     header_limit_ += 2; // for the final chunk's CRLF
0652 }
0653 
0654 template<bool isRequest>
0655 void
0656 basic_parser<isRequest>::
0657 parse_chunk_body(char const*& p,
0658     std::size_t n, error_code& ec)
0659 {
0660     ec = {};
0661     n = this->on_chunk_body_impl(
0662         len_, string_view{p,
0663             beast::detail::clamp(len_, n)}, ec);
0664     p += n;
0665     len_ -= n;
0666     if(len_ == 0)
0667         state_ = state::chunk_header;
0668 }
0669 
0670 template<bool isRequest>
0671 void
0672 basic_parser<isRequest>::
0673 do_field(field f,
0674     string_view value, error_code& ec)
0675 {
0676     using namespace beast::detail::string_literals;
0677     // Connection
0678     if(f == field::connection ||
0679         f == field::proxy_connection)
0680     {
0681         auto const list = opt_token_list{value};
0682         if(! validate_list(list))
0683         {
0684             // VFALCO Should this be a field specific error?
0685             BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
0686             return;
0687         }
0688         for(auto const& s : list)
0689         {
0690             if(beast::iequals("close"_sv, s))
0691             {
0692                 f_ |= flagConnectionClose;
0693                 continue;
0694             }
0695 
0696             if(beast::iequals("keep-alive"_sv, s))
0697             {
0698                 f_ |= flagConnectionKeepAlive;
0699                 continue;
0700             }
0701 
0702             if(beast::iequals("upgrade"_sv, s))
0703             {
0704                 f_ |= flagConnectionUpgrade;
0705                 continue;
0706             }
0707         }
0708         ec = {};
0709         return;
0710     }
0711 
0712     // Content-Length
0713     if(f == field::content_length)
0714     {
0715         auto bad_content_length = [&ec]
0716         {
0717             BOOST_BEAST_ASSIGN_EC(ec, error::bad_content_length);
0718         };
0719 
0720         auto multiple_content_length = [&ec]
0721         {
0722             BOOST_BEAST_ASSIGN_EC(ec, error::multiple_content_length);
0723         };
0724 
0725         // conflicting field
0726         if(f_ & flagChunked)
0727             return bad_content_length();
0728 
0729         // Content-length may be a comma-separated list of integers
0730         auto tokens_unprocessed = 1 +
0731             std::count(value.begin(), value.end(), ',');
0732         auto tokens = opt_token_list(value);
0733         if (tokens.begin() == tokens.end() ||
0734             !validate_list(tokens))
0735                 return bad_content_length();
0736 
0737         auto existing = this->content_length_unchecked();
0738         for (auto tok : tokens)
0739         {
0740             std::uint64_t v;
0741             if (!parse_dec(tok, v))
0742                 return bad_content_length();
0743             --tokens_unprocessed;
0744             if (existing.has_value())
0745             {
0746                 if (v != *existing)
0747                     return multiple_content_length();
0748             }
0749             else
0750             {
0751                 existing = v;
0752             }
0753         }
0754 
0755         if (tokens_unprocessed)
0756             return bad_content_length();
0757 
0758         BOOST_ASSERT(existing.has_value());
0759         ec = {};
0760         len_ = *existing;
0761         len0_ = *existing;
0762         f_ |= flagContentLength;
0763         return;
0764     }
0765 
0766     // Transfer-Encoding
0767     if(f == field::transfer_encoding)
0768     {
0769         if(f_ & flagChunked)
0770         {
0771             // duplicate
0772             BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
0773             return;
0774         }
0775 
0776         if(f_ & flagContentLength)
0777         {
0778             // conflicting field
0779             BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
0780             return;
0781         }
0782 
0783         ec = {};
0784         auto const v = token_list{value};
0785         auto const p = std::find_if(v.begin(), v.end(),
0786             [&](string_view const& s)
0787             {
0788                 return beast::iequals("chunked"_sv, s);
0789             });
0790         if(p == v.end())
0791             return;
0792         if(std::next(p) != v.end())
0793             return;
0794         len_ = 0;
0795         f_ |= flagChunked;
0796         return;
0797     }
0798 
0799     // Upgrade
0800     if(f == field::upgrade)
0801     {
0802         ec = {};
0803         f_ |= flagUpgrade;
0804         return;
0805     }
0806 
0807     ec = {};
0808 }
0809 
0810 #ifdef BOOST_BEAST_SOURCE
0811 template class http::basic_parser<true>;
0812 template class http::basic_parser<false>;
0813 #endif
0814 
0815 } // http
0816 } // beast
0817 } // boost
0818 
0819 #endif