2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
11 #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
13 #include <boost/beast/core/static_string.hpp>
14 #include <boost/beast/core/type_traits.hpp>
15 #include <boost/beast/core/detail/clamp.hpp>
16 #include <boost/beast/core/detail/config.hpp>
17 #include <boost/beast/http/error.hpp>
18 #include <boost/beast/http/rfc7230.hpp>
19 #include <boost/asio/buffer.hpp>
20 #include <boost/make_unique.hpp>
28 template<bool isRequest, class Derived>
29 template<class OtherDerived>
30 basic_parser<isRequest, Derived>::
31 basic_parser(basic_parser<
32 isRequest, OtherDerived>&& other)
33 : body_limit_(other.body_limit_)
35 , buf_(std::move(other.buf_))
36 , buf_len_(other.buf_len_)
38 , header_limit_(other.header_limit_)
39 , status_(other.status_)
40 , state_(other.state_)
45 template<bool isRequest, class Derived>
47 basic_parser<isRequest, Derived>::
50 BOOST_ASSERT(is_header_done());
53 if(f_ & flagConnectionClose)
58 if(! (f_ & flagConnectionKeepAlive))
61 return (f_ & flagNeedEOF) == 0;
64 template<bool isRequest, class Derived>
65 boost::optional<std::uint64_t>
66 basic_parser<isRequest, Derived>::
67 content_length() const
69 BOOST_ASSERT(is_header_done());
70 if(! (f_ & flagContentLength))
75 template<bool isRequest, class Derived>
77 basic_parser<isRequest, Derived>::
80 BOOST_ASSERT(! got_some());
87 template<bool isRequest, class Derived>
88 template<class ConstBufferSequence>
90 basic_parser<isRequest, Derived>::
91 put(ConstBufferSequence const& buffers,
94 static_assert(boost::asio::is_const_buffer_sequence<
95 ConstBufferSequence>::value,
96 "ConstBufferSequence requirements not met");
97 using boost::asio::buffer_copy;
98 using boost::asio::buffer_size;
99 auto const p = boost::asio::buffer_sequence_begin(buffers);
100 auto const last = boost::asio::buffer_sequence_end(buffers);
103 ec.assign(0, ec.category());
106 if(std::next(p) == last)
109 return put(boost::asio::const_buffer(*p), ec);
111 auto const size = buffer_size(buffers);
112 if(size <= max_stack_buffer)
113 return put_from_stack(size, buffers, ec);
117 buf_ = boost::make_unique_noinit<char[]>(size);
121 buffer_copy(boost::asio::buffer(
122 buf_.get(), buf_len_), buffers);
123 return put(boost::asio::const_buffer{
124 buf_.get(), buf_len_}, ec);
127 template<bool isRequest, class Derived>
129 basic_parser<isRequest, Derived>::
130 put(boost::asio::const_buffer const& buffer,
133 BOOST_ASSERT(state_ != state::complete);
134 using boost::asio::buffer_size;
135 auto p = reinterpret_cast<
136 char const*>(buffer.data());
137 auto n = buffer.size();
139 auto const p1 = p0 + n;
140 ec.assign(0, ec.category());
144 case state::nothing_yet:
147 ec = error::need_more;
150 state_ = state::start_line;
151 BOOST_BEAST_FALLTHROUGH;
153 case state::start_line:
155 maybe_need_more(p, n, ec);
158 parse_start_line(p, p + (std::min<std::size_t>)(
159 header_limit_, n), ec, is_request{});
162 if(ec == error::need_more)
164 if(n >= header_limit_)
166 ec = error::header_limit;
171 std::size_t>(p1 - p - 3);
175 BOOST_ASSERT(! is_done());
176 n = static_cast<std::size_t>(p1 - p);
179 ec = error::need_more;
182 BOOST_BEAST_FALLTHROUGH;
186 maybe_need_more(p, n, ec);
189 parse_fields(p, p + (std::min<std::size_t>)(
190 header_limit_, n), ec);
193 if(ec == error::need_more)
195 if(n >= header_limit_)
197 ec = error::header_limit;
202 std::size_t>(p1 - p - 3);
206 finish_header(ec, is_request{});
210 BOOST_ASSERT(! skip_);
211 impl().on_body_init_impl(content_length(), ec);
214 state_ = state::body;
215 BOOST_BEAST_FALLTHROUGH;
218 BOOST_ASSERT(! skip_);
219 parse_body(p, n, ec);
224 case state::body_to_eof0:
225 BOOST_ASSERT(! skip_);
226 impl().on_body_init_impl(content_length(), ec);
229 state_ = state::body_to_eof;
230 BOOST_BEAST_FALLTHROUGH;
232 case state::body_to_eof:
233 BOOST_ASSERT(! skip_);
234 parse_body_to_eof(p, n, ec);
239 case state::chunk_header0:
240 impl().on_body_init_impl(content_length(), ec);
243 state_ = state::chunk_header;
244 BOOST_BEAST_FALLTHROUGH;
246 case state::chunk_header:
247 parse_chunk_header(p, n, ec);
252 case state::chunk_body:
253 parse_chunk_body(p, n, ec);
258 case state::complete:
259 ec.assign(0, ec.category());
262 if(p < p1 && ! is_done() && eager())
264 n = static_cast<std::size_t>(p1 - p);
268 return static_cast<std::size_t>(p - p0);
271 template<bool isRequest, class Derived>
273 basic_parser<isRequest, Derived>::
274 put_eof(error_code& ec)
276 BOOST_ASSERT(got_some());
277 if( state_ == state::start_line ||
278 state_ == state::fields)
280 ec = error::partial_message;
283 if(f_ & (flagContentLength | flagChunked))
285 if(state_ != state::complete)
287 ec = error::partial_message;
290 ec.assign(0, ec.category());
293 impl().on_finish_impl(ec);
296 state_ = state::complete;
299 template<bool isRequest, class Derived>
300 template<class ConstBufferSequence>
302 basic_parser<isRequest, Derived>::
303 put_from_stack(std::size_t size,
304 ConstBufferSequence const& buffers,
307 char buf[max_stack_buffer];
308 using boost::asio::buffer;
309 using boost::asio::buffer_copy;
310 buffer_copy(buffer(buf, sizeof(buf)), buffers);
311 return put(boost::asio::const_buffer{
315 template<bool isRequest, class Derived>
318 basic_parser<isRequest, Derived>::
320 char const* p, std::size_t n,
325 if( n > header_limit_)
329 ec = error::need_more;
333 find_eom(p + skip_, p + n);
337 if(skip_ + 4 > header_limit_)
339 ec = error::header_limit;
342 ec = error::need_more;
348 template<bool isRequest, class Derived>
351 basic_parser<isRequest, Derived>::
353 char const*& in, char const* last,
354 error_code& ec, std::true_type)
357 request-line = method SP request-target SP HTTP-version CRLF
363 parse_method(p, last, method, ec);
368 parse_target(p, last, target, ec);
373 parse_version(p, last, version, ec);
376 if(version < 10 || version > 11)
378 ec = error::bad_version;
384 ec = error::need_more;
387 if(p[0] != '\r' || p[1] != '\n')
389 ec = error::bad_version;
397 impl().on_request_impl(string_to_verb(method),
398 method, target, version, ec);
403 state_ = state::fields;
406 template<bool isRequest, class Derived>
409 basic_parser<isRequest, Derived>::
411 char const*& in, char const* last,
412 error_code& ec, std::false_type)
415 status-line = HTTP-version SP status-code SP reason-phrase CRLF
416 status-code = 3*DIGIT
417 reason-phrase = *( HTAB / SP / VCHAR / obs-text )
422 parse_version(p, last, version, ec);
425 if(version < 10 || version > 11)
427 ec = error::bad_version;
434 ec = error::need_more;
439 ec = error::bad_version;
443 parse_status(p, last, status_, ec);
449 parse_reason(p, last, reason, ec);
456 impl().on_response_impl(
457 status_, reason, version, ec);
462 state_ = state::fields;
465 template<bool isRequest, class Derived>
467 basic_parser<isRequest, Derived>::
468 parse_fields(char const*& in,
469 char const* last, error_code& ec)
473 // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
474 static_string<max_obs_fold> buf;
480 ec = error::need_more;
486 ec = error::bad_line_ending;
490 parse_field(p, last, name, value, buf, ec);
493 auto const f = string_to_field(name);
494 do_field(f, value, ec);
497 impl().on_field_impl(f, name, value, ec);
504 template<bool isRequest, class Derived>
507 basic_parser<isRequest, Derived>::
508 finish_header(error_code& ec, std::true_type)
510 // RFC 7230 section 3.3
511 // https://tools.ietf.org/html/rfc7230#section-3.3
513 if(f_ & flagSkipBody)
515 state_ = state::complete;
517 else if(f_ & flagContentLength)
519 if(len_ > body_limit_)
521 ec = error::body_limit;
527 state_ = state::body0;
531 state_ = state::complete;
534 else if(f_ & flagChunked)
537 state_ = state::chunk_header0;
542 state_ = state::complete;
545 impl().on_header_impl(ec);
548 if(state_ == state::complete)
550 impl().on_finish_impl(ec);
556 template<bool isRequest, class Derived>
559 basic_parser<isRequest, Derived>::
560 finish_header(error_code& ec, std::false_type)
562 // RFC 7230 section 3.3
563 // https://tools.ietf.org/html/rfc7230#section-3.3
565 if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
566 status_ / 100 == 1 || // 1xx e.g. Continue
567 status_ == 204 || // No Content
568 status_ == 304) // Not Modified
570 // VFALCO Content-Length may be present, but we
571 // treat the message as not having a body.
572 // https://github.com/boostorg/beast/issues/692
573 state_ = state::complete;
575 else if(f_ & flagContentLength)
577 if(len_ > body_limit_)
579 ec = error::body_limit;
585 state_ = state::body0;
589 state_ = state::complete;
592 else if(f_ & flagChunked)
595 state_ = state::chunk_header0;
601 state_ = state::body_to_eof0;
604 impl().on_header_impl(ec);
607 if(state_ == state::complete)
609 impl().on_finish_impl(ec);
615 template<bool isRequest, class Derived>
618 basic_parser<isRequest, Derived>::
619 parse_body(char const*& p,
620 std::size_t n, error_code& ec)
622 n = impl().on_body_impl(string_view{p,
623 beast::detail::clamp(len_, n)}, ec);
630 impl().on_finish_impl(ec);
633 state_ = state::complete;
636 template<bool isRequest, class Derived>
639 basic_parser<isRequest, Derived>::
640 parse_body_to_eof(char const*& p,
641 std::size_t n, error_code& ec)
645 ec = error::body_limit;
648 body_limit_ = body_limit_ - n;
649 n = impl().on_body_impl(string_view{p, n}, ec);
655 template<bool isRequest, class Derived>
657 basic_parser<isRequest, Derived>::
658 parse_chunk_header(char const*& p0,
659 std::size_t n, error_code& ec)
662 chunked-body = *chunk last-chunk trailer-part CRLF
664 chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
665 last-chunk = 1*("0") [ chunk-ext ] CRLF
666 trailer-part = *( header-field CRLF )
668 chunk-size = 1*HEXDIG
669 chunk-data = 1*OCTET ; a sequence of chunk-size octets
670 chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
671 chunk-ext-name = token
672 chunk-ext-val = token / quoted-string
676 auto const pend = p + n;
679 if(! (f_ & flagFinalChunk))
683 ec = error::need_more;
686 if(f_ & flagExpectCRLF)
688 // Treat the last CRLF in a chunk as
689 // part of the next chunk, so p can
690 // be parsed in one call instead of two.
693 ec = error::bad_chunk;
697 eol = find_eol(p0 + skip_, pend, ec);
702 ec = error::need_more;
707 std::size_t>(eol - 2 - p0);
710 if(! parse_hex(p, size))
712 ec = error::bad_chunk;
717 if(size > body_limit_)
719 ec = error::body_limit;
723 auto const start = p;
724 parse_chunk_extensions(p, pend, ec);
729 ec = error::bad_chunk_extension;
732 auto const ext = make_string(start, p);
733 impl().on_chunk_header_impl(size, ext, ec);
739 f_ |= flagExpectCRLF;
740 state_ = state::chunk_body;
744 f_ |= flagFinalChunk;
748 BOOST_ASSERT(n >= 5);
749 if(f_ & flagExpectCRLF)
750 BOOST_VERIFY(parse_crlf(p));
752 BOOST_VERIFY(parse_hex(p, size));
753 eol = find_eol(p, pend, ec);
757 auto eom = find_eom(p0 + skip_, pend);
760 BOOST_ASSERT(n >= 3);
762 ec = error::need_more;
766 auto const start = p;
767 parse_chunk_extensions(p, pend, ec);
772 ec = error::bad_chunk_extension;
775 auto const ext = make_string(start, p);
776 impl().on_chunk_header_impl(0, ext, ec);
780 parse_fields(p, eom, ec);
783 BOOST_ASSERT(p == eom);
786 impl().on_finish_impl(ec);
789 state_ = state::complete;
792 template<bool isRequest, class Derived>
795 basic_parser<isRequest, Derived>::
796 parse_chunk_body(char const*& p,
797 std::size_t n, error_code& ec)
799 n = impl().on_chunk_body_impl(
801 beast::detail::clamp(len_, n)}, ec);
805 state_ = state::chunk_header;
808 template<bool isRequest, class Derived>
810 basic_parser<isRequest, Derived>::
812 string_view value, error_code& ec)
815 if(f == field::connection ||
816 f == field::proxy_connection)
818 auto const list = opt_token_list{value};
819 if(! validate_list(list))
821 // VFALCO Should this be a field specific error?
822 ec = error::bad_value;
825 for(auto const& s : list)
827 if(iequals({"close", 5}, s))
829 f_ |= flagConnectionClose;
833 if(iequals({"keep-alive", 10}, s))
835 f_ |= flagConnectionKeepAlive;
839 if(iequals({"upgrade", 7}, s))
841 f_ |= flagConnectionUpgrade;
845 ec.assign(0, ec.category());
850 if(f == field::content_length)
852 if(f_ & flagContentLength)
855 ec = error::bad_content_length;
862 ec = error::bad_content_length;
868 value.begin(), value.end(), v))
870 ec = error::bad_content_length;
874 ec.assign(0, ec.category());
876 f_ |= flagContentLength;
881 if(f == field::transfer_encoding)
886 ec = error::bad_transfer_encoding;
890 if(f_ & flagContentLength)
893 ec = error::bad_transfer_encoding;
897 ec.assign(0, ec.category());
898 auto const v = token_list{value};
899 auto const p = std::find_if(v.begin(), v.end(),
900 [&](typename token_list::value_type const& s)
902 return iequals({"chunked", 7}, s);
906 if(std::next(p) != v.end())
914 if(f == field::upgrade)
916 ec.assign(0, ec.category());
921 ec.assign(0, ec.category());