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