1// 2// Copyright (c) 2016-2019 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 10#ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP 11#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP 12 13#include <boost/beast/http/basic_parser.hpp> 14#include <boost/beast/http/error.hpp> 15#include <boost/beast/http/rfc7230.hpp> 16#include <boost/beast/core/buffer_traits.hpp> 17#include <boost/beast/core/detail/clamp.hpp> 18#include <boost/beast/core/detail/config.hpp> 19#include <boost/beast/core/detail/string.hpp> 20#include <boost/asio/buffer.hpp> 21#include <algorithm> 22#include <utility> 23 24namespace boost { 25namespace beast { 26namespace http { 27 28template<bool isRequest> 29bool 30basic_parser<isRequest>:: 31keep_alive() const 32{ 33 BOOST_ASSERT(is_header_done()); 34 if(f_ & flagHTTP11) 35 { 36 if(f_ & flagConnectionClose) 37 return false; 38 } 39 else 40 { 41 if(! (f_ & flagConnectionKeepAlive)) 42 return false; 43 } 44 return (f_ & flagNeedEOF) == 0; 45} 46 47template<bool isRequest> 48boost::optional<std::uint64_t> 49basic_parser<isRequest>:: 50content_length() const 51{ 52 BOOST_ASSERT(is_header_done()); 53 return content_length_unchecked(); 54} 55 56template<bool isRequest> 57boost::optional<std::uint64_t> 58basic_parser<isRequest>:: 59content_length_remaining() const 60{ 61 BOOST_ASSERT(is_header_done()); 62 if(! (f_ & flagContentLength)) 63 return boost::none; 64 return len_; 65} 66 67template<bool isRequest> 68void 69basic_parser<isRequest>:: 70skip(bool v) 71{ 72 BOOST_ASSERT(! got_some()); 73 if(v) 74 f_ |= flagSkipBody; 75 else 76 f_ &= ~flagSkipBody; 77} 78 79template<bool isRequest> 80std::size_t 81basic_parser<isRequest>:: 82put(net::const_buffer buffer, 83 error_code& ec) 84{ 85 BOOST_ASSERT(state_ != state::complete); 86 auto p = static_cast<char const*>(buffer.data()); 87 auto n = buffer.size(); 88 auto const p0 = p; 89 auto const p1 = p0 + n; 90 ec = {}; 91loop: 92 switch(state_) 93 { 94 case state::nothing_yet: 95 if(n == 0) 96 { 97 ec = error::need_more; 98 return 0; 99 } 100 state_ = state::start_line; 101 BOOST_FALLTHROUGH; 102 103 case state::start_line: 104 { 105 maybe_need_more(p, n, ec); 106 if(ec) 107 goto done; 108 parse_start_line(p, p + (std::min<std::size_t>)( 109 header_limit_, n), ec, is_request{}); 110 if(ec) 111 { 112 if(ec == error::need_more) 113 { 114 if(n >= header_limit_) 115 { 116 ec = error::header_limit; 117 goto done; 118 } 119 if(p + 3 <= p1) 120 skip_ = static_cast< 121 std::size_t>(p1 - p - 3); 122 } 123 goto done; 124 } 125 BOOST_ASSERT(! is_done()); 126 n = static_cast<std::size_t>(p1 - p); 127 if(p >= p1) 128 { 129 ec = error::need_more; 130 goto done; 131 } 132 BOOST_FALLTHROUGH; 133 } 134 135 case state::fields: 136 maybe_need_more(p, n, ec); 137 if(ec) 138 goto done; 139 parse_fields(p, p + (std::min<std::size_t>)( 140 header_limit_, n), ec); 141 if(ec) 142 { 143 if(ec == error::need_more) 144 { 145 if(n >= header_limit_) 146 { 147 ec = error::header_limit; 148 goto done; 149 } 150 if(p + 3 <= p1) 151 skip_ = static_cast< 152 std::size_t>(p1 - p - 3); 153 } 154 goto done; 155 } 156 finish_header(ec, is_request{}); 157 break; 158 159 case state::body0: 160 BOOST_ASSERT(! skip_); 161 this->on_body_init_impl(content_length(), ec); 162 if(ec) 163 goto done; 164 state_ = state::body; 165 BOOST_FALLTHROUGH; 166 167 case state::body: 168 BOOST_ASSERT(! skip_); 169 parse_body(p, n, ec); 170 if(ec) 171 goto done; 172 break; 173 174 case state::body_to_eof0: 175 BOOST_ASSERT(! skip_); 176 this->on_body_init_impl(content_length(), ec); 177 if(ec) 178 goto done; 179 state_ = state::body_to_eof; 180 BOOST_FALLTHROUGH; 181 182 case state::body_to_eof: 183 BOOST_ASSERT(! skip_); 184 parse_body_to_eof(p, n, ec); 185 if(ec) 186 goto done; 187 break; 188 189 case state::chunk_header0: 190 this->on_body_init_impl(content_length(), ec); 191 if(ec) 192 goto done; 193 state_ = state::chunk_header; 194 BOOST_FALLTHROUGH; 195 196 case state::chunk_header: 197 parse_chunk_header(p, n, ec); 198 if(ec) 199 goto done; 200 break; 201 202 case state::chunk_body: 203 parse_chunk_body(p, n, ec); 204 if(ec) 205 goto done; 206 break; 207 208 case state::complete: 209 ec = {}; 210 goto done; 211 } 212 if(p < p1 && ! is_done() && eager()) 213 { 214 n = static_cast<std::size_t>(p1 - p); 215 goto loop; 216 } 217done: 218 return static_cast<std::size_t>(p - p0); 219} 220 221template<bool isRequest> 222void 223basic_parser<isRequest>:: 224put_eof(error_code& ec) 225{ 226 BOOST_ASSERT(got_some()); 227 if( state_ == state::start_line || 228 state_ == state::fields) 229 { 230 ec = error::partial_message; 231 return; 232 } 233 if(f_ & (flagContentLength | flagChunked)) 234 { 235 if(state_ != state::complete) 236 { 237 ec = error::partial_message; 238 return; 239 } 240 ec = {}; 241 return; 242 } 243 ec = {}; 244 this->on_finish_impl(ec); 245 if(ec) 246 return; 247 state_ = state::complete; 248} 249 250template<bool isRequest> 251void 252basic_parser<isRequest>:: 253maybe_need_more( 254 char const* p, std::size_t n, 255 error_code& ec) 256{ 257 if(skip_ == 0) 258 return; 259 if( n > header_limit_) 260 n = header_limit_; 261 if(n < skip_ + 4) 262 { 263 ec = error::need_more; 264 return; 265 } 266 auto const term = 267 find_eom(p + skip_, p + n); 268 if(! term) 269 { 270 skip_ = n - 3; 271 if(skip_ + 4 > header_limit_) 272 { 273 ec = error::header_limit; 274 return; 275 } 276 ec = error::need_more; 277 return; 278 } 279 skip_ = 0; 280} 281 282template<bool isRequest> 283void 284basic_parser<isRequest>:: 285parse_start_line( 286 char const*& in, char const* last, 287 error_code& ec, std::true_type) 288{ 289/* 290 request-line = method SP request-target SP HTTP-version CRLF 291 method = token 292*/ 293 auto p = in; 294 295 string_view method; 296 parse_method(p, last, method, ec); 297 if(ec) 298 return; 299 300 string_view target; 301 parse_target(p, last, target, ec); 302 if(ec) 303 return; 304 305 int version = 0; 306 parse_version(p, last, version, ec); 307 if(ec) 308 return; 309 if(version < 10 || version > 11) 310 { 311 ec = error::bad_version; 312 return; 313 } 314 315 if(p + 2 > last) 316 { 317 ec = error::need_more; 318 return; 319 } 320 if(p[0] != '\r' || p[1] != '\n') 321 { 322 ec = error::bad_version; 323 return; 324 } 325 p += 2; 326 327 if(version >= 11) 328 f_ |= flagHTTP11; 329 330 this->on_request_impl(string_to_verb(method), 331 method, target, version, ec); 332 if(ec) 333 return; 334 335 in = p; 336 state_ = state::fields; 337} 338 339template<bool isRequest> 340void 341basic_parser<isRequest>:: 342parse_start_line( 343 char const*& in, char const* last, 344 error_code& ec, std::false_type) 345{ 346/* 347 status-line = HTTP-version SP status-code SP reason-phrase CRLF 348 status-code = 3*DIGIT 349 reason-phrase = *( HTAB / SP / VCHAR / obs-text ) 350*/ 351 auto p = in; 352 353 int version = 0; 354 parse_version(p, last, version, ec); 355 if(ec) 356 return; 357 if(version < 10 || version > 11) 358 { 359 ec = error::bad_version; 360 return; 361 } 362 363 // SP 364 if(p + 1 > last) 365 { 366 ec = error::need_more; 367 return; 368 } 369 if(*p++ != ' ') 370 { 371 ec = error::bad_version; 372 return; 373 } 374 375 parse_status(p, last, status_, ec); 376 if(ec) 377 return; 378 379 // parse reason CRLF 380 string_view reason; 381 parse_reason(p, last, reason, ec); 382 if(ec) 383 return; 384 385 if(version >= 11) 386 f_ |= flagHTTP11; 387 388 this->on_response_impl( 389 status_, reason, version, ec); 390 if(ec) 391 return; 392 393 in = p; 394 state_ = state::fields; 395} 396 397template<bool isRequest> 398void 399basic_parser<isRequest>:: 400parse_fields(char const*& in, 401 char const* last, error_code& ec) 402{ 403 string_view name; 404 string_view value; 405 // https://stackoverflow.com/questions/686217/maximum-on-http-header-values 406 beast::detail::char_buffer<max_obs_fold> buf; 407 auto p = in; 408 for(;;) 409 { 410 if(p + 2 > last) 411 { 412 ec = error::need_more; 413 return; 414 } 415 if(p[0] == '\r') 416 { 417 if(p[1] != '\n') 418 ec = error::bad_line_ending; 419 in = p + 2; 420 return; 421 } 422 parse_field(p, last, name, value, buf, ec); 423 if(ec) 424 return; 425 auto const f = string_to_field(name); 426 do_field(f, value, ec); 427 if(ec) 428 return; 429 this->on_field_impl(f, name, value, ec); 430 if(ec) 431 return; 432 in = p; 433 } 434} 435 436template<bool isRequest> 437void 438basic_parser<isRequest>:: 439finish_header(error_code& ec, std::true_type) 440{ 441 // RFC 7230 section 3.3 442 // https://tools.ietf.org/html/rfc7230#section-3.3 443 444 if(f_ & flagSkipBody) 445 { 446 state_ = state::complete; 447 } 448 else if(f_ & flagContentLength) 449 { 450 if(len_ > body_limit_) 451 { 452 ec = error::body_limit; 453 return; 454 } 455 if(len_ > 0) 456 { 457 f_ |= flagHasBody; 458 state_ = state::body0; 459 } 460 else 461 { 462 state_ = state::complete; 463 } 464 } 465 else if(f_ & flagChunked) 466 { 467 f_ |= flagHasBody; 468 state_ = state::chunk_header0; 469 } 470 else 471 { 472 len_ = 0; 473 len0_ = 0; 474 state_ = state::complete; 475 } 476 477 ec = {}; 478 this->on_header_impl(ec); 479 if(ec) 480 return; 481 if(state_ == state::complete) 482 { 483 this->on_finish_impl(ec); 484 if(ec) 485 return; 486 } 487} 488 489template<bool isRequest> 490void 491basic_parser<isRequest>:: 492finish_header(error_code& ec, std::false_type) 493{ 494 // RFC 7230 section 3.3 495 // https://tools.ietf.org/html/rfc7230#section-3.3 496 497 if( (f_ & flagSkipBody) || // e.g. response to a HEAD request 498 status_ / 100 == 1 || // 1xx e.g. Continue 499 status_ == 204 || // No Content 500 status_ == 304) // Not Modified 501 { 502 // VFALCO Content-Length may be present, but we 503 // treat the message as not having a body. 504 // https://github.com/boostorg/beast/issues/692 505 state_ = state::complete; 506 } 507 else if(f_ & flagContentLength) 508 { 509 if(len_ > 0) 510 { 511 f_ |= flagHasBody; 512 state_ = state::body0; 513 514 if(len_ > body_limit_) 515 { 516 ec = error::body_limit; 517 return; 518 } 519 } 520 else 521 { 522 state_ = state::complete; 523 } 524 } 525 else if(f_ & flagChunked) 526 { 527 f_ |= flagHasBody; 528 state_ = state::chunk_header0; 529 } 530 else 531 { 532 f_ |= flagHasBody; 533 f_ |= flagNeedEOF; 534 state_ = state::body_to_eof0; 535 } 536 537 ec = {}; 538 this->on_header_impl(ec); 539 if(ec) 540 return; 541 if(state_ == state::complete) 542 { 543 this->on_finish_impl(ec); 544 if(ec) 545 return; 546 } 547} 548 549template<bool isRequest> 550void 551basic_parser<isRequest>:: 552parse_body(char const*& p, 553 std::size_t n, error_code& ec) 554{ 555 ec = {}; 556 n = this->on_body_impl(string_view{p, 557 beast::detail::clamp(len_, n)}, ec); 558 p += n; 559 len_ -= n; 560 if(ec) 561 return; 562 if(len_ > 0) 563 return; 564 this->on_finish_impl(ec); 565 if(ec) 566 return; 567 state_ = state::complete; 568} 569 570template<bool isRequest> 571void 572basic_parser<isRequest>:: 573parse_body_to_eof(char const*& p, 574 std::size_t n, error_code& ec) 575{ 576 if(body_limit_.has_value()) 577 { 578 if (n > *body_limit_) 579 { 580 ec = error::body_limit; 581 return; 582 } 583 *body_limit_ -= n; 584 } 585 ec = {}; 586 n = this->on_body_impl(string_view{p, n}, ec); 587 p += n; 588 if(ec) 589 return; 590} 591 592template<bool isRequest> 593void 594basic_parser<isRequest>:: 595parse_chunk_header(char const*& p0, 596 std::size_t n, error_code& ec) 597{ 598/* 599 chunked-body = *chunk last-chunk trailer-part CRLF 600 601 chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF 602 last-chunk = 1*("0") [ chunk-ext ] CRLF 603 trailer-part = *( header-field CRLF ) 604 605 chunk-size = 1*HEXDIG 606 chunk-data = 1*OCTET ; a sequence of chunk-size octets 607 chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) 608 chunk-ext-name = token 609 chunk-ext-val = token / quoted-string 610*/ 611 612 auto p = p0; 613 auto const pend = p + n; 614 char const* eol; 615 616 if(! (f_ & flagFinalChunk)) 617 { 618 if(n < skip_ + 2) 619 { 620 ec = error::need_more; 621 return; 622 } 623 if(f_ & flagExpectCRLF) 624 { 625 // Treat the last CRLF in a chunk as 626 // part of the next chunk, so p can 627 // be parsed in one call instead of two. 628 if(! parse_crlf(p)) 629 { 630 ec = error::bad_chunk; 631 return; 632 } 633 } 634 eol = find_eol(p0 + skip_, pend, ec); 635 if(ec) 636 return; 637 if(! eol) 638 { 639 ec = error::need_more; 640 skip_ = n - 1; 641 return; 642 } 643 skip_ = static_cast< 644 std::size_t>(eol - 2 - p0); 645 646 std::uint64_t size; 647 if(! parse_hex(p, size)) 648 { 649 ec = error::bad_chunk; 650 return; 651 } 652 if(size != 0) 653 { 654 if (body_limit_.has_value()) 655 { 656 if (size > *body_limit_) 657 { 658 ec = error::body_limit; 659 return; 660 } 661 *body_limit_ -= size; 662 } 663 auto const start = p; 664 parse_chunk_extensions(p, pend, ec); 665 if(ec) 666 return; 667 if(p != eol -2 ) 668 { 669 ec = error::bad_chunk_extension; 670 return; 671 } 672 auto const ext = make_string(start, p); 673 this->on_chunk_header_impl(size, ext, ec); 674 if(ec) 675 return; 676 len_ = size; 677 skip_ = 2; 678 p0 = eol; 679 f_ |= flagExpectCRLF; 680 state_ = state::chunk_body; 681 return; 682 } 683 684 f_ |= flagFinalChunk; 685 } 686 else 687 { 688 BOOST_ASSERT(n >= 5); 689 if(f_ & flagExpectCRLF) 690 BOOST_VERIFY(parse_crlf(p)); 691 std::uint64_t size; 692 BOOST_VERIFY(parse_hex(p, size)); 693 eol = find_eol(p, pend, ec); 694 BOOST_ASSERT(! ec); 695 } 696 697 auto eom = find_eom(p0 + skip_, pend); 698 if(! eom) 699 { 700 BOOST_ASSERT(n >= 3); 701 skip_ = n - 3; 702 ec = error::need_more; 703 return; 704 } 705 706 auto const start = p; 707 parse_chunk_extensions(p, pend, ec); 708 if(ec) 709 return; 710 if(p != eol - 2) 711 { 712 ec = error::bad_chunk_extension; 713 return; 714 } 715 auto const ext = make_string(start, p); 716 this->on_chunk_header_impl(0, ext, ec); 717 if(ec) 718 return; 719 p = eol; 720 parse_fields(p, eom, ec); 721 if(ec) 722 return; 723 BOOST_ASSERT(p == eom); 724 p0 = eom; 725 726 this->on_finish_impl(ec); 727 if(ec) 728 return; 729 state_ = state::complete; 730} 731 732template<bool isRequest> 733void 734basic_parser<isRequest>:: 735parse_chunk_body(char const*& p, 736 std::size_t n, error_code& ec) 737{ 738 ec = {}; 739 n = this->on_chunk_body_impl( 740 len_, string_view{p, 741 beast::detail::clamp(len_, n)}, ec); 742 p += n; 743 len_ -= n; 744 if(len_ == 0) 745 state_ = state::chunk_header; 746} 747 748template<bool isRequest> 749void 750basic_parser<isRequest>:: 751do_field(field f, 752 string_view value, error_code& ec) 753{ 754 using namespace beast::detail::string_literals; 755 // Connection 756 if(f == field::connection || 757 f == field::proxy_connection) 758 { 759 auto const list = opt_token_list{value}; 760 if(! validate_list(list)) 761 { 762 // VFALCO Should this be a field specific error? 763 ec = error::bad_value; 764 return; 765 } 766 for(auto const& s : list) 767 { 768 if(beast::iequals("close"_sv, s)) 769 { 770 f_ |= flagConnectionClose; 771 continue; 772 } 773 774 if(beast::iequals("keep-alive"_sv, s)) 775 { 776 f_ |= flagConnectionKeepAlive; 777 continue; 778 } 779 780 if(beast::iequals("upgrade"_sv, s)) 781 { 782 f_ |= flagConnectionUpgrade; 783 continue; 784 } 785 } 786 ec = {}; 787 return; 788 } 789 790 // Content-Length 791 if(f == field::content_length) 792 { 793 auto bad_content_length = [&ec] 794 { 795 ec = error::bad_content_length; 796 }; 797 798 // conflicting field 799 if(f_ & flagChunked) 800 return bad_content_length(); 801 802 // Content-length may be a comma-separated list of integers 803 auto tokens_unprocessed = 1 + 804 std::count(value.begin(), value.end(), ','); 805 auto tokens = opt_token_list(value); 806 if (tokens.begin() == tokens.end() || 807 !validate_list(tokens)) 808 return bad_content_length(); 809 810 auto existing = this->content_length_unchecked(); 811 for (auto tok : tokens) 812 { 813 std::uint64_t v; 814 if (!parse_dec(tok, v)) 815 return bad_content_length(); 816 --tokens_unprocessed; 817 if (existing.has_value()) 818 { 819 if (v != *existing) 820 return bad_content_length(); 821 } 822 else 823 { 824 existing = v; 825 } 826 } 827 828 if (tokens_unprocessed) 829 return bad_content_length(); 830 831 BOOST_ASSERT(existing.has_value()); 832 ec = {}; 833 len_ = *existing; 834 len0_ = *existing; 835 f_ |= flagContentLength; 836 return; 837 } 838 839 // Transfer-Encoding 840 if(f == field::transfer_encoding) 841 { 842 if(f_ & flagChunked) 843 { 844 // duplicate 845 ec = error::bad_transfer_encoding; 846 return; 847 } 848 849 if(f_ & flagContentLength) 850 { 851 // conflicting field 852 ec = error::bad_transfer_encoding; 853 return; 854 } 855 856 ec = {}; 857 auto const v = token_list{value}; 858 auto const p = std::find_if(v.begin(), v.end(), 859 [&](string_view const& s) 860 { 861 return beast::iequals("chunked"_sv, s); 862 }); 863 if(p == v.end()) 864 return; 865 if(std::next(p) != v.end()) 866 return; 867 len_ = 0; 868 f_ |= flagChunked; 869 return; 870 } 871 872 // Upgrade 873 if(f == field::upgrade) 874 { 875 ec = {}; 876 f_ |= flagUpgrade; 877 return; 878 } 879 880 ec = {}; 881} 882 883#ifdef BOOST_BEAST_SOURCE 884template class http::basic_parser<true>; 885template class http::basic_parser<false>; 886#endif 887 888} // http 889} // beast 890} // boost 891 892#endif 893