123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 |
- //
- // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- // Official repository: https://github.com/boostorg/beast
- //
- #ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
- #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
- #include <boost/beast/http/basic_parser.hpp>
- #include <boost/beast/http/error.hpp>
- #include <boost/beast/http/rfc7230.hpp>
- #include <boost/beast/core/buffer_traits.hpp>
- #include <boost/beast/core/detail/clamp.hpp>
- #include <boost/beast/core/detail/config.hpp>
- #include <boost/beast/core/detail/string.hpp>
- #include <boost/asio/buffer.hpp>
- #include <algorithm>
- #include <utility>
- namespace boost {
- namespace beast {
- namespace http {
- template<bool isRequest>
- bool
- basic_parser<isRequest>::
- keep_alive() const
- {
- BOOST_ASSERT(is_header_done());
- if(f_ & flagHTTP11)
- {
- if(f_ & flagConnectionClose)
- return false;
- }
- else
- {
- if(! (f_ & flagConnectionKeepAlive))
- return false;
- }
- return (f_ & flagNeedEOF) == 0;
- }
- template<bool isRequest>
- boost::optional<std::uint64_t>
- basic_parser<isRequest>::
- content_length() const
- {
- BOOST_ASSERT(is_header_done());
- return content_length_unchecked();
- }
- template<bool isRequest>
- boost::optional<std::uint64_t>
- basic_parser<isRequest>::
- content_length_remaining() const
- {
- BOOST_ASSERT(is_header_done());
- if(! (f_ & flagContentLength))
- return boost::none;
- return len_;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- skip(bool v)
- {
- BOOST_ASSERT(! got_some());
- if(v)
- f_ |= flagSkipBody;
- else
- f_ &= ~flagSkipBody;
- }
- template<bool isRequest>
- std::size_t
- basic_parser<isRequest>::
- put(net::const_buffer buffer,
- error_code& ec)
- {
- // If this goes off you have tried to parse more data after the parser
- // has completed. A common cause of this is re-using a parser, which is
- // not supported. If you need to re-use a parser, consider storing it
- // in an optional. Then reset() and emplace() prior to parsing each new
- // message.
- BOOST_ASSERT(!is_done());
- if (is_done())
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::stale_parser);
- return 0;
- }
- auto p = static_cast<char const*>(buffer.data());
- auto n = buffer.size();
- auto const p0 = p;
- auto const p1 = p0 + n;
- ec = {};
- loop:
- switch(state_)
- {
- case state::nothing_yet:
- if(n == 0)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- return 0;
- }
- state_ = state::start_line;
- BOOST_FALLTHROUGH;
- case state::start_line:
- {
- maybe_need_more(p, n, ec);
- if(ec)
- goto done;
- parse_start_line(p, p + (std::min<std::size_t>)(
- header_limit_, n), ec, is_request{});
- if(ec)
- {
- if(ec == error::need_more)
- {
- if(n >= header_limit_)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
- goto done;
- }
- if(p + 3 <= p1)
- skip_ = static_cast<
- std::size_t>(p1 - p - 3);
- }
- goto done;
- }
- BOOST_ASSERT(! is_done());
- n = static_cast<std::size_t>(p1 - p);
- if(p >= p1)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- goto done;
- }
- BOOST_FALLTHROUGH;
- }
- case state::fields:
- maybe_need_more(p, n, ec);
- if(ec)
- goto done;
- parse_fields(p, p + (std::min<std::size_t>)(
- header_limit_, n), ec);
- if(ec)
- {
- if(ec == error::need_more)
- {
- if(n >= header_limit_)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
- goto done;
- }
- if(p + 3 <= p1)
- skip_ = static_cast<
- std::size_t>(p1 - p - 3);
- }
- goto done;
- }
- finish_header(ec, is_request{});
- if(ec)
- goto done;
- break;
- case state::body0:
- BOOST_ASSERT(! skip_);
- this->on_body_init_impl(content_length(), ec);
- if(ec)
- goto done;
- state_ = state::body;
- BOOST_FALLTHROUGH;
- case state::body:
- BOOST_ASSERT(! skip_);
- parse_body(p, n, ec);
- if(ec)
- goto done;
- break;
- case state::body_to_eof0:
- BOOST_ASSERT(! skip_);
- this->on_body_init_impl(content_length(), ec);
- if(ec)
- goto done;
- state_ = state::body_to_eof;
- BOOST_FALLTHROUGH;
- case state::body_to_eof:
- BOOST_ASSERT(! skip_);
- parse_body_to_eof(p, n, ec);
- if(ec)
- goto done;
- break;
- case state::chunk_header0:
- this->on_body_init_impl(content_length(), ec);
- if(ec)
- goto done;
- state_ = state::chunk_header;
- BOOST_FALLTHROUGH;
- case state::chunk_header:
- parse_chunk_header(p, n, ec);
- if(ec)
- goto done;
- break;
- case state::chunk_body:
- parse_chunk_body(p, n, ec);
- if(ec)
- goto done;
- break;
- case state::complete:
- ec = {};
- goto done;
- }
- if(p < p1 && ! is_done() && eager())
- {
- n = static_cast<std::size_t>(p1 - p);
- goto loop;
- }
- done:
- return static_cast<std::size_t>(p - p0);
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- put_eof(error_code& ec)
- {
- BOOST_ASSERT(got_some());
- if( state_ == state::start_line ||
- state_ == state::fields)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
- return;
- }
- if(f_ & (flagContentLength | flagChunked))
- {
- if(state_ != state::complete)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
- return;
- }
- ec = {};
- return;
- }
- ec = {};
- this->on_finish_impl(ec);
- if(ec)
- return;
- state_ = state::complete;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- maybe_need_more(
- char const* p, std::size_t n,
- error_code& ec)
- {
- if(skip_ == 0)
- return;
- if( n > header_limit_)
- n = header_limit_;
- if(n < skip_ + 4)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- return;
- }
- auto const term =
- find_eom(p + skip_, p + n);
- if(! term)
- {
- skip_ = n - 3;
- if(skip_ + 4 > header_limit_)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
- return;
- }
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- return;
- }
- skip_ = 0;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- parse_start_line(
- char const*& in, char const* last,
- error_code& ec, std::true_type)
- {
- /*
- request-line = method SP request-target SP HTTP-version CRLF
- method = token
- */
- auto p = in;
- string_view method;
- parse_method(p, last, method, ec);
- if(ec)
- return;
- string_view target;
- parse_target(p, last, target, ec);
- if(ec)
- return;
- int version = 0;
- parse_version(p, last, version, ec);
- if(ec)
- return;
- if(version < 10 || version > 11)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
- return;
- }
- if(p + 2 > last)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- return;
- }
- if(p[0] != '\r' || p[1] != '\n')
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
- return;
- }
- p += 2;
- if(version >= 11)
- f_ |= flagHTTP11;
- this->on_request_impl(string_to_verb(method),
- method, target, version, ec);
- if(ec)
- return;
- in = p;
- state_ = state::fields;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- parse_start_line(
- char const*& in, char const* last,
- error_code& ec, std::false_type)
- {
- /*
- status-line = HTTP-version SP status-code SP reason-phrase CRLF
- status-code = 3*DIGIT
- reason-phrase = *( HTAB / SP / VCHAR / obs-text )
- */
- auto p = in;
- int version = 0;
- parse_version(p, last, version, ec);
- if(ec)
- return;
- if(version < 10 || version > 11)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
- return;
- }
- // SP
- if(p + 1 > last)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- return;
- }
- if(*p++ != ' ')
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
- return;
- }
- parse_status(p, last, status_, ec);
- if(ec)
- return;
- // parse reason CRLF
- string_view reason;
- parse_reason(p, last, reason, ec);
- if(ec)
- return;
- if(version >= 11)
- f_ |= flagHTTP11;
- this->on_response_impl(
- status_, reason, version, ec);
- if(ec)
- return;
- in = p;
- state_ = state::fields;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- parse_fields(char const*& in,
- char const* last, error_code& ec)
- {
- string_view name;
- string_view value;
- // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
- beast::detail::char_buffer<max_obs_fold> buf;
- auto p = in;
- for(;;)
- {
- if(p + 2 > last)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- return;
- }
- if(p[0] == '\r')
- {
- if(p[1] != '\n')
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_line_ending);
- }
- in = p + 2;
- return;
- }
- parse_field(p, last, name, value, buf, ec);
- if(ec)
- return;
- auto const f = string_to_field(name);
- do_field(f, value, ec);
- if(ec)
- return;
- this->on_field_impl(f, name, value, ec);
- if(ec)
- return;
- in = p;
- }
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- finish_header(error_code& ec, std::true_type)
- {
- // RFC 7230 section 3.3
- // https://tools.ietf.org/html/rfc7230#section-3.3
- if(f_ & flagSkipBody)
- {
- state_ = state::complete;
- }
- else if(f_ & flagContentLength)
- {
- if(body_limit_.has_value() &&
- len_ > body_limit_)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
- return;
- }
- if(len_ > 0)
- {
- f_ |= flagHasBody;
- state_ = state::body0;
- }
- else
- {
- state_ = state::complete;
- }
- }
- else if(f_ & flagChunked)
- {
- f_ |= flagHasBody;
- state_ = state::chunk_header0;
- }
- else
- {
- len_ = 0;
- len0_ = 0;
- state_ = state::complete;
- }
- ec = {};
- this->on_header_impl(ec);
- if(ec)
- return;
- if(state_ == state::complete)
- {
- this->on_finish_impl(ec);
- if(ec)
- return;
- }
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- finish_header(error_code& ec, std::false_type)
- {
- // RFC 7230 section 3.3
- // https://tools.ietf.org/html/rfc7230#section-3.3
- if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
- status_ / 100 == 1 || // 1xx e.g. Continue
- status_ == 204 || // No Content
- status_ == 304) // Not Modified
- {
- // VFALCO Content-Length may be present, but we
- // treat the message as not having a body.
- // https://github.com/boostorg/beast/issues/692
- state_ = state::complete;
- }
- else if(f_ & flagContentLength)
- {
- if(len_ > 0)
- {
- f_ |= flagHasBody;
- state_ = state::body0;
- if(body_limit_.has_value() &&
- len_ > body_limit_)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
- return;
- }
- }
- else
- {
- state_ = state::complete;
- }
- }
- else if(f_ & flagChunked)
- {
- f_ |= flagHasBody;
- state_ = state::chunk_header0;
- }
- else
- {
- f_ |= flagHasBody;
- f_ |= flagNeedEOF;
- state_ = state::body_to_eof0;
- }
- ec = {};
- this->on_header_impl(ec);
- if(ec)
- return;
- if(state_ == state::complete)
- {
- this->on_finish_impl(ec);
- if(ec)
- return;
- }
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- parse_body(char const*& p,
- std::size_t n, error_code& ec)
- {
- ec = {};
- n = this->on_body_impl(string_view{p,
- beast::detail::clamp(len_, n)}, ec);
- p += n;
- len_ -= n;
- if(ec)
- return;
- if(len_ > 0)
- return;
- this->on_finish_impl(ec);
- if(ec)
- return;
- state_ = state::complete;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- parse_body_to_eof(char const*& p,
- std::size_t n, error_code& ec)
- {
- if(body_limit_.has_value())
- {
- if (n > *body_limit_)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
- return;
- }
- *body_limit_ -= n;
- }
- ec = {};
- n = this->on_body_impl(string_view{p, n}, ec);
- p += n;
- if(ec)
- return;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- parse_chunk_header(char const*& p0,
- std::size_t n, error_code& ec)
- {
- /*
- chunked-body = *chunk last-chunk trailer-part CRLF
- chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
- last-chunk = 1*("0") [ chunk-ext ] CRLF
- trailer-part = *( header-field CRLF )
- chunk-size = 1*HEXDIG
- chunk-data = 1*OCTET ; a sequence of chunk-size octets
- chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
- chunk-ext-name = token
- chunk-ext-val = token / quoted-string
- */
- auto p = p0;
- auto const pend = p + n;
- char const* eol;
- if(! (f_ & flagFinalChunk))
- {
- if(n < skip_ + 2)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- return;
- }
- if(f_ & flagExpectCRLF)
- {
- // Treat the last CRLF in a chunk as
- // part of the next chunk, so p can
- // be parsed in one call instead of two.
- if(! parse_crlf(p))
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
- return;
- }
- }
- eol = find_eol(p0 + skip_, pend, ec);
- if(ec)
- return;
- if(! eol)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- skip_ = n - 1;
- return;
- }
- skip_ = static_cast<
- std::size_t>(eol - 2 - p0);
- std::uint64_t size;
- if(! parse_hex(p, size))
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
- return;
- }
- if(size != 0)
- {
- if (body_limit_.has_value())
- {
- if (size > *body_limit_)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
- return;
- }
- *body_limit_ -= size;
- }
- auto const start = p;
- parse_chunk_extensions(p, pend, ec);
- if(ec)
- return;
- if(p != eol -2 )
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
- return;
- }
- auto const ext = make_string(start, p);
- this->on_chunk_header_impl(size, ext, ec);
- if(ec)
- return;
- len_ = size;
- skip_ = 2;
- p0 = eol;
- f_ |= flagExpectCRLF;
- state_ = state::chunk_body;
- return;
- }
- f_ |= flagFinalChunk;
- }
- else
- {
- BOOST_ASSERT(n >= 3);
- if(f_ & flagExpectCRLF)
- {
- BOOST_ASSERT(n >= 5);
- BOOST_VERIFY(parse_crlf(p));
- }
- std::uint64_t size;
- BOOST_VERIFY(parse_hex(p, size));
- eol = find_eol(p, pend, ec);
- BOOST_ASSERT(! ec);
- }
- auto eom = find_eom(p0 + skip_, pend);
- if(! eom)
- {
- BOOST_ASSERT(n >= 3);
- skip_ = n - 3;
- BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
- return;
- }
- auto const start = p;
- parse_chunk_extensions(p, pend, ec);
- if(ec)
- return;
- if(p != eol - 2)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
- return;
- }
- auto const ext = make_string(start, p);
- this->on_chunk_header_impl(0, ext, ec);
- if(ec)
- return;
- p = eol;
- parse_fields(p, eom, ec);
- if(ec)
- return;
- BOOST_ASSERT(p == eom);
- p0 = eom;
- this->on_finish_impl(ec);
- if(ec)
- return;
- state_ = state::complete;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- parse_chunk_body(char const*& p,
- std::size_t n, error_code& ec)
- {
- ec = {};
- n = this->on_chunk_body_impl(
- len_, string_view{p,
- beast::detail::clamp(len_, n)}, ec);
- p += n;
- len_ -= n;
- if(len_ == 0)
- state_ = state::chunk_header;
- }
- template<bool isRequest>
- void
- basic_parser<isRequest>::
- do_field(field f,
- string_view value, error_code& ec)
- {
- using namespace beast::detail::string_literals;
- // Connection
- if(f == field::connection ||
- f == field::proxy_connection)
- {
- auto const list = opt_token_list{value};
- if(! validate_list(list))
- {
- // VFALCO Should this be a field specific error?
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
- return;
- }
- for(auto const& s : list)
- {
- if(beast::iequals("close"_sv, s))
- {
- f_ |= flagConnectionClose;
- continue;
- }
- if(beast::iequals("keep-alive"_sv, s))
- {
- f_ |= flagConnectionKeepAlive;
- continue;
- }
- if(beast::iequals("upgrade"_sv, s))
- {
- f_ |= flagConnectionUpgrade;
- continue;
- }
- }
- ec = {};
- return;
- }
- // Content-Length
- if(f == field::content_length)
- {
- auto bad_content_length = [&ec]
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_content_length);
- };
- auto multiple_content_length = [&ec]
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::multiple_content_length);
- };
- // conflicting field
- if(f_ & flagChunked)
- return bad_content_length();
- // Content-length may be a comma-separated list of integers
- auto tokens_unprocessed = 1 +
- std::count(value.begin(), value.end(), ',');
- auto tokens = opt_token_list(value);
- if (tokens.begin() == tokens.end() ||
- !validate_list(tokens))
- return bad_content_length();
- auto existing = this->content_length_unchecked();
- for (auto tok : tokens)
- {
- std::uint64_t v;
- if (!parse_dec(tok, v))
- return bad_content_length();
- --tokens_unprocessed;
- if (existing.has_value())
- {
- if (v != *existing)
- return multiple_content_length();
- }
- else
- {
- existing = v;
- }
- }
- if (tokens_unprocessed)
- return bad_content_length();
- BOOST_ASSERT(existing.has_value());
- ec = {};
- len_ = *existing;
- len0_ = *existing;
- f_ |= flagContentLength;
- return;
- }
- // Transfer-Encoding
- if(f == field::transfer_encoding)
- {
- if(f_ & flagChunked)
- {
- // duplicate
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
- return;
- }
- if(f_ & flagContentLength)
- {
- // conflicting field
- BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
- return;
- }
- ec = {};
- auto const v = token_list{value};
- auto const p = std::find_if(v.begin(), v.end(),
- [&](string_view const& s)
- {
- return beast::iequals("chunked"_sv, s);
- });
- if(p == v.end())
- return;
- if(std::next(p) != v.end())
- return;
- len_ = 0;
- f_ |= flagChunked;
- return;
- }
- // Upgrade
- if(f == field::upgrade)
- {
- ec = {};
- f_ |= flagUpgrade;
- return;
- }
- ec = {};
- }
- #ifdef BOOST_BEAST_SOURCE
- template class http::basic_parser<true>;
- template class http::basic_parser<false>;
- #endif
- } // http
- } // beast
- } // boost
- #endif
|