diff --git a/CHANGELOG.md b/CHANGELOG.md index c00e32d9..32db9d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Version 219: * Visual Studio 2017 minimum requirement for Windows * Better treatment of SSL short reads * ssl_stream is a public interface +* basic_parser is abstract, not CRTP (API Change) -------------------------------------------------------------------------------- diff --git a/Jamfile b/Jamfile index ed4d16b1..e1ab6e68 100644 --- a/Jamfile +++ b/Jamfile @@ -51,29 +51,9 @@ variant beast_ubasan path-constant TEST_MAIN : include/boost/beast/_experimental/unit_test/main.cpp ; -lib static_asio - : test/lib_asio.cpp - : requirements - [ requires - cxx11_constexpr - cxx11_decltype - cxx11_hdr_tuple - cxx11_template_aliases - cxx11_variadic_templates - ] - BOOST_ASIO_SEPARATE_COMPILATION - BOOST_ASIO_NO_DEPRECATED=1 - BOOST_ASIO_DISABLE_BOOST_ARRAY=1 - BOOST_ASIO_DISABLE_BOOST_BIND=1 - BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 - BOOST_ASIO_DISABLE_BOOST_REGEX=1 - BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 - windows:_WIN32_WINNT=0x0601 - static - ; - lib static_beast : test/lib_beast.cpp + test/lib_asio.cpp : requirements [ requires cxx11_constexpr @@ -83,12 +63,14 @@ lib static_beast cxx11_variadic_templates ] BOOST_BEAST_SPLIT_COMPILATION + BOOST_ASIO_SEPARATE_COMPILATION BOOST_ASIO_NO_DEPRECATED=1 BOOST_ASIO_DISABLE_BOOST_ARRAY=1 BOOST_ASIO_DISABLE_BOOST_BIND=1 BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 BOOST_ASIO_DISABLE_BOOST_REGEX=1 BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 + msvc-14.1:"/permissive-" msvc:_SCL_SECURE_NO_WARNINGS=1 msvc:_CRT_SECURE_NO_WARNINGS=1 msvc:_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING @@ -103,7 +85,7 @@ project /boost/beast ./test/extras/include /boost/coroutine//boost_coroutine /boost/filesystem//boost_filesystem - static_asio + #static_asio static_beast /boost//headers multi @@ -112,16 +94,16 @@ project /boost/beast [ ac.check-library /boost/beast//ssl : /boost/beast//ssl : no ] /boost/beast//crypto BOOST_ALL_NO_LIB=1 + BOOST_BEAST_SPLIT_COMPILATION + BOOST_ASIO_SEPARATE_COMPILATION BOOST_ASIO_NO_DEPRECATED=1 BOOST_ASIO_DISABLE_BOOST_ARRAY=1 BOOST_ASIO_DISABLE_BOOST_BIND=1 BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 BOOST_ASIO_DISABLE_BOOST_REGEX=1 BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 - #BOOST_ASIO_SEPARATE_COMPILATION - BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1 - BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1 - #BOOST_BEAST_SEPARATE_COMPILATION +BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1 +BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1 msvc:"/bigobj" msvc-14.1:"/permissive-" msvc:_SCL_SECURE_NO_WARNINGS=1 diff --git a/example/doc/http_examples.hpp b/example/doc/http_examples.hpp index 285b9e56..062832fc 100644 --- a/example/doc/http_examples.hpp +++ b/example/doc/http_examples.hpp @@ -151,11 +151,7 @@ receive_expect_100_continue( // Read the rest of the message. // - // We use parser.base() to return a basic_parser&, to avoid an - // ambiguous function error (from boost::asio::read). Another - // solution is to qualify the call, e.g. `beast::http::read` - // - read(stream, buffer, parser.base(), ec); + read(stream, buffer, parser, ec); } //] @@ -875,14 +871,9 @@ do_form_request( //[example_http_custom_parser template -class custom_parser - : public basic_parser> +class custom_parser : public basic_parser { private: - // The friend declaration is needed, - // otherwise the callbacks must be made public. - friend class basic_parser; - /// Called after receiving the request-line (isRequest == true). void on_request_impl( @@ -890,7 +881,7 @@ private: string_view method_str, // The method as a string string_view target, // The request-target int version, // The HTTP-version - error_code& ec); // The error returned to the caller, if any + error_code& ec) override; // The error returned to the caller, if any /// Called after receiving the start-line (isRequest == false). void @@ -898,7 +889,7 @@ private: int code, // The status-code string_view reason, // The obsolete reason-phrase int version, // The HTTP-version - error_code& ec); // The error returned to the caller, if any + error_code& ec) override; // The error returned to the caller, if any /// Called after receiving a header field. void @@ -906,12 +897,12 @@ private: field f, // The known-field enumeration constant string_view name, // The field name string. string_view value, // The field value - error_code& ec); // The error returned to the caller, if any + error_code& ec) override; // The error returned to the caller, if any /// Called after the complete header is received. void on_header_impl( - error_code& ec); // The error returned to the caller, if any + error_code& ec) override; // The error returned to the caller, if any /// Called just before processing the body, if a body exists. void @@ -919,7 +910,7 @@ private: boost::optional< std::uint64_t> const& content_length, // Content length if known, else `boost::none` - error_code& ec); // The error returned to the caller, if any + error_code& ec) override; // The error returned to the caller, if any /// Called for each piece of the body, if a body exists. //! @@ -932,7 +923,7 @@ private: std::size_t on_body_impl( string_view s, // A portion of the body - error_code& ec); // The error returned to the caller, if any + error_code& ec) override; // The error returned to the caller, if any /// Called for each chunk header. void @@ -940,7 +931,7 @@ private: std::uint64_t size, // The size of the upcoming chunk, // or zero for the last chunk string_view extension, // The chunk extensions (may be empty) - error_code& ec); // The error returned to the caller, if any + error_code& ec) override; // The error returned to the caller, if any /// Called to deliver the chunk body. //! @@ -958,11 +949,12 @@ private: // including what is being passed here. // or zero for the last chunk string_view body, // The next piece of the chunk body - error_code& ec); // The error returned to the caller, if any + error_code& ec) override; // The error returned to the caller, if any /// Called when the complete message is parsed. void - on_finish_impl(error_code& ec); + on_finish_impl( + error_code& ec) override; // The error returned to the caller, if any public: custom_parser() = default; diff --git a/include/boost/beast/_experimental/test/error.hpp b/include/boost/beast/_experimental/test/error.hpp index d583bf93..e857999a 100644 --- a/include/boost/beast/_experimental/test/error.hpp +++ b/include/boost/beast/_experimental/test/error.hpp @@ -33,5 +33,8 @@ enum class error } // boost #include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/_experimental/test/fail_count.hpp b/include/boost/beast/_experimental/test/fail_count.hpp index 4f8881a0..f5683191 100644 --- a/include/boost/beast/_experimental/test/fail_count.hpp +++ b/include/boost/beast/_experimental/test/fail_count.hpp @@ -63,6 +63,8 @@ public: } // beast } // boost -#include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/_experimental/test/impl/error.hpp b/include/boost/beast/_experimental/test/impl/error.hpp index e7d8bd8b..e9c84795 100644 --- a/include/boost/beast/_experimental/test/impl/error.hpp +++ b/include/boost/beast/_experimental/test/impl/error.hpp @@ -37,8 +37,4 @@ make_error_code(error e) noexcept; } // beast } // boost -#ifdef BOOST_BEAST_HEADER_ONLY -#include -#endif - #endif diff --git a/include/boost/beast/_experimental/test/impl/fail_count.hpp b/include/boost/beast/_experimental/test/impl/fail_count.ipp similarity index 84% rename from include/boost/beast/_experimental/test/impl/fail_count.hpp rename to include/boost/beast/_experimental/test/impl/fail_count.ipp index f105905f..c0c25642 100644 --- a/include/boost/beast/_experimental/test/impl/fail_count.hpp +++ b/include/boost/beast/_experimental/test/impl/fail_count.ipp @@ -7,9 +7,10 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_TEST_IMPL_FAIL_COUNT_HPP -#define BOOST_BEAST_TEST_IMPL_FAIL_COUNT_HPP +#ifndef BOOST_BEAST_TEST_IMPL_FAIL_COUNT_IPP +#define BOOST_BEAST_TEST_IMPL_FAIL_COUNT_IPP +#include #include namespace boost { diff --git a/include/boost/beast/_experimental/test/impl/stream.hpp b/include/boost/beast/_experimental/test/impl/stream.hpp index 97d31c6c..222c954e 100644 --- a/include/boost/beast/_experimental/test/impl/stream.hpp +++ b/include/boost/beast/_experimental/test/impl/stream.hpp @@ -433,8 +433,4 @@ connect(stream& to, Arg1&& arg1, ArgN&&... argn) } // beast } // boost -#ifdef BOOST_BEAST_HEADER_ONLY -#include -#endif - #endif diff --git a/include/boost/beast/_experimental/test/stream.hpp b/include/boost/beast/_experimental/test/stream.hpp index f78b94c5..aac64283 100644 --- a/include/boost/beast/_experimental/test/stream.hpp +++ b/include/boost/beast/_experimental/test/stream.hpp @@ -550,5 +550,8 @@ connect(stream& to, Arg1&& arg1, ArgN&&... argn); } // boost #include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/core/error.hpp b/include/boost/beast/core/error.hpp index c6c87b12..29a8648a 100644 --- a/include/boost/beast/core/error.hpp +++ b/include/boost/beast/core/error.hpp @@ -81,5 +81,8 @@ enum class condition } // boost #include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/core/flat_static_buffer.hpp b/include/boost/beast/core/flat_static_buffer.hpp index f83b7e31..d664eedf 100644 --- a/include/boost/beast/core/flat_static_buffer.hpp +++ b/include/boost/beast/core/flat_static_buffer.hpp @@ -329,6 +329,7 @@ public: } // beast } // boost +#include #ifdef BOOST_BEAST_HEADER_ONLY #include #endif diff --git a/include/boost/beast/core/impl/error.hpp b/include/boost/beast/core/impl/error.hpp index ec677f0e..a02caeef 100644 --- a/include/boost/beast/core/impl/error.hpp +++ b/include/boost/beast/core/impl/error.hpp @@ -41,8 +41,4 @@ make_error_condition(condition c); } // beast } // boost -#ifdef BOOST_BEAST_HEADER_ONLY -#include -#endif - #endif diff --git a/include/boost/beast/core/impl/flat_static_buffer.hpp b/include/boost/beast/core/impl/flat_static_buffer.hpp new file mode 100644 index 00000000..c812dd0c --- /dev/null +++ b/include/boost/beast/core/impl/flat_static_buffer.hpp @@ -0,0 +1,43 @@ +// +// 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_IMPL_FLAT_STATIC_BUFFER_HPP +#define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_HPP + +namespace boost { +namespace beast { + +template +flat_static_buffer:: +flat_static_buffer( + flat_static_buffer const& other) + : flat_static_buffer_base(buf_, N) +{ + this->commit(net::buffer_copy( + this->prepare(other.size()), other.data())); +} + +template +auto +flat_static_buffer:: +operator=(flat_static_buffer const& other) -> + flat_static_buffer& +{ + if(this == &other) + return *this; + this->consume(this->size()); + this->commit(net::buffer_copy( + this->prepare(other.size()), other.data())); + return *this; +} + +} // beast +} // boost + +#endif \ No newline at end of file diff --git a/include/boost/beast/core/impl/flat_static_buffer.ipp b/include/boost/beast/core/impl/flat_static_buffer.ipp index 694b2fb8..31f158eb 100644 --- a/include/boost/beast/core/impl/flat_static_buffer.ipp +++ b/include/boost/beast/core/impl/flat_static_buffer.ipp @@ -83,31 +83,6 @@ reset(void* p, std::size_t n) noexcept end_ = begin_ + n; } -//------------------------------------------------------------------------------ - -template -flat_static_buffer:: -flat_static_buffer(flat_static_buffer const& other) - : flat_static_buffer_base(buf_, N) -{ - this->commit(net::buffer_copy( - this->prepare(other.size()), other.data())); -} - -template -auto -flat_static_buffer:: -operator=(flat_static_buffer const& other) -> - flat_static_buffer& -{ - if(this == &other) - return *this; - this->consume(this->size()); - this->commit(net::buffer_copy( - this->prepare(other.size()), other.data())); - return *this; -} - } // beast } // boost diff --git a/include/boost/beast/http/basic_parser.hpp b/include/boost/beast/http/basic_parser.hpp index edd01371..8e51393e 100644 --- a/include/boost/beast/http/basic_parser.hpp +++ b/include/boost/beast/http/basic_parser.hpp @@ -47,133 +47,38 @@ namespace http { the structured portion of the HTTP message (header or chunk header) is contained in a linear buffer. - The interface uses CRTP (Curiously Recurring Template Pattern). - To use this class directly, derive from @ref basic_parser. When + The interface to the parser uses virtual member functions. + To use this class, derive your type from @ref basic_parser. When bytes are presented, the implementation will make a series of zero - or more calls to derived class members functions (termed "callbacks" - in this context) matching a specific signature. + or more calls to virtual functions, which the derived class must + implement. - Every callback must be provided by the derived class, or else - a compilation error will be generated. This exemplar shows - the signature and description of the callbacks required in - the derived class. - For each callback, the function will ensure that `!ec` is `true` - if there was no error or set to the appropriate error code if - there was one. If an error is set, the value is propagated to - the caller of the parser. - - @par Derived Class Requirements - @code - template - class derived - : public basic_parser> - { - private: - // The friend declaration is needed, - // otherwise the callbacks must be made public. - friend class basic_parser; - - /// Called after receiving the request-line (isRequest == true). - void - on_request_impl( - verb method, // The method verb, verb::unknown if no match - string_view method_str, // The method as a string - string_view target, // The request-target - int version, // The HTTP-version - error_code& ec); // The error returned to the caller, if any - - /// Called after receiving the start-line (isRequest == false). - void - on_response_impl( - int code, // The status-code - string_view reason, // The obsolete reason-phrase - int version, // The HTTP-version - error_code& ec); // The error returned to the caller, if any - - /// Called after receiving a header field. - void - on_field_impl( - field f, // The known-field enumeration constant - string_view name, // The field name string. - string_view value, // The field value - error_code& ec); // The error returned to the caller, if any - - /// Called after the complete header is received. - void - on_header_impl( - error_code& ec); // The error returned to the caller, if any - - /// Called just before processing the body, if a body exists. - void - on_body_init_impl( - boost::optional< - std::uint64_t> const& - content_length, // Content length if known, else `boost::none` - error_code& ec); // The error returned to the caller, if any - - /// Called for each piece of the body, if a body exists. - //! - //! This is used when there is no chunked transfer coding. - //! - //! The function returns the number of bytes consumed from the - //! input buffer. Any input octets not consumed will be will be - //! presented on subsequent calls. - //! - std::size_t - on_body_impl( - string_view s, // A portion of the body - error_code& ec); // The error returned to the caller, if any - - /// Called for each chunk header. - void - on_chunk_header_impl( - std::uint64_t size, // The size of the upcoming chunk, - // or zero for the last chunk - string_view extension, // The chunk extensions (may be empty) - error_code& ec); // The error returned to the caller, if any - - /// Called to deliver the chunk body. - //! - //! This is used when there is a chunked transfer coding. The - //! implementation will automatically remove the encoding before - //! calling this function. - //! - //! The function returns the number of bytes consumed from the - //! input buffer. Any input octets not consumed will be will be - //! presented on subsequent calls. - //! - std::size_t - on_chunk_body_impl( - std::uint64_t remain, // The number of bytes remaining in the chunk, - // including what is being passed here. - // or zero for the last chunk - string_view body, // The next piece of the chunk body - error_code& ec); // The error returned to the caller, if any - - /// Called when the complete message is parsed. - void - on_finish_impl(error_code& ec); - - public: - derived() = default; - }; - @endcode + Every virtual function must be provided by the derived class, + or else a compilation error will be generated. The implementation + will make sure that `ec` is clear before each virtual function + is invoked. If a virtual function sets an error, it is propagated + out of the parser to the caller. @tparam isRequest A `bool` indicating whether the parser will be presented with request or response message. - @tparam Derived The derived class type. This is part of the - Curiously Recurring Template Pattern interface. - @note If the parser encounters a field value with obs-fold longer than 4 kilobytes in length, an error is generated. */ -template +template class basic_parser : private detail::basic_parser_base { - template - friend class basic_parser; + std::uint64_t body_limit_ = + default_body_limit(is_request{}); // max payload body + std::uint64_t len_ = 0; // size of chunk or body + std::unique_ptr buf_; // temp storage + std::size_t buf_len_ = 0; // size of buf_ + std::size_t skip_ = 0; // resume search here + std::uint32_t header_limit_ = 8192; // max header size + unsigned short status_ = 0; // response status + state state_ = state::nothing_yet; // initial state + unsigned f_ = 0; // flags // limit on the size of the stack flat buffer static std::size_t constexpr max_stack_buffer = 8192; @@ -219,27 +124,13 @@ class basic_parser return 8 * 1024 * 1024; // 8MB } - std::uint64_t body_limit_ = - default_body_limit(is_request{}); // max payload body - std::uint64_t len_ = 0; // size of chunk or body - std::unique_ptr buf_; // temp storage - std::size_t buf_len_ = 0; // size of buf_ - std::size_t skip_ = 0; // resume search here - std::uint32_t header_limit_ = 8192; // max header size - unsigned short status_ = 0; // response status - state state_ = state::nothing_yet; // initial state - unsigned f_ = 0; // flags + template + friend class basic_parser; protected: /// Default constructor basic_parser() = default; - /// Move constructor - basic_parser(basic_parser &&) = default; - - /// Move assignment - basic_parser& operator=(basic_parser &&) = default; - /** Move constructor @note @@ -247,8 +138,10 @@ protected: After the move, the only valid operation on the moved-from object is destruction. */ - template - basic_parser(basic_parser&&); + basic_parser(basic_parser &&) = default; + + /// Move assignment + basic_parser& operator=(basic_parser &&) = default; public: /// `true` if this parser parses requests, `false` for responses. @@ -264,30 +157,6 @@ public: /// Copy assignment basic_parser& operator=(basic_parser const&) = delete; - /** Returns a reference to this object as a `basic_parser`. - - This is used to pass a derived class where a base class is - expected, to choose a correct function overload when the - resolution would be ambiguous. - */ - basic_parser& - base() - { - return *this; - } - - /** Returns a constant reference to this object as a `basic_parser`. - - This is used to pass a derived class where a base class is - expected, to choose a correct function overload when the - resolution would be ambiguous. - */ - basic_parser const& - base() const - { - return *this; - } - /// Returns `true` if the parser has received at least one byte of input. bool got_some() const @@ -526,7 +395,7 @@ public: #if ! BOOST_BEAST_DOXYGEN std::size_t - put(net::const_buffer const& buffer, + put(net::const_buffer buffer, error_code& ec); #endif @@ -549,19 +418,205 @@ public: void put_eof(error_code& ec); -private: - inline - Derived& - impl() +protected: + /** Called after receiving the request-line. + + This virtual function is invoked after receiving a request-line + when parsing HTTP requests. + It can only be called when `isRequest == true`. + + @param method The verb enumeration. If the method string is not + one of the predefined strings, this value will be @ref verb::unknown. + + @param method_str The unmodified string representing the verb. + + @param target The request-target. + + @param version The HTTP-version. This will be 10 for HTTP/1.0, + and 11 for HTTP/1.1. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_request_impl( + verb method, + string_view method_str, + string_view target, + int version, + error_code& ec) = 0; + + /** Called after receiving the status-line. + + This virtual function is invoked after receiving a status-line + when parsing HTTP responses. + It can only be called when `isRequest == false`. + + @param code The numeric status code. + + @param reason The reason-phrase. Note that this value is + now obsolete, and only provided for historical or diagnostic + purposes. + + @param version The HTTP-version. This will be 10 for HTTP/1.0, + and 11 for HTTP/1.1. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_response_impl( + int code, + string_view reason, + int version, + error_code& ec) = 0; + + /** Called once for each complete field in the HTTP header. + + This virtual function is invoked for each field that is received + while parsing an HTTP message. + + @param name The known field enum value. If the name of the field + is not recognized, this value will be @ref field::unknown. + + @param name_string The exact name of the field as received from + the input, represented as a string. + + @param value A string holding the value of the field. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_field_impl( + field name, + string_view name_string, + string_view value, + error_code& ec) = 0; + + /** Called once after the complete HTTP header is received. + + This virtual function is invoked once, after the complete HTTP + header is received while parsing a message. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_header_impl(error_code& ec) = 0; + + /** Called once before the body is processed. + + This virtual function is invoked once, before the content body is + processed (but after the complete header is received). + + @param content_length A value representing the content length in + bytes if the length is known (this can include a zero length). + Otherwise, the value will be `boost::none`. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_body_init_impl( + boost::optional const& content_length, + error_code& ec) = 0; + + /** Called each time additional data is received representing the content body. + + This virtual function is invoked for each piece of the body which is + received while parsing of a message. This function is only used when + no chunked transfer encoding is present. + + @param body A string holding the additional body contents. This may + contain nulls or unprintable characters. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + + @see on_chunk_body_impl + */ + virtual + std::size_t + on_body_impl( + string_view body, + error_code& ec) { - return *static_cast(this); } + /** Called each time a new chunk header of a chunk encoded body is received. + + This function is invoked each time a new chunk header is received. + The function is only used when the chunked transfer encoding is present. + + @param size The size of this chunk, in bytes. + + @param extensions A string containing the entire chunk extensions. + This may be empty, indicating no extensions are present. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_chunk_header_impl( + std::uint64_t size, + string_view extensions, + error_code& ec) = 0; + + /** Called each time additional data is received representing part of a body chunk. + + This virtual function is invoked for each piece of the body which is + received while parsing of a message. This function is only used when + no chunked transfer encoding is present. + + @param remain The number of bytes remaining in this chunk. This includes + the contents of passed `body`. If this value is zero, then this represents + the final chunk. + + @param body A string holding the additional body contents. This may + contain nulls or unprintable characters. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + + @return This function should return the number of bytes actually consumed + from the `body` value. Any bytes that are not consumed on this call + will be presented in a subsequent call. + + @see on_body_impl + */ + virtual + std::size_t + on_chunk_body_impl( + std::uint64_t remain, + string_view body, + error_code& ec) = 0; + + /** Called once when the complete message is received. + + This virtual function is invoked once, after successfully parsing + a complete HTTP message. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_finish_impl(error_code& ec) = 0; + +private: template std::size_t - put_from_stack(std::size_t size, + put_from_stack( + std::size_t size, ConstBufferSequence const& buffers, - error_code& ec); + error_code& ec); void maybe_need_more( @@ -617,5 +672,8 @@ private: } // boost #include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/http/detail/type_traits.hpp b/include/boost/beast/http/detail/type_traits.hpp index 7eb6b03f..77d6d3fd 100644 --- a/include/boost/beast/http/detail/type_traits.hpp +++ b/include/boost/beast/http/detail/type_traits.hpp @@ -24,7 +24,7 @@ class header; template class message; -template +template class parser; namespace detail { diff --git a/include/boost/beast/http/error.hpp b/include/boost/beast/http/error.hpp index 7a003bc5..8dc59053 100644 --- a/include/boost/beast/http/error.hpp +++ b/include/boost/beast/http/error.hpp @@ -152,5 +152,8 @@ enum class error } // boost #include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/http/impl/basic_parser.hpp b/include/boost/beast/http/impl/basic_parser.hpp index 5192113f..6303439d 100644 --- a/include/boost/beast/http/impl/basic_parser.hpp +++ b/include/boost/beast/http/impl/basic_parser.hpp @@ -11,83 +11,17 @@ #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP #include -#include -#include -#include -#include -#include #include #include -#include -#include namespace boost { namespace beast { namespace http { -template -template -basic_parser:: -basic_parser(basic_parser< - isRequest, OtherDerived>&& other) - : body_limit_(other.body_limit_) - , len_(other.len_) - , buf_(std::move(other.buf_)) - , buf_len_(other.buf_len_) - , skip_(other.skip_) - , header_limit_(other.header_limit_) - , status_(other.status_) - , state_(other.state_) - , f_(other.f_) -{ -} - -template -bool -basic_parser:: -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 -boost::optional -basic_parser:: -content_length() const -{ - BOOST_ASSERT(is_header_done()); - if(! (f_ & flagContentLength)) - return boost::none; - return len_; -} - -template -void -basic_parser:: -skip(bool v) -{ - BOOST_ASSERT(! got_some()); - if(v) - f_ |= flagSkipBody; - else - f_ &= ~flagSkipBody; -} - -template +template template std::size_t -basic_parser:: +basic_parser:: put(ConstBufferSequence const& buffers, error_code& ec) { @@ -122,180 +56,10 @@ put(ConstBufferSequence const& buffers, buf_.get(), buf_len_}, ec); } -template -std::size_t -basic_parser:: -put(net::const_buffer const& buffer, - error_code& ec) -{ - BOOST_ASSERT(state_ != state::complete); - auto p = static_cast(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) - { - 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)( - header_limit_, n), ec, is_request{}); - if(ec) - { - if(ec == error::need_more) - { - if(n >= header_limit_) - { - 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(p1 - p); - if(p >= p1) - { - 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)( - header_limit_, n), ec); - if(ec) - { - if(ec == error::need_more) - { - if(n >= header_limit_) - { - 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{}); - break; - - case state::body0: - BOOST_ASSERT(! skip_); - impl().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_); - impl().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: - impl().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(p1 - p); - goto loop; - } -done: - return static_cast(p - p0); -} - -template -void -basic_parser:: -put_eof(error_code& ec) -{ - BOOST_ASSERT(got_some()); - if( state_ == state::start_line || - state_ == state::fields) - { - ec = error::partial_message; - return; - } - if(f_ & (flagContentLength | flagChunked)) - { - if(state_ != state::complete) - { - ec = error::partial_message; - return; - } - ec = {}; - return; - } - impl().on_finish_impl(ec); - if(ec) - return; - state_ = state::complete; -} - -template +template template std::size_t -basic_parser:: +basic_parser:: put_from_stack(std::size_t size, ConstBufferSequence const& buffers, error_code& ec) @@ -307,615 +71,6 @@ put_from_stack(std::size_t size, buf, size}, ec); } -template -inline -void -basic_parser:: -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) - { - ec = error::need_more; - return; - } - auto const term = - find_eom(p + skip_, p + n); - if(! term) - { - skip_ = n - 3; - if(skip_ + 4 > header_limit_) - { - ec = error::header_limit; - return; - } - ec = error::need_more; - return; - } - skip_ = 0; -} - -template -inline -void -basic_parser:: -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) - { - ec = error::bad_version; - return; - } - - if(p + 2 > last) - { - ec = error::need_more; - return; - } - if(p[0] != '\r' || p[1] != '\n') - { - ec = error::bad_version; - return; - } - p += 2; - - if(version >= 11) - f_ |= flagHTTP11; - - impl().on_request_impl(string_to_verb(method), - method, target, version, ec); - if(ec) - return; - - in = p; - state_ = state::fields; -} - -template -inline -void -basic_parser:: -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) - { - ec = error::bad_version; - return; - } - - // SP - if(p + 1 > last) - { - ec = error::need_more; - return; - } - if(*p++ != ' ') - { - 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; - - impl().on_response_impl( - status_, reason, version, ec); - if(ec) - return; - - in = p; - state_ = state::fields; -} - -template -void -basic_parser:: -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 - static_string buf; - auto p = in; - for(;;) - { - if(p + 2 > last) - { - ec = error::need_more; - return; - } - if(p[0] == '\r') - { - if(p[1] != '\n') - 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; - impl().on_field_impl(f, name, value, ec); - if(ec) - return; - in = p; - } -} - -template -inline -void -basic_parser:: -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(len_ > body_limit_) - { - 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; - state_ = state::complete; - } - - impl().on_header_impl(ec); - if(ec) - return; - if(state_ == state::complete) - { - impl().on_finish_impl(ec); - if(ec) - return; - } -} - -template -inline -void -basic_parser:: -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_ > body_limit_) - { - 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 - { - f_ |= flagHasBody; - f_ |= flagNeedEOF; - state_ = state::body_to_eof0; - } - - impl().on_header_impl(ec); - if(ec) - return; - if(state_ == state::complete) - { - impl().on_finish_impl(ec); - if(ec) - return; - } -} - -template -inline -void -basic_parser:: -parse_body(char const*& p, - std::size_t n, error_code& ec) -{ - n = impl().on_body_impl(string_view{p, - beast::detail::clamp(len_, n)}, ec); - p += n; - len_ -= n; - if(ec) - return; - if(len_ > 0) - return; - impl().on_finish_impl(ec); - if(ec) - return; - state_ = state::complete; -} - -template -inline -void -basic_parser:: -parse_body_to_eof(char const*& p, - std::size_t n, error_code& ec) -{ - if(n > body_limit_) - { - ec = error::body_limit; - return; - } - body_limit_ = body_limit_ - n; - n = impl().on_body_impl(string_view{p, n}, ec); - p += n; - if(ec) - return; -} - -template -void -basic_parser:: -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) - { - 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)) - { - ec = error::bad_chunk; - return; - } - } - eol = find_eol(p0 + skip_, pend, ec); - if(ec) - return; - if(! eol) - { - 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)) - { - ec = error::bad_chunk; - return; - } - if(size != 0) - { - if(size > body_limit_) - { - 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 ) - { - ec = error::bad_chunk_extension; - return; - } - auto const ext = make_string(start, p); - impl().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 >= 5); - if(f_ & flagExpectCRLF) - 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; - ec = error::need_more; - return; - } - - auto const start = p; - parse_chunk_extensions(p, pend, ec); - if(ec) - return; - if(p != eol - 2) - { - ec = error::bad_chunk_extension; - return; - } - auto const ext = make_string(start, p); - impl().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; - - impl().on_finish_impl(ec); - if(ec) - return; - state_ = state::complete; -} - -template -inline -void -basic_parser:: -parse_chunk_body(char const*& p, - std::size_t n, error_code& ec) -{ - n = impl().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 -void -basic_parser:: -do_field(field f, - string_view value, error_code& ec) -{ - // 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? - ec = error::bad_value; - return; - } - for(auto const& s : list) - { - if(iequals({"close", 5}, s)) - { - f_ |= flagConnectionClose; - continue; - } - - if(iequals({"keep-alive", 10}, s)) - { - f_ |= flagConnectionKeepAlive; - continue; - } - - if(iequals({"upgrade", 7}, s)) - { - f_ |= flagConnectionUpgrade; - continue; - } - } - ec = {}; - return; - } - - // Content-Length - if(f == field::content_length) - { - if(f_ & flagContentLength) - { - // duplicate - ec = error::bad_content_length; - return; - } - - if(f_ & flagChunked) - { - // conflicting field - ec = error::bad_content_length; - return; - } - - std::uint64_t v; - if(! parse_dec( - value.begin(), value.end(), v)) - { - ec = error::bad_content_length; - return; - } - - ec = {}; - len_ = v; - f_ |= flagContentLength; - return; - } - - // Transfer-Encoding - if(f == field::transfer_encoding) - { - if(f_ & flagChunked) - { - // duplicate - ec = error::bad_transfer_encoding; - return; - } - - if(f_ & flagContentLength) - { - // conflicting field - ec = error::bad_transfer_encoding; - return; - } - - ec = {}; - auto const v = token_list{value}; - auto const p = std::find_if(v.begin(), v.end(), - [&](typename token_list::value_type const& s) - { - return iequals({"chunked", 7}, 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 = {}; -} - } // http } // beast } // boost diff --git a/include/boost/beast/http/impl/basic_parser.ipp b/include/boost/beast/http/impl/basic_parser.ipp new file mode 100644 index 00000000..ae329f71 --- /dev/null +++ b/include/boost/beast/http/impl/basic_parser.ipp @@ -0,0 +1,857 @@ +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { + +template +bool +basic_parser:: +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 +boost::optional +basic_parser:: +content_length() const +{ + BOOST_ASSERT(is_header_done()); + if(! (f_ & flagContentLength)) + return boost::none; + return len_; +} + +template +void +basic_parser:: +skip(bool v) +{ + BOOST_ASSERT(! got_some()); + if(v) + f_ |= flagSkipBody; + else + f_ &= ~flagSkipBody; +} + +template +std::size_t +basic_parser:: +put(net::const_buffer buffer, + error_code& ec) +{ + BOOST_ASSERT(state_ != state::complete); + auto p = static_cast(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) + { + 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)( + header_limit_, n), ec, is_request{}); + if(ec) + { + if(ec == error::need_more) + { + if(n >= header_limit_) + { + 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(p1 - p); + if(p >= p1) + { + 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)( + header_limit_, n), ec); + if(ec) + { + if(ec == error::need_more) + { + if(n >= header_limit_) + { + 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{}); + 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(p1 - p); + goto loop; + } +done: + return static_cast(p - p0); +} + +template +void +basic_parser:: +put_eof(error_code& ec) +{ + BOOST_ASSERT(got_some()); + if( state_ == state::start_line || + state_ == state::fields) + { + ec = error::partial_message; + return; + } + if(f_ & (flagContentLength | flagChunked)) + { + if(state_ != state::complete) + { + ec = error::partial_message; + return; + } + ec = {}; + return; + } + ec = {}; + this->on_finish_impl(ec); + if(ec) + return; + state_ = state::complete; +} + +template +void +basic_parser:: +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) + { + ec = error::need_more; + return; + } + auto const term = + find_eom(p + skip_, p + n); + if(! term) + { + skip_ = n - 3; + if(skip_ + 4 > header_limit_) + { + ec = error::header_limit; + return; + } + ec = error::need_more; + return; + } + skip_ = 0; +} + +template +void +basic_parser:: +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) + { + ec = error::bad_version; + return; + } + + if(p + 2 > last) + { + ec = error::need_more; + return; + } + if(p[0] != '\r' || p[1] != '\n') + { + 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 +void +basic_parser:: +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) + { + ec = error::bad_version; + return; + } + + // SP + if(p + 1 > last) + { + ec = error::need_more; + return; + } + if(*p++ != ' ') + { + 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 +void +basic_parser:: +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 + static_string buf; + auto p = in; + for(;;) + { + if(p + 2 > last) + { + ec = error::need_more; + return; + } + if(p[0] == '\r') + { + if(p[1] != '\n') + 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 +void +basic_parser:: +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(len_ > body_limit_) + { + 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; + 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 +void +basic_parser:: +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_ > body_limit_) + { + 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 + { + 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 +void +basic_parser:: +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 +void +basic_parser:: +parse_body_to_eof(char const*& p, + std::size_t n, error_code& ec) +{ + if(n > body_limit_) + { + ec = error::body_limit; + return; + } + body_limit_ = body_limit_ - n; + ec = {}; + n = this->on_body_impl(string_view{p, n}, ec); + p += n; + if(ec) + return; +} + +template +void +basic_parser:: +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) + { + 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)) + { + ec = error::bad_chunk; + return; + } + } + eol = find_eol(p0 + skip_, pend, ec); + if(ec) + return; + if(! eol) + { + 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)) + { + ec = error::bad_chunk; + return; + } + if(size != 0) + { + if(size > body_limit_) + { + 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 ) + { + 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 >= 5); + if(f_ & flagExpectCRLF) + 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; + ec = error::need_more; + return; + } + + auto const start = p; + parse_chunk_extensions(p, pend, ec); + if(ec) + return; + if(p != eol - 2) + { + 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 +void +basic_parser:: +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 +void +basic_parser:: +do_field(field f, + string_view value, error_code& ec) +{ + // 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? + ec = error::bad_value; + return; + } + for(auto const& s : list) + { + if(iequals({"close", 5}, s)) + { + f_ |= flagConnectionClose; + continue; + } + + if(iequals({"keep-alive", 10}, s)) + { + f_ |= flagConnectionKeepAlive; + continue; + } + + if(iequals({"upgrade", 7}, s)) + { + f_ |= flagConnectionUpgrade; + continue; + } + } + ec = {}; + return; + } + + // Content-Length + if(f == field::content_length) + { + if(f_ & flagContentLength) + { + // duplicate + ec = error::bad_content_length; + return; + } + + if(f_ & flagChunked) + { + // conflicting field + ec = error::bad_content_length; + return; + } + + std::uint64_t v; + if(! parse_dec( + value.begin(), value.end(), v)) + { + ec = error::bad_content_length; + return; + } + + ec = {}; + len_ = v; + f_ |= flagContentLength; + return; + } + + // Transfer-Encoding + if(f == field::transfer_encoding) + { + if(f_ & flagChunked) + { + // duplicate + ec = error::bad_transfer_encoding; + return; + } + + if(f_ & flagContentLength) + { + // conflicting field + ec = error::bad_transfer_encoding; + return; + } + + ec = {}; + auto const v = token_list{value}; + auto const p = std::find_if(v.begin(), v.end(), + [&](typename token_list::value_type const& s) + { + return iequals({"chunked", 7}, 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; +template class http::basic_parser; +#endif + +} // http +} // beast +} // boost + +#endif diff --git a/include/boost/beast/http/impl/error.hpp b/include/boost/beast/http/impl/error.hpp index 0ffef3fd..b4efaa05 100644 --- a/include/boost/beast/http/impl/error.hpp +++ b/include/boost/beast/http/impl/error.hpp @@ -34,8 +34,4 @@ make_error_code(error ev); } // beast } // boost -#ifdef BOOST_BEAST_HEADER_ONLY -#include -#endif - #endif diff --git a/include/boost/beast/http/impl/parser.hpp b/include/boost/beast/http/impl/parser.hpp index 18996cce..066ac5fe 100644 --- a/include/boost/beast/http/impl/parser.hpp +++ b/include/boost/beast/http/impl/parser.hpp @@ -42,7 +42,7 @@ parser:: parser( parser&& other, Args&&... args) - : base_type(std::move(other)) + : basic_parser(std::move(other)) , m_(other.release(), std::forward(args)...) , rd_(m_.base(), m_.body()) { diff --git a/include/boost/beast/http/impl/read.hpp b/include/boost/beast/http/impl/read.hpp index 01b05d79..fd2f02be 100644 --- a/include/boost/beast/http/impl/read.hpp +++ b/include/boost/beast/http/impl/read.hpp @@ -30,12 +30,12 @@ std::size_t constexpr default_max_transfer_size = 65536; template< class DynamicBuffer, - bool isRequest, class Derived, + bool isRequest, class Condition> std::size_t parse_until( DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec, Condition cond) { @@ -92,10 +92,10 @@ parse_until( } // predicate is true on any forward parser progress -template +template struct read_some_condition { - basic_parser& parser; + basic_parser& parser; template std::size_t @@ -112,10 +112,10 @@ struct read_some_condition }; // predicate is true when parser header is complete -template +template struct read_header_condition { - basic_parser& parser; + basic_parser& parser; template std::size_t @@ -132,10 +132,10 @@ struct read_header_condition }; // predicate is true when parser message is complete -template +template struct read_all_condition { - basic_parser& parser; + basic_parser& parser; template std::size_t @@ -256,12 +256,12 @@ struct run_read_msg_op template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_some( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser) + basic_parser& parser) { static_assert( is_sync_read_stream::value, @@ -271,7 +271,7 @@ read_some( "DynamicBuffer type requirements not met"); error_code ec; auto const bytes_transferred = - read_some(stream, buffer, parser, ec); + http::read_some(stream, buffer, parser, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return bytes_transferred; @@ -280,12 +280,12 @@ read_some( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_some( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec) { static_assert( @@ -296,27 +296,27 @@ read_some( "DynamicBuffer type requirements not met"); return beast::detail::read(stream, buffer, detail::read_some_condition< - isRequest, Derived>{parser}, ec); + isRequest>{parser}, ec); } template< class AsyncReadStream, class DynamicBuffer, - bool isRequest, class Derived, + bool isRequest, class ReadHandler> BOOST_ASIO_INITFN_RESULT_TYPE( ReadHandler, void(error_code, std::size_t)) async_read_some( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler) { return beast::detail::async_read( stream, buffer, detail::read_some_condition< - isRequest, Derived>{parser}, + isRequest>{parser}, std::forward(handler)); } @@ -325,12 +325,12 @@ async_read_some( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_header( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser) + basic_parser& parser) { static_assert( is_sync_read_stream::value, @@ -340,7 +340,7 @@ read_header( "DynamicBuffer type requirements not met"); error_code ec; auto const bytes_transferred = - read_header(stream, buffer, parser, ec); + http::read_header(stream, buffer, parser, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return bytes_transferred; @@ -349,12 +349,12 @@ read_header( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_header( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec) { static_assert( @@ -366,20 +366,20 @@ read_header( parser.eager(false); return beast::detail::read(stream, buffer, detail::read_header_condition< - isRequest, Derived>{parser}, ec); + isRequest>{parser}, ec); } template< class AsyncReadStream, class DynamicBuffer, - bool isRequest, class Derived, + bool isRequest, class ReadHandler> BOOST_ASIO_INITFN_RESULT_TYPE( ReadHandler, void(error_code, std::size_t)) async_read_header( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler) { parser.eager(false); @@ -387,7 +387,7 @@ async_read_header( stream, buffer, detail::read_header_condition< - isRequest, Derived>{parser}, + isRequest>{parser}, std::forward(handler)); } @@ -396,12 +396,12 @@ async_read_header( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser) + basic_parser& parser) { static_assert( is_sync_read_stream::value, @@ -411,7 +411,7 @@ read( "DynamicBuffer type requirements not met"); error_code ec; auto const bytes_transferred = - read(stream, buffer, parser, ec); + http::read(stream, buffer, parser, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return bytes_transferred; @@ -420,12 +420,12 @@ read( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec) { static_assert( @@ -437,20 +437,20 @@ read( parser.eager(true); return beast::detail::read(stream, buffer, detail::read_all_condition< - isRequest, Derived>{parser}, ec); + isRequest>{parser}, ec); } template< class AsyncReadStream, class DynamicBuffer, - bool isRequest, class Derived, + bool isRequest, class ReadHandler> BOOST_ASIO_INITFN_RESULT_TYPE( ReadHandler, void(error_code, std::size_t)) async_read( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler) { static_assert( @@ -464,7 +464,7 @@ async_read( stream, buffer, detail::read_all_condition< - isRequest, Derived>{parser}, + isRequest>{parser}, std::forward(handler)); } @@ -492,7 +492,7 @@ read( "BodyReader type requirements not met"); error_code ec; auto const bytes_transferred = - read(stream, buffer, msg, ec); + http::read(stream, buffer, msg, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return bytes_transferred; @@ -522,7 +522,7 @@ read( parser p(std::move(msg)); p.eager(true); auto const bytes_transferred = - read(stream, buffer, p.base(), ec); + http::read(stream, buffer, p, ec); if(ec) return bytes_transferred; msg = p.release(); diff --git a/include/boost/beast/http/parser.hpp b/include/boost/beast/http/parser.hpp index 30bb1800..854d93bb 100644 --- a/include/boost/beast/http/parser.hpp +++ b/include/boost/beast/http/parser.hpp @@ -47,8 +47,7 @@ template< class Body, class Allocator = std::allocator> class parser - : public basic_parser> + : public basic_parser { static_assert(is_body::value, "Body type requirements not met"); @@ -59,9 +58,6 @@ class parser template friend class parser; - using base_type = basic_parser>; - message> m_; typename Body::reader rd_; bool rd_inited_ = false; @@ -297,8 +293,6 @@ public: } private: - friend class basic_parser; - parser(std::true_type); parser(std::false_type); @@ -338,7 +332,8 @@ private: string_view method_str, string_view target, int version, - error_code& ec) + error_code& ec, + std::true_type) { try { @@ -347,7 +342,6 @@ private: m_.method(method); else m_.method_string(method_str); - ec = {}; } catch(std::bad_alloc const&) { @@ -356,19 +350,39 @@ private: m_.version(version); } + void + on_request_impl( + verb, string_view, string_view, + int, error_code&, std::false_type) + { + } + + void + on_request_impl( + verb method, + string_view method_str, + string_view target, + int version, + error_code& ec) override + { + this->on_request_impl( + method, method_str, target, version, ec, + std::integral_constant{}); + } + void on_response_impl( int code, string_view reason, int version, - error_code& ec) + error_code& ec, + std::true_type) { m_.result(code); m_.version(version); try { m_.reason(reason); - ec = {}; } catch(std::bad_alloc const&) { @@ -376,17 +390,35 @@ private: } } + void + on_response_impl( + int, string_view, int, + error_code&, std::false_type) + { + } + + void + on_response_impl( + int code, + string_view reason, + int version, + error_code& ec) override + { + this->on_response_impl( + code, reason, version, ec, + std::integral_constant{}); + } + void on_field_impl( field name, string_view name_string, string_view value, - error_code& ec) + error_code& ec) override { try { m_.insert(name, name_string, value); - ec = {}; } catch(std::bad_alloc const&) { @@ -395,7 +427,7 @@ private: } void - on_header_impl(error_code& ec) + on_header_impl(error_code& ec) override { ec = {}; } @@ -403,7 +435,7 @@ private: void on_body_init_impl( boost::optional const& content_length, - error_code& ec) + error_code& ec) override { rd_.init(content_length, ec); rd_inited_ = true; @@ -412,7 +444,7 @@ private: std::size_t on_body_impl( string_view body, - error_code& ec) + error_code& ec) override { return rd_.put(net::buffer( body.data(), body.size()), ec); @@ -422,18 +454,17 @@ private: on_chunk_header_impl( std::uint64_t size, string_view extensions, - error_code& ec) + error_code& ec) override { if(cb_h_) return cb_h_(size, extensions, ec); - ec = {}; } std::size_t on_chunk_body_impl( std::uint64_t remain, string_view body, - error_code& ec) + error_code& ec) override { if(cb_b_) return cb_b_(remain, body, ec); @@ -442,7 +473,8 @@ private: } void - on_finish_impl(error_code& ec) + on_finish_impl( + error_code& ec) override { rd_.finish(ec); } diff --git a/include/boost/beast/http/read.hpp b/include/boost/beast/http/read.hpp index 44cf9553..d8eef0bf 100644 --- a/include/boost/beast/http/read.hpp +++ b/include/boost/beast/http/read.hpp @@ -72,12 +72,12 @@ namespace http { template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_some( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser); + basic_parser& parser); /** Read part of a message from a stream using a parser. @@ -129,12 +129,12 @@ read_some( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_some( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec); /** Read part of a message asynchronously from a stream using a parser. @@ -205,14 +205,14 @@ read_some( template< class AsyncReadStream, class DynamicBuffer, - bool isRequest, class Derived, + bool isRequest, class ReadHandler> BOOST_ASIO_INITFN_RESULT_TYPE( ReadHandler, void(error_code, std::size_t)) async_read_some( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler); //------------------------------------------------------------------------------ @@ -267,12 +267,12 @@ async_read_some( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_header( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser); + basic_parser& parser); /** Read a complete message header from a stream using a parser. @@ -324,12 +324,12 @@ read_header( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_header( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec); /** Read a complete message header asynchronously from a stream using a parser. @@ -401,14 +401,14 @@ read_header( template< class AsyncReadStream, class DynamicBuffer, - bool isRequest, class Derived, + bool isRequest, class ReadHandler> BOOST_ASIO_INITFN_RESULT_TYPE( ReadHandler, void(error_code, std::size_t)) async_read_header( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler); //------------------------------------------------------------------------------ @@ -463,12 +463,12 @@ async_read_header( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser); + basic_parser& parser); /** Read a complete message from a stream using a parser. @@ -520,12 +520,12 @@ read( template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec); /** Read a complete message asynchronously from a stream using a parser. @@ -597,14 +597,14 @@ read( template< class AsyncReadStream, class DynamicBuffer, - bool isRequest, class Derived, + bool isRequest, class ReadHandler> BOOST_ASIO_INITFN_RESULT_TYPE( ReadHandler, void(error_code, std::size_t)) async_read( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler); //------------------------------------------------------------------------------ diff --git a/include/boost/beast/src.hpp b/include/boost/beast/src.hpp index dbdf6ce0..d564dd73 100644 --- a/include/boost/beast/src.hpp +++ b/include/boost/beast/src.hpp @@ -26,6 +26,7 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined. #endif #include +#include #include #include @@ -38,6 +39,7 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined. #include #include +#include #include #include #include diff --git a/include/boost/beast/websocket/error.hpp b/include/boost/beast/websocket/error.hpp index 4bb7d14b..cc510912 100644 --- a/include/boost/beast/websocket/error.hpp +++ b/include/boost/beast/websocket/error.hpp @@ -249,5 +249,8 @@ enum class condition } // boost #include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/websocket/impl/error.hpp b/include/boost/beast/websocket/impl/error.hpp index 3cc99472..1f3a4e7a 100644 --- a/include/boost/beast/websocket/impl/error.hpp +++ b/include/boost/beast/websocket/impl/error.hpp @@ -41,8 +41,4 @@ make_error_condition(condition c); } // beast } // boost -#ifdef BOOST_BEAST_HEADER_ONLY -#include -#endif - #endif diff --git a/include/boost/beast/zlib/error.hpp b/include/boost/beast/zlib/error.hpp index 43790560..2c7d3915 100644 --- a/include/boost/beast/zlib/error.hpp +++ b/include/boost/beast/zlib/error.hpp @@ -133,6 +133,9 @@ enum class error } // boost #include +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif #endif diff --git a/include/boost/beast/zlib/impl/error.hpp b/include/boost/beast/zlib/impl/error.hpp index 03cf0db2..2084b3b7 100644 --- a/include/boost/beast/zlib/impl/error.hpp +++ b/include/boost/beast/zlib/impl/error.hpp @@ -60,8 +60,4 @@ make_error_code(error ev); } // beast } // boost -#ifdef BOOST_BEAST_HEADER_ONLY -#include -#endif - #endif diff --git a/test/beast/http/basic_parser.cpp b/test/beast/http/basic_parser.cpp index cac21612..b3f43fa2 100644 --- a/test/beast/http/basic_parser.cpp +++ b/test/beast/http/basic_parser.cpp @@ -894,10 +894,10 @@ public: return {s.data(), s.size()}; } - template + template std::size_t feed(ConstBufferSequence const& buffers, - basic_parser& p, error_code& ec) + basic_parser& p, error_code& ec) { p.eager(true); return p.put(buffers, ec); diff --git a/test/beast/http/parser.cpp b/test/beast/http/parser.cpp index ddbd403a..6b0060e1 100644 --- a/test/beast/http/parser.cpp +++ b/test/beast/http/parser.cpp @@ -45,11 +45,11 @@ public: } template + bool isRequest> static void put(ConstBufferSequence const& buffers, - basic_parser& p, + basic_parser& p, error_code& ec) { buffers_suffix cb{buffers}; diff --git a/test/beast/http/test_parser.hpp b/test/beast/http/test_parser.hpp index dcc45c90..a12ad2b4 100644 --- a/test/beast/http/test_parser.hpp +++ b/test/beast/http/test_parser.hpp @@ -21,7 +21,7 @@ namespace http { template class test_parser - : public basic_parser> + : public basic_parser { test::fail_count* fc_ = nullptr; @@ -65,8 +65,6 @@ public: ++got_on_begin; if(fc_) fc_->fail(ec); - else - ec = {}; } void @@ -81,8 +79,6 @@ public: ++got_on_begin; if(fc_) fc_->fail(ec); - else - ec = {}; } void @@ -92,8 +88,6 @@ public: ++got_on_field; if(fc_) fc_->fail(ec); - else - ec = {}; fields[std::string(name)] = std::string(value); } @@ -103,8 +97,6 @@ public: ++got_on_header; if(fc_) fc_->fail(ec); - else - ec = {}; } void @@ -117,8 +109,6 @@ public: static_cast(content_length_); if(fc_) fc_->fail(ec); - else - ec = {}; } std::size_t @@ -128,8 +118,6 @@ public: body.append(s.data(), s.size()); if(fc_) fc_->fail(ec); - else - ec = {}; return s.size(); } @@ -142,8 +130,6 @@ public: ++got_on_chunk; if(fc_) fc_->fail(ec); - else - ec = {}; } std::size_t @@ -155,8 +141,6 @@ public: body.append(s.data(), s.size()); if(fc_) fc_->fail(ec); - else - ec = {}; return s.size(); } @@ -167,8 +151,6 @@ public: ++got_on_complete; if(fc_) fc_->fail(ec); - else - ec = {}; } }; diff --git a/test/bench/parser/bench_parser.cpp b/test/bench/parser/bench_parser.cpp index 8f380db3..d8b2cdac 100644 --- a/test/bench/parser/bench_parser.cpp +++ b/test/bench/parser/bench_parser.cpp @@ -70,11 +70,11 @@ public: } template + bool isRequest> static std::size_t feed(ConstBufferSequence const& buffers, - basic_parser& parser, + basic_parser& parser, error_code& ec) { beast::buffers_suffix< @@ -149,79 +149,131 @@ public: template struct null_parser : - basic_parser> + basic_parser { + void + on_request_impl( + verb, string_view, string_view, + int, error_code&) override + { + } + + void + on_response_impl( + int, string_view, int, + error_code&) override + { + } + + void + on_field_impl( + field, string_view, string_view, + error_code&) override + { + } + + void + on_header_impl(error_code&) override + { + } + + void + on_body_init_impl( + boost::optional const&, + error_code&) override + { + } + + std::size_t + on_body_impl( + string_view, + error_code&) override + { + return 0; + } + + void + on_chunk_header_impl( + std::uint64_t, + string_view, + error_code&) override + { + } + + std::size_t + on_chunk_body_impl( + std::uint64_t, + string_view, + error_code&) override + { + return 0; + } + + void + on_finish_impl(error_code& ec) override + { + } }; template - struct bench_parser : basic_parser< - isRequest, bench_parser> + struct bench_parser : basic_parser { using mutable_buffers_type = net::mutable_buffer; void on_request_impl(verb, string_view, - string_view, int, error_code& ec) + string_view, int, error_code&) override { - ec = {}; } void on_response_impl(int, - string_view, - int, error_code& ec) + string_view, int, error_code&) override { - ec = {}; } void on_field_impl(field, - string_view, string_view, error_code& ec) + string_view, string_view, error_code&) override { - ec = {}; } void - on_header_impl(error_code& ec) + on_header_impl(error_code&) override { - ec = {}; } void on_body_init_impl( boost::optional const&, - error_code& ec) + error_code&) override { - ec = {}; } std::size_t - on_body_impl(string_view s, error_code& ec) + on_body_impl( + string_view s, error_code&) override { - ec = {}; return s.size(); } void on_chunk_header_impl(std::uint64_t, - string_view, error_code& ec) + string_view, error_code&) override { - ec = {}; } std::size_t on_chunk_body_impl(std::uint64_t, - string_view s, error_code& ec) + string_view s, error_code&) override { - ec = {}; return s.size(); } void - on_finish_impl(error_code& ec) + on_finish_impl(error_code&) override { - ec = {}; } };