basic_parser is abstract, not CRTP (API Change):

* `basic_parser` now uses pure virtual member functions instead
  of the curiously recurring template pattern.

Actions Required:

* Change uses of the `basic_parser` type to omit the `Derived`
  template parameter

* Classes derived from `basic_parser` no longer need to friend
  the base.

* Virtual functions in the derived class may be marked `override`.
This commit is contained in:
Vinnie Falco
2019-02-23 06:55:12 -08:00
parent bbd62dd181
commit 59bda5d9c6
33 changed files with 1365 additions and 1236 deletions

View File

@@ -4,6 +4,7 @@ Version 219:
* Visual Studio 2017 minimum requirement for Windows * Visual Studio 2017 minimum requirement for Windows
* Better treatment of SSL short reads * Better treatment of SSL short reads
* ssl_stream is a public interface * ssl_stream is a public interface
* basic_parser is abstract, not CRTP (API Change)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

34
Jamfile
View File

@@ -51,29 +51,9 @@ variant beast_ubasan
path-constant TEST_MAIN : include/boost/beast/_experimental/unit_test/main.cpp ; 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
]
<define>BOOST_ASIO_SEPARATE_COMPILATION
<define>BOOST_ASIO_NO_DEPRECATED=1
<define>BOOST_ASIO_DISABLE_BOOST_ARRAY=1
<define>BOOST_ASIO_DISABLE_BOOST_BIND=1
<define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1
<define>BOOST_ASIO_DISABLE_BOOST_REGEX=1
<define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1
<target-os>windows:<define>_WIN32_WINNT=0x0601
<link>static
;
lib static_beast lib static_beast
: test/lib_beast.cpp : test/lib_beast.cpp
test/lib_asio.cpp
: requirements : requirements
[ requires [ requires
cxx11_constexpr cxx11_constexpr
@@ -83,12 +63,14 @@ lib static_beast
cxx11_variadic_templates cxx11_variadic_templates
] ]
<define>BOOST_BEAST_SPLIT_COMPILATION <define>BOOST_BEAST_SPLIT_COMPILATION
<define>BOOST_ASIO_SEPARATE_COMPILATION
<define>BOOST_ASIO_NO_DEPRECATED=1 <define>BOOST_ASIO_NO_DEPRECATED=1
<define>BOOST_ASIO_DISABLE_BOOST_ARRAY=1 <define>BOOST_ASIO_DISABLE_BOOST_ARRAY=1
<define>BOOST_ASIO_DISABLE_BOOST_BIND=1 <define>BOOST_ASIO_DISABLE_BOOST_BIND=1
<define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 <define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1
<define>BOOST_ASIO_DISABLE_BOOST_REGEX=1 <define>BOOST_ASIO_DISABLE_BOOST_REGEX=1
<define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 <define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1
<toolset>msvc-14.1:<cxxflags>"/permissive-"
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1 <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1 <toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING <toolset>msvc:<define>_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING
@@ -103,7 +85,7 @@ project /boost/beast
<include>./test/extras/include <include>./test/extras/include
<library>/boost/coroutine//boost_coroutine <library>/boost/coroutine//boost_coroutine
<library>/boost/filesystem//boost_filesystem <library>/boost/filesystem//boost_filesystem
<library>static_asio #<library>static_asio
<library>static_beast <library>static_beast
<implicit-dependency>/boost//headers <implicit-dependency>/boost//headers
<threading>multi <threading>multi
@@ -112,16 +94,16 @@ project /boost/beast
[ ac.check-library /boost/beast//ssl : <library>/boost/beast//ssl : <build>no ] [ ac.check-library /boost/beast//ssl : <library>/boost/beast//ssl : <build>no ]
<library>/boost/beast//crypto <library>/boost/beast//crypto
<define>BOOST_ALL_NO_LIB=1 <define>BOOST_ALL_NO_LIB=1
<define>BOOST_BEAST_SPLIT_COMPILATION
<define>BOOST_ASIO_SEPARATE_COMPILATION
<define>BOOST_ASIO_NO_DEPRECATED=1 <define>BOOST_ASIO_NO_DEPRECATED=1
<define>BOOST_ASIO_DISABLE_BOOST_ARRAY=1 <define>BOOST_ASIO_DISABLE_BOOST_ARRAY=1
<define>BOOST_ASIO_DISABLE_BOOST_BIND=1 <define>BOOST_ASIO_DISABLE_BOOST_BIND=1
<define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1 <define>BOOST_ASIO_DISABLE_BOOST_DATE_TIME=1
<define>BOOST_ASIO_DISABLE_BOOST_REGEX=1 <define>BOOST_ASIO_DISABLE_BOOST_REGEX=1
<define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1 <define>BOOST_COROUTINES_NO_DEPRECATION_WARNING=1
#<define>BOOST_ASIO_SEPARATE_COMPILATION <define>BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1
<define>BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1 <define>BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1
<define>BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1
#<define>BOOST_BEAST_SEPARATE_COMPILATION
<toolset>msvc:<cxxflags>"/bigobj" <toolset>msvc:<cxxflags>"/bigobj"
<toolset>msvc-14.1:<cxxflags>"/permissive-" <toolset>msvc-14.1:<cxxflags>"/permissive-"
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1 <toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1

View File

@@ -151,11 +151,7 @@ receive_expect_100_continue(
// Read the rest of the message. // Read the rest of the message.
// //
// We use parser.base() to return a basic_parser&, to avoid an read(stream, buffer, parser, ec);
// 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);
} }
//] //]
@@ -875,14 +871,9 @@ do_form_request(
//[example_http_custom_parser //[example_http_custom_parser
template<bool isRequest> template<bool isRequest>
class custom_parser class custom_parser : public basic_parser<isRequest>
: public basic_parser<isRequest, custom_parser<isRequest>>
{ {
private: private:
// The friend declaration is needed,
// otherwise the callbacks must be made public.
friend class basic_parser<isRequest, custom_parser>;
/// Called after receiving the request-line (isRequest == true). /// Called after receiving the request-line (isRequest == true).
void void
on_request_impl( on_request_impl(
@@ -890,7 +881,7 @@ private:
string_view method_str, // The method as a string string_view method_str, // The method as a string
string_view target, // The request-target string_view target, // The request-target
int version, // The HTTP-version 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). /// Called after receiving the start-line (isRequest == false).
void void
@@ -898,7 +889,7 @@ private:
int code, // The status-code int code, // The status-code
string_view reason, // The obsolete reason-phrase string_view reason, // The obsolete reason-phrase
int version, // The HTTP-version 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. /// Called after receiving a header field.
void void
@@ -906,12 +897,12 @@ private:
field f, // The known-field enumeration constant field f, // The known-field enumeration constant
string_view name, // The field name string. string_view name, // The field name string.
string_view value, // The field value 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. /// Called after the complete header is received.
void void
on_header_impl( 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. /// Called just before processing the body, if a body exists.
void void
@@ -919,7 +910,7 @@ private:
boost::optional< boost::optional<
std::uint64_t> const& std::uint64_t> const&
content_length, // Content length if known, else `boost::none` 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. /// Called for each piece of the body, if a body exists.
//! //!
@@ -932,7 +923,7 @@ private:
std::size_t std::size_t
on_body_impl( on_body_impl(
string_view s, // A portion of the body 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. /// Called for each chunk header.
void void
@@ -940,7 +931,7 @@ private:
std::uint64_t size, // The size of the upcoming chunk, std::uint64_t size, // The size of the upcoming chunk,
// or zero for the last chunk // or zero for the last chunk
string_view extension, // The chunk extensions (may be empty) 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. /// Called to deliver the chunk body.
//! //!
@@ -958,11 +949,12 @@ private:
// including what is being passed here. // including what is being passed here.
// or zero for the last chunk // or zero for the last chunk
string_view body, // The next piece of the chunk body 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. /// Called when the complete message is parsed.
void void
on_finish_impl(error_code& ec); on_finish_impl(
error_code& ec) override; // The error returned to the caller, if any
public: public:
custom_parser() = default; custom_parser() = default;

View File

@@ -33,5 +33,8 @@ enum class error
} // boost } // boost
#include <boost/beast/_experimental/test/impl/error.hpp> #include <boost/beast/_experimental/test/impl/error.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/_experimental/test/impl/error.ipp>
#endif
#endif #endif

View File

@@ -63,6 +63,8 @@ public:
} // beast } // beast
} // boost } // boost
#include <boost/beast/_experimental/test/impl/fail_count.hpp> #ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/_experimental/test/impl/fail_count.ipp>
#endif
#endif #endif

View File

@@ -37,8 +37,4 @@ make_error_code(error e) noexcept;
} // beast } // beast
} // boost } // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/_experimental/test/impl/error.ipp>
#endif
#endif #endif

View File

@@ -7,9 +7,10 @@
// Official repository: https://github.com/boostorg/beast // Official repository: https://github.com/boostorg/beast
// //
#ifndef BOOST_BEAST_TEST_IMPL_FAIL_COUNT_HPP #ifndef BOOST_BEAST_TEST_IMPL_FAIL_COUNT_IPP
#define BOOST_BEAST_TEST_IMPL_FAIL_COUNT_HPP #define BOOST_BEAST_TEST_IMPL_FAIL_COUNT_IPP
#include <boost/beast/_experimental/test/fail_count.hpp>
#include <boost/throw_exception.hpp> #include <boost/throw_exception.hpp>
namespace boost { namespace boost {

View File

@@ -433,8 +433,4 @@ connect(stream& to, Arg1&& arg1, ArgN&&... argn)
} // beast } // beast
} // boost } // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/_experimental/test/impl/stream.ipp>
#endif
#endif #endif

View File

@@ -550,5 +550,8 @@ connect(stream& to, Arg1&& arg1, ArgN&&... argn);
} // boost } // boost
#include <boost/beast/_experimental/test/impl/stream.hpp> #include <boost/beast/_experimental/test/impl/stream.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/_experimental/test/impl/stream.ipp>
#endif
#endif #endif

View File

@@ -81,5 +81,8 @@ enum class condition
} // boost } // boost
#include <boost/beast/core/impl/error.hpp> #include <boost/beast/core/impl/error.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/error.ipp>
#endif
#endif #endif

View File

@@ -329,6 +329,7 @@ public:
} // beast } // beast
} // boost } // boost
#include <boost/beast/core/impl/flat_static_buffer.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY #ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/flat_static_buffer.ipp> #include <boost/beast/core/impl/flat_static_buffer.ipp>
#endif #endif

