File indexing completed on 2025-09-16 08:29:30
0001
0002
0003
0004
0005
0006
0007
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
0086
0087
0088
0089
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
0235
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
0292
0293
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
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
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
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
0421
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
0471
0472
0473 if( (f_ & flagSkipBody) ||
0474 status_ / 100 == 1 ||
0475 status_ == 204 ||
0476 status_ == 304)
0477 {
0478
0479
0480
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
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
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
0594
0595
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;
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
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
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
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
0726 if(f_ & flagChunked)
0727 return bad_content_length();
0728
0729
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
0767 if(f == field::transfer_encoding)
0768 {
0769 if(f_ & flagChunked)
0770 {
0771
0772 BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
0773 return;
0774 }
0775
0776 if(f_ & flagContentLength)
0777 {
0778
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
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 }
0816 }
0817 }
0818
0819 #endif