View File

@@ -41,8 +41,4 @@ make_error_condition(condition c);
} // beast } // beast
} // boost } // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/error.ipp>
#endif
#endif #endif

View File

@@ -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<std::size_t N>
flat_static_buffer<N>::
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<std::size_t N>
auto
flat_static_buffer<N>::
operator=(flat_static_buffer const& other) ->
flat_static_buffer<N>&
{
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

View File

@@ -83,31 +83,6 @@ reset(void* p, std::size_t n) noexcept
end_ = begin_ + n; end_ = begin_ + n;
} }
//------------------------------------------------------------------------------
template<std::size_t N>
flat_static_buffer<N>::
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<std::size_t N>
auto
flat_static_buffer<N>::
operator=(flat_static_buffer const& other) ->
flat_static_buffer<N>&
{
if(this == &other)
return *this;
this->consume(this->size());
this->commit(net::buffer_copy(
this->prepare(other.size()), other.data()));
return *this;
}
} // beast } // beast
} // boost } // boost

View File

@@ -47,133 +47,38 @@ namespace http {
the structured portion of the HTTP message (header or chunk header) the structured portion of the HTTP message (header or chunk header)
is contained in a linear buffer. is contained in a linear buffer.
The interface uses CRTP (Curiously Recurring Template Pattern). The interface to the parser uses virtual member functions.
To use this class directly, derive from @ref basic_parser. When To use this class, derive your type from @ref basic_parser. When
bytes are presented, the implementation will make a series of zero bytes are presented, the implementation will make a series of zero
or more calls to derived class members functions (termed "callbacks" or more calls to virtual functions, which the derived class must
in this context) matching a specific signature. implement.
Every callback must be provided by the derived class, or else Every virtual function must be provided by the derived class,
a compilation error will be generated. This exemplar shows or else a compilation error will be generated. The implementation
the signature and description of the callbacks required in will make sure that `ec` is clear before each virtual function
the derived class. is invoked. If a virtual function sets an error, it is propagated
For each callback, the function will ensure that `!ec` is `true` out of the parser to the caller.
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<bool isRequest>
class derived
: public basic_parser<isRequest, derived<isRequest>>
{
private:
// The friend declaration is needed,
// otherwise the callbacks must be made public.
friend class basic_parser<isRequest, derived>;
/// 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
@tparam isRequest A `bool` indicating whether the parser will be @tparam isRequest A `bool` indicating whether the parser will be
presented with request or response message. 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 @note If the parser encounters a field value with obs-fold
longer than 4 kilobytes in length, an error is generated. longer than 4 kilobytes in length, an error is generated.
*/ */
template<bool isRequest, class Derived> template<bool isRequest>
class basic_parser class basic_parser
: private detail::basic_parser_base : private detail::basic_parser_base
{ {
template<bool OtherIsRequest, class OtherDerived> std::uint64_t body_limit_ =
friend class basic_parser; default_body_limit(is_request{}); // max payload body
std::uint64_t len_ = 0; // size of chunk or body
std::unique_ptr<char[]> 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 // limit on the size of the stack flat buffer
static std::size_t constexpr max_stack_buffer = 8192; static std::size_t constexpr max_stack_buffer = 8192;
@@ -219,27 +124,13 @@ class basic_parser
return 8 * 1024 * 1024; // 8MB return 8 * 1024 * 1024; // 8MB
} }
std::uint64_t body_limit_ = template<bool OtherIsRequest>
default_body_limit(is_request{}); // max payload body friend class basic_parser;
std::uint64_t len_ = 0; // size of chunk or body
std::unique_ptr<char[]> 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
protected: protected:
/// Default constructor /// Default constructor
basic_parser() = default; basic_parser() = default;
/// Move constructor
basic_parser(basic_parser &&) = default;
/// Move assignment
basic_parser& operator=(basic_parser &&) = default;
/** Move constructor /** Move constructor
@note @note
@@ -247,8 +138,10 @@ protected:
After the move, the only valid operation on the After the move, the only valid operation on the
moved-from object is destruction. moved-from object is destruction.
*/ */
template<class OtherDerived> basic_parser(basic_parser &&) = default;
basic_parser(basic_parser<isRequest, OtherDerived>&&);
/// Move assignment
basic_parser& operator=(basic_parser &&) = default;
public: public:
/// `true` if this parser parses requests, `false` for responses. /// `true` if this parser parses requests, `false` for responses.
@@ -264,30 +157,6 @@ public:
/// Copy assignment /// Copy assignment
basic_parser& operator=(basic_parser const&) = delete; 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. /// Returns `true` if the parser has received at least one byte of input.
bool bool
got_some() const got_some() const
@@ -526,7 +395,7 @@ public:
#if ! BOOST_BEAST_DOXYGEN #if ! BOOST_BEAST_DOXYGEN
std::size_t std::size_t
put(net::const_buffer const& buffer, put(net::const_buffer buffer,
error_code& ec); error_code& ec);
#endif #endif
@@ -549,19 +418,205 @@ public:
void void
put_eof(error_code& ec); put_eof(error_code& ec);
private: protected:
inline /** Called after receiving the request-line.
Derived&
impl() 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<std::uint64_t> 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<Derived*>(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<class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
put_from_stack(std::size_t size, put_from_stack(
std::size_t size,
ConstBufferSequence const& buffers, ConstBufferSequence const& buffers,
error_code& ec); error_code& ec);
void void
maybe_need_more( maybe_need_more(
@@ -617,5 +672,8 @@ private:
} // boost } // boost
#include <boost/beast/http/impl/basic_parser.hpp> #include <boost/beast/http/impl/basic_parser.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/http/impl/basic_parser.ipp>
#endif
#endif #endif

View File

@@ -24,7 +24,7 @@ class header;
template<bool, class, class> template<bool, class, class>
class message; class message;
template<bool isRequest,class Body, class Fields> template<bool isRequest, class Body, class Fields>
class parser; class parser;
namespace detail { namespace detail {

View File

@@ -152,5 +152,8 @@ enum class error
} // boost } // boost
#include <boost/beast/http/impl/error.hpp> #include <boost/beast/http/impl/error.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/http/impl/error.ipp>
#endif
#endif #endif

View File

@@ -11,83 +11,17 @@
#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP
#include <boost/beast/core/buffer_size.hpp> #include <boost/beast/core/buffer_size.hpp>
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/http/error.hpp>
#include <boost/beast/http/rfc7230.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/make_unique.hpp> #include <boost/make_unique.hpp>
#include <algorithm>
#include <utility>
namespace boost { namespace boost {
namespace beast { namespace beast {
namespace http { namespace http {
template<bool isRequest, class Derived> template<bool isRequest>
template<class OtherDerived>
basic_parser<isRequest, Derived>::
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 isRequest, class Derived>
bool
basic_parser<isRequest, Derived>::
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, class Derived>
boost::optional<std::uint64_t>
basic_parser<isRequest, Derived>::
content_length() const
{
BOOST_ASSERT(is_header_done());
if(! (f_ & flagContentLength))
return boost::none;
return len_;
}
template<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
skip(bool v)
{
BOOST_ASSERT(! got_some());
if(v)
f_ |= flagSkipBody;
else
f_ &= ~flagSkipBody;
}
template<bool isRequest, class Derived>
template<class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
basic_parser<isRequest, Derived>:: basic_parser<isRequest>::
put(ConstBufferSequence const& buffers, put(ConstBufferSequence const& buffers,
error_code& ec) error_code& ec)
{ {
@@ -122,180 +56,10 @@ put(ConstBufferSequence const& buffers,
buf_.get(), buf_len_}, ec); buf_.get(), buf_len_}, ec);
} }
template<bool isRequest, class Derived> template<bool isRequest>
std::size_t
basic_parser<isRequest, Derived>::
put(net::const_buffer const& buffer,
error_code& ec)
{
BOOST_ASSERT(state_ != state::complete);
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)
{
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_)
{
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)
{
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_)
{
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<std::size_t>(p1 - p);
goto loop;
}
done:
return static_cast<std::size_t>(p - p0);
}
template<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
template<class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
basic_parser<isRequest, Derived>:: basic_parser<isRequest>::
put_from_stack(std::size_t size, put_from_stack(std::size_t size,
ConstBufferSequence const& buffers, ConstBufferSequence const& buffers,
error_code& ec) error_code& ec)
@@ -307,615 +71,6 @@ put_from_stack(std::size_t size,
buf, size}, ec); buf, size}, ec);
} }
template<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
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<max_obs_fold> 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<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
inline
void
basic_parser<isRequest, Derived>::
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<bool isRequest, class Derived>
void
basic_parser<isRequest, Derived>::
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 } // http
} // beast } // beast
} // boost } // boost

View File

@@ -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 <boost/beast/http/basic_parser.hpp>
#include <boost/beast/http/error.hpp>
#include <boost/beast/http/rfc7230.hpp>
#include <boost/beast/core/buffer_size.hpp>
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/core/detail/config.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/make_unique.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());
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)
{
BOOST_ASSERT(state_ != state::complete);
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)
{
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_)
{
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)
{
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_)
{
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<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)
{
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<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)
{
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<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)
{
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<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)
{
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<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
static_string<max_obs_fold> 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<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(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<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_ > 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<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(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<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)
{
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<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)
{
// 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<true>;
template class http::basic_parser<false>;
#endif
} // http
} // beast
} // boost
#endif

View File

@@ -34,8 +34,4 @@ make_error_code(error ev);
} // beast } // beast
} // boost } // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/http/impl/error.ipp>
#endif
#endif #endif

View File

@@ -42,7 +42,7 @@ parser<isRequest, Body, Allocator>::
parser( parser(
parser<isRequest, OtherBody, Allocator>&& other, parser<isRequest, OtherBody, Allocator>&& other,
Args&&... args) Args&&... args)
: base_type(std::move(other)) : basic_parser<isRequest>(std::move(other))
, m_(other.release(), std::forward<Args>(args)...) , m_(other.release(), std::forward<Args>(args)...)
, rd_(m_.base(), m_.body()) , rd_(m_.base(), m_.body())
{ {

View File

@@ -30,12 +30,12 @@ std::size_t constexpr default_max_transfer_size = 65536;
template< template<
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived, bool isRequest,
class Condition> class Condition>
std::size_t std::size_t
parse_until( parse_until(
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
error_code& ec, error_code& ec,
Condition cond) Condition cond)
{ {
@@ -92,10 +92,10 @@ parse_until(
} }
// predicate is true on any forward parser progress // predicate is true on any forward parser progress
template<bool isRequest, class Derived> template<bool isRequest>
struct read_some_condition struct read_some_condition
{ {
basic_parser<isRequest, Derived>& parser; basic_parser<isRequest>& parser;
template<class DynamicBuffer> template<class DynamicBuffer>
std::size_t std::size_t
@@ -112,10 +112,10 @@ struct read_some_condition
}; };
// predicate is true when parser header is complete // predicate is true when parser header is complete
template<bool isRequest, class Derived> template<bool isRequest>
struct read_header_condition struct read_header_condition
{ {
basic_parser<isRequest, Derived>& parser; basic_parser<isRequest>& parser;
template<class DynamicBuffer> template<class DynamicBuffer>
std::size_t std::size_t
@@ -132,10 +132,10 @@ struct read_header_condition
}; };
// predicate is true when parser message is complete // predicate is true when parser message is complete
template<bool isRequest, class Derived> template<bool isRequest>
struct read_all_condition struct read_all_condition
{ {
basic_parser<isRequest, Derived>& parser; basic_parser<isRequest>& parser;
template<class DynamicBuffer> template<class DynamicBuffer>
std::size_t std::size_t
@@ -256,12 +256,12 @@ struct run_read_msg_op
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read_some( read_some(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser) basic_parser<isRequest>& parser)
{ {
static_assert( static_assert(
is_sync_read_stream<SyncReadStream>::value, is_sync_read_stream<SyncReadStream>::value,
@@ -271,7 +271,7 @@ read_some(
"DynamicBuffer type requirements not met"); "DynamicBuffer type requirements not met");
error_code ec; error_code ec;
auto const bytes_transferred = auto const bytes_transferred =
read_some(stream, buffer, parser, ec); http::read_some(stream, buffer, parser, ec);
if(ec) if(ec)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred; return bytes_transferred;
@@ -280,12 +280,12 @@ read_some(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read_some( read_some(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
error_code& ec) error_code& ec)
{ {
static_assert( static_assert(
@@ -296,27 +296,27 @@ read_some(
"DynamicBuffer type requirements not met"); "DynamicBuffer type requirements not met");
return beast::detail::read(stream, buffer, return beast::detail::read(stream, buffer,
detail::read_some_condition< detail::read_some_condition<
isRequest, Derived>{parser}, ec); isRequest>{parser}, ec);
} }
template< template<
class AsyncReadStream, class AsyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived, bool isRequest,
class ReadHandler> class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
async_read_some( async_read_some(
AsyncReadStream& stream, AsyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
ReadHandler&& handler) ReadHandler&& handler)
{ {
return beast::detail::async_read( return beast::detail::async_read(
stream, stream,
buffer, buffer,
detail::read_some_condition< detail::read_some_condition<
isRequest, Derived>{parser}, isRequest>{parser},
std::forward<ReadHandler>(handler)); std::forward<ReadHandler>(handler));
} }
@@ -325,12 +325,12 @@ async_read_some(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read_header( read_header(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser) basic_parser<isRequest>& parser)
{ {
static_assert( static_assert(
is_sync_read_stream<SyncReadStream>::value, is_sync_read_stream<SyncReadStream>::value,
@@ -340,7 +340,7 @@ read_header(
"DynamicBuffer type requirements not met"); "DynamicBuffer type requirements not met");
error_code ec; error_code ec;
auto const bytes_transferred = auto const bytes_transferred =
read_header(stream, buffer, parser, ec); http::read_header(stream, buffer, parser, ec);
if(ec) if(ec)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred; return bytes_transferred;
@@ -349,12 +349,12 @@ read_header(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read_header( read_header(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
error_code& ec) error_code& ec)
{ {
static_assert( static_assert(
@@ -366,20 +366,20 @@ read_header(
parser.eager(false); parser.eager(false);
return beast::detail::read(stream, buffer, return beast::detail::read(stream, buffer,
detail::read_header_condition< detail::read_header_condition<
isRequest, Derived>{parser}, ec); isRequest>{parser}, ec);
} }
template< template<
class AsyncReadStream, class AsyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived, bool isRequest,
class ReadHandler> class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
async_read_header( async_read_header(
AsyncReadStream& stream, AsyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
ReadHandler&& handler) ReadHandler&& handler)
{ {
parser.eager(false); parser.eager(false);
@@ -387,7 +387,7 @@ async_read_header(
stream, stream,
buffer, buffer,
detail::read_header_condition< detail::read_header_condition<
isRequest, Derived>{parser}, isRequest>{parser},
std::forward<ReadHandler>(handler)); std::forward<ReadHandler>(handler));
} }
@@ -396,12 +396,12 @@ async_read_header(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read( read(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser) basic_parser<isRequest>& parser)
{ {
static_assert( static_assert(
is_sync_read_stream<SyncReadStream>::value, is_sync_read_stream<SyncReadStream>::value,
@@ -411,7 +411,7 @@ read(
"DynamicBuffer type requirements not met"); "DynamicBuffer type requirements not met");
error_code ec; error_code ec;
auto const bytes_transferred = auto const bytes_transferred =
read(stream, buffer, parser, ec); http::read(stream, buffer, parser, ec);
if(ec) if(ec)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred; return bytes_transferred;
@@ -420,12 +420,12 @@ read(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read( read(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
error_code& ec) error_code& ec)
{ {
static_assert( static_assert(
@@ -437,20 +437,20 @@ read(
parser.eager(true); parser.eager(true);
return beast::detail::read(stream, buffer, return beast::detail::read(stream, buffer,
detail::read_all_condition< detail::read_all_condition<
isRequest, Derived>{parser}, ec); isRequest>{parser}, ec);
} }
template< template<
class AsyncReadStream, class AsyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived, bool isRequest,
class ReadHandler> class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
async_read( async_read(
AsyncReadStream& stream, AsyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
ReadHandler&& handler) ReadHandler&& handler)
{ {
static_assert( static_assert(
@@ -464,7 +464,7 @@ async_read(
stream, stream,
buffer, buffer,
detail::read_all_condition< detail::read_all_condition<
isRequest, Derived>{parser}, isRequest>{parser},
std::forward<ReadHandler>(handler)); std::forward<ReadHandler>(handler));
} }
@@ -492,7 +492,7 @@ read(
"BodyReader type requirements not met"); "BodyReader type requirements not met");
error_code ec; error_code ec;
auto const bytes_transferred = auto const bytes_transferred =
read(stream, buffer, msg, ec); http::read(stream, buffer, msg, ec);
if(ec) if(ec)
BOOST_THROW_EXCEPTION(system_error{ec}); BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred; return bytes_transferred;
@@ -522,7 +522,7 @@ read(
parser<isRequest, Body, Allocator> p(std::move(msg)); parser<isRequest, Body, Allocator> p(std::move(msg));
p.eager(true); p.eager(true);
auto const bytes_transferred = auto const bytes_transferred =
read(stream, buffer, p.base(), ec); http::read(stream, buffer, p, ec);
if(ec) if(ec)
return bytes_transferred; return bytes_transferred;
msg = p.release(); msg = p.release();

View File

@@ -47,8 +47,7 @@ template<
class Body, class Body,
class Allocator = std::allocator<char>> class Allocator = std::allocator<char>>
class parser class parser
: public basic_parser<isRequest, : public basic_parser<isRequest>
parser<isRequest, Body, Allocator>>
{ {
static_assert(is_body<Body>::value, static_assert(is_body<Body>::value,
"Body type requirements not met"); "Body type requirements not met");
@@ -59,9 +58,6 @@ class parser
template<bool, class, class> template<bool, class, class>
friend class parser; friend class parser;
using base_type = basic_parser<isRequest,
parser<isRequest, Body, Allocator>>;
message<isRequest, Body, basic_fields<Allocator>> m_; message<isRequest, Body, basic_fields<Allocator>> m_;
typename Body::reader rd_; typename Body::reader rd_;
bool rd_inited_ = false; bool rd_inited_ = false;
@@ -297,8 +293,6 @@ public:
} }
private: private:
friend class basic_parser<isRequest, parser>;
parser(std::true_type); parser(std::true_type);
parser(std::false_type); parser(std::false_type);
@@ -338,7 +332,8 @@ private:
string_view method_str, string_view method_str,
string_view target, string_view target,
int version, int version,
error_code& ec) error_code& ec,
std::true_type)
{ {
try try
{ {
@@ -347,7 +342,6 @@ private:
m_.method(method); m_.method(method);
else else
m_.method_string(method_str); m_.method_string(method_str);
ec = {};
} }
catch(std::bad_alloc const&) catch(std::bad_alloc const&)
{ {
@@ -356,19 +350,39 @@ private:
m_.version(version); 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<bool, isRequest>{});
}
void void
on_response_impl( on_response_impl(
int code, int code,
string_view reason, string_view reason,
int version, int version,
error_code& ec) error_code& ec,
std::true_type)
{ {
m_.result(code); m_.result(code);
m_.version(version); m_.version(version);
try try
{ {
m_.reason(reason); m_.reason(reason);
ec = {};
} }
catch(std::bad_alloc const&) 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<bool, ! isRequest>{});
}
void void
on_field_impl( on_field_impl(
field name, field name,
string_view name_string, string_view name_string,
string_view value, string_view value,
error_code& ec) error_code& ec) override
{ {
try try
{ {
m_.insert(name, name_string, value); m_.insert(name, name_string, value);
ec = {};
} }
catch(std::bad_alloc const&) catch(std::bad_alloc const&)
{ {
@@ -395,7 +427,7 @@ private:
} }
void void
on_header_impl(error_code& ec) on_header_impl(error_code& ec) override
{ {
ec = {}; ec = {};
} }
@@ -403,7 +435,7 @@ private:
void void
on_body_init_impl( on_body_init_impl(
boost::optional<std::uint64_t> const& content_length, boost::optional<std::uint64_t> const& content_length,
error_code& ec) error_code& ec) override
{ {
rd_.init(content_length, ec); rd_.init(content_length, ec);
rd_inited_ = true; rd_inited_ = true;
@@ -412,7 +444,7 @@ private:
std::size_t std::size_t
on_body_impl( on_body_impl(
string_view body, string_view body,
error_code& ec) error_code& ec) override
{ {
return rd_.put(net::buffer( return rd_.put(net::buffer(
body.data(), body.size()), ec); body.data(), body.size()), ec);
@@ -422,18 +454,17 @@ private:
on_chunk_header_impl( on_chunk_header_impl(
std::uint64_t size, std::uint64_t size,
string_view extensions, string_view extensions,
error_code& ec) error_code& ec) override
{ {
if(cb_h_) if(cb_h_)
return cb_h_(size, extensions, ec); return cb_h_(size, extensions, ec);
ec = {};
} }
std::size_t std::size_t
on_chunk_body_impl( on_chunk_body_impl(
std::uint64_t remain, std::uint64_t remain,
string_view body, string_view body,
error_code& ec) error_code& ec) override
{ {
if(cb_b_) if(cb_b_)
return cb_b_(remain, body, ec); return cb_b_(remain, body, ec);
@@ -442,7 +473,8 @@ private:
} }
void void
on_finish_impl(error_code& ec) on_finish_impl(
error_code& ec) override
{ {
rd_.finish(ec); rd_.finish(ec);
} }

View File

@@ -72,12 +72,12 @@ namespace http {
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read_some( read_some(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser); basic_parser<isRequest>& parser);
/** Read part of a message from a stream using a parser. /** Read part of a message from a stream using a parser.
@@ -129,12 +129,12 @@ read_some(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read_some( read_some(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
error_code& ec); error_code& ec);
/** Read part of a message asynchronously from a stream using a parser. /** Read part of a message asynchronously from a stream using a parser.
@@ -205,14 +205,14 @@ read_some(
template< template<
class AsyncReadStream, class AsyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived, bool isRequest,
class ReadHandler> class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
async_read_some( async_read_some(
AsyncReadStream& stream, AsyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
ReadHandler&& handler); ReadHandler&& handler);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -267,12 +267,12 @@ async_read_some(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read_header( read_header(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser); basic_parser<isRequest>& parser);
/** Read a complete message header from a stream using a parser. /** Read a complete message header from a stream using a parser.
@@ -324,12 +324,12 @@ read_header(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read_header( read_header(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
error_code& ec); error_code& ec);
/** Read a complete message header asynchronously from a stream using a parser. /** Read a complete message header asynchronously from a stream using a parser.
@@ -401,14 +401,14 @@ read_header(
template< template<
class AsyncReadStream, class AsyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived, bool isRequest,
class ReadHandler> class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
async_read_header( async_read_header(
AsyncReadStream& stream, AsyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
ReadHandler&& handler); ReadHandler&& handler);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -463,12 +463,12 @@ async_read_header(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read( read(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser); basic_parser<isRequest>& parser);
/** Read a complete message from a stream using a parser. /** Read a complete message from a stream using a parser.
@@ -520,12 +520,12 @@ read(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived> bool isRequest>
std::size_t std::size_t
read( read(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
error_code& ec); error_code& ec);
/** Read a complete message asynchronously from a stream using a parser. /** Read a complete message asynchronously from a stream using a parser.
@@ -597,14 +597,14 @@ read(
template< template<
class AsyncReadStream, class AsyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, class Derived, bool isRequest,
class ReadHandler> class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE( BOOST_ASIO_INITFN_RESULT_TYPE(
ReadHandler, void(error_code, std::size_t)) ReadHandler, void(error_code, std::size_t))
async_read( async_read(
AsyncReadStream& stream, AsyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
ReadHandler&& handler); ReadHandler&& handler);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -26,6 +26,7 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined.
#endif #endif
#include <boost/beast/_experimental/test/impl/error.ipp> #include <boost/beast/_experimental/test/impl/error.ipp>
#include <boost/beast/_experimental/test/impl/fail_count.ipp>
#include <boost/beast/_experimental/test/impl/stream.ipp> #include <boost/beast/_experimental/test/impl/stream.ipp>
#include <boost/beast/core/detail/base64.ipp> #include <boost/beast/core/detail/base64.ipp>
@@ -38,6 +39,7 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined.
#include <boost/beast/core/impl/saved_handler.ipp> #include <boost/beast/core/impl/saved_handler.ipp>
#include <boost/beast/core/impl/static_buffer.ipp> #include <boost/beast/core/impl/static_buffer.ipp>
#include <boost/beast/http/impl/basic_parser.ipp>
#include <boost/beast/http/impl/error.ipp> #include <boost/beast/http/impl/error.ipp>
#include <boost/beast/http/impl/field.ipp> #include <boost/beast/http/impl/field.ipp>
#include <boost/beast/http/impl/status.ipp> #include <boost/beast/http/impl/status.ipp>

View File

@@ -249,5 +249,8 @@ enum class condition
} // boost } // boost
#include <boost/beast/websocket/impl/error.hpp> #include <boost/beast/websocket/impl/error.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/websocket/impl/error.ipp>
#endif
#endif #endif

View File

@@ -41,8 +41,4 @@ make_error_condition(condition c);
} // beast } // beast
} // boost } // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/websocket/impl/error.ipp>
#endif
#endif #endif

View File

@@ -133,6 +133,9 @@ enum class error
} // boost } // boost
#include <boost/beast/zlib/impl/error.hpp> #include <boost/beast/zlib/impl/error.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/zlib/impl/error.ipp>
#endif
#endif #endif

View File

@@ -60,8 +60,4 @@ make_error_code(error ev);
} // beast } // beast
} // boost } // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/zlib/impl/error.ipp>
#endif
#endif #endif

View File

@@ -894,10 +894,10 @@ public:
return {s.data(), s.size()}; return {s.data(), s.size()};
} }
template<class ConstBufferSequence, bool isRequest, class Derived> template<class ConstBufferSequence, bool isRequest>
std::size_t std::size_t
feed(ConstBufferSequence const& buffers, feed(ConstBufferSequence const& buffers,
basic_parser<isRequest, Derived>& p, error_code& ec) basic_parser<isRequest>& p, error_code& ec)
{ {
p.eager(true); p.eager(true);
return p.put(buffers, ec); return p.put(buffers, ec);

View File

@@ -45,11 +45,11 @@ public:
} }
template<class ConstBufferSequence, template<class ConstBufferSequence,
bool isRequest, class Derived> bool isRequest>
static static
void void
put(ConstBufferSequence const& buffers, put(ConstBufferSequence const& buffers,
basic_parser<isRequest, Derived>& p, basic_parser<isRequest>& p,
error_code& ec) error_code& ec)
{ {
buffers_suffix<ConstBufferSequence> cb{buffers}; buffers_suffix<ConstBufferSequence> cb{buffers};

View File

@@ -21,7 +21,7 @@ namespace http {
template<bool isRequest> template<bool isRequest>
class test_parser class test_parser
: public basic_parser<isRequest, test_parser<isRequest>> : public basic_parser<isRequest>
{ {
test::fail_count* fc_ = nullptr; test::fail_count* fc_ = nullptr;
@@ -65,8 +65,6 @@ public:
++got_on_begin; ++got_on_begin;
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
} }
void void
@@ -81,8 +79,6 @@ public:
++got_on_begin; ++got_on_begin;
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
} }
void void
@@ -92,8 +88,6 @@ public:
++got_on_field; ++got_on_field;
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
fields[std::string(name)] = std::string(value); fields[std::string(name)] = std::string(value);
} }
@@ -103,8 +97,6 @@ public:
++got_on_header; ++got_on_header;
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
} }
void void
@@ -117,8 +109,6 @@ public:
static_cast<bool>(content_length_); static_cast<bool>(content_length_);
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
} }
std::size_t std::size_t
@@ -128,8 +118,6 @@ public:
body.append(s.data(), s.size()); body.append(s.data(), s.size());
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
return s.size(); return s.size();
} }
@@ -142,8 +130,6 @@ public:
++got_on_chunk; ++got_on_chunk;
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
} }
std::size_t std::size_t
@@ -155,8 +141,6 @@ public:
body.append(s.data(), s.size()); body.append(s.data(), s.size());
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
return s.size(); return s.size();
} }
@@ -167,8 +151,6 @@ public:
++got_on_complete; ++got_on_complete;
if(fc_) if(fc_)
fc_->fail(ec); fc_->fail(ec);
else
ec = {};
} }
}; };

View File

@@ -70,11 +70,11 @@ public:
} }
template<class ConstBufferSequence, template<class ConstBufferSequence,
bool isRequest, class Derived> bool isRequest>
static static
std::size_t std::size_t
feed(ConstBufferSequence const& buffers, feed(ConstBufferSequence const& buffers,
basic_parser<isRequest, Derived>& parser, basic_parser<isRequest>& parser,
error_code& ec) error_code& ec)
{ {
beast::buffers_suffix< beast::buffers_suffix<
@@ -149,79 +149,131 @@ public:
template<bool isRequest> template<bool isRequest>
struct null_parser : struct null_parser :
basic_parser<isRequest, null_parser<isRequest>> basic_parser<isRequest>
{ {
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<std::uint64_t> 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<bool isRequest, class Body, class Fields> template<bool isRequest, class Body, class Fields>
struct bench_parser : basic_parser< struct bench_parser : basic_parser<isRequest>
isRequest, bench_parser<isRequest, Body, Fields>>
{ {
using mutable_buffers_type = using mutable_buffers_type =
net::mutable_buffer; net::mutable_buffer;
void void
on_request_impl(verb, string_view, on_request_impl(verb, string_view,
string_view, int, error_code& ec) string_view, int, error_code&) override
{ {
ec = {};
} }
void void
on_response_impl(int, on_response_impl(int,
string_view, string_view, int, error_code&) override
int, error_code& ec)
{ {
ec = {};
} }
void void
on_field_impl(field, on_field_impl(field,
string_view, string_view, error_code& ec) string_view, string_view, error_code&) override
{ {
ec = {};
} }
void void
on_header_impl(error_code& ec) on_header_impl(error_code&) override
{ {
ec = {};
} }
void void
on_body_init_impl( on_body_init_impl(
boost::optional<std::uint64_t> const&, boost::optional<std::uint64_t> const&,
error_code& ec) error_code&) override
{ {
ec = {};
} }
std::size_t 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(); return s.size();
} }
void void
on_chunk_header_impl(std::uint64_t, on_chunk_header_impl(std::uint64_t,
string_view, error_code& ec) string_view, error_code&) override
{ {
ec = {};
} }
std::size_t std::size_t
on_chunk_body_impl(std::uint64_t, on_chunk_body_impl(std::uint64_t,
string_view s, error_code& ec) string_view s, error_code&) override
{ {
ec = {};
return s.size(); return s.size();
} }
void void
on_finish_impl(error_code& ec) on_finish_impl(error_code&) override
{ {
ec = {};
} }
}; };