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
* Better treatment of SSL short reads
* 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 ;
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
: test/lib_beast.cpp
test/lib_asio.cpp
: requirements
[ requires
cxx11_constexpr
@@ -83,12 +63,14 @@ lib static_beast
cxx11_variadic_templates
]
<define>BOOST_BEAST_SPLIT_COMPILATION
<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
<toolset>msvc-14.1:<cxxflags>"/permissive-"
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
<toolset>msvc:<define>_SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING
@@ -103,7 +85,7 @@ project /boost/beast
<include>./test/extras/include
<library>/boost/coroutine//boost_coroutine
<library>/boost/filesystem//boost_filesystem
<library>static_asio
#<library>static_asio
<library>static_beast
<implicit-dependency>/boost//headers
<threading>multi
@@ -112,16 +94,16 @@ project /boost/beast
[ ac.check-library /boost/beast//ssl : <library>/boost/beast//ssl : <build>no ]
<library>/boost/beast//crypto
<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_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
#<define>BOOST_ASIO_SEPARATE_COMPILATION
<define>BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1
<define>BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1
#<define>BOOST_BEAST_SEPARATE_COMPILATION
<define>BOOST_ASIO_DISABLE_WINDOWS_RANDOM_ACCESS_HANDLE=1
<define>BOOST_ASIO_DISABLE_WINDOWS_STREAM_HANDLE=1
<toolset>msvc:<cxxflags>"/bigobj"
<toolset>msvc-14.1:<cxxflags>"/permissive-"
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1

View File

@@ -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<bool isRequest>
class custom_parser
: public basic_parser<isRequest, custom_parser<isRequest>>
class custom_parser : public basic_parser<isRequest>
{
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).
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;

View File

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

View File

@@ -63,6 +63,8 @@ public:
} // beast
} // 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

View File

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

View File

@@ -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 <boost/beast/_experimental/test/fail_count.hpp>
#include <boost/throw_exception.hpp>
namespace boost {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<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
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<bool isRequest, class Derived>
template<bool isRequest>
class basic_parser
: private detail::basic_parser_base
{
template<bool OtherIsRequest, class OtherDerived>
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<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
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<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
template<bool OtherIsRequest>
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<class OtherDerived>
basic_parser(basic_parser<isRequest, OtherDerived>&&);
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,17 +418,203 @@ 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<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>
std::size_t
put_from_stack(std::size_t size,
put_from_stack(
std::size_t size,
ConstBufferSequence const& buffers,
error_code& ec);
@@ -617,5 +672,8 @@ private:
} // boost
#include <boost/beast/http/impl/basic_parser.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/http/impl/basic_parser.ipp>
#endif
#endif

View File

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

View File

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

View File

@@ -11,83 +11,17 @@
#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_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/make_unique.hpp>
#include <algorithm>
#include <utility>
namespace boost {
namespace beast {
namespace http {
template<bool isRequest, class Derived>
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<bool isRequest>
template<class ConstBufferSequence>
std::size_t
basic_parser<isRequest, Derived>::
basic_parser<isRequest>::
put(ConstBufferSequence const& buffers,
error_code& ec)
{
@@ -122,180 +56,10 @@ put(ConstBufferSequence const& buffers,
buf_.get(), buf_len_}, ec);
}
template<bool isRequest, class Derived>
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<bool isRequest>
template<class ConstBufferSequence>
std::size_t
basic_parser<isRequest, Derived>::
basic_parser<isRequest>::
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<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
} // beast
} // 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
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/http/impl/error.ipp>
#endif
#endif

View File

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

View File

@@ -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<isRequest, Derived>& parser,
basic_parser<isRequest>& parser,
error_code& ec,
Condition cond)
{
@@ -92,10 +92,10 @@ parse_until(
}
// predicate is true on any forward parser progress
template<bool isRequest, class Derived>
template<bool isRequest>
struct read_some_condition
{
basic_parser<isRequest, Derived>& parser;
basic_parser<isRequest>& parser;
template<class DynamicBuffer>
std::size_t
@@ -112,10 +112,10 @@ struct read_some_condition
};
// predicate is true when parser header is complete
template<bool isRequest, class Derived>
template<bool isRequest>
struct read_header_condition
{
basic_parser<isRequest, Derived>& parser;
basic_parser<isRequest>& parser;
template<class DynamicBuffer>
std::size_t
@@ -132,10 +132,10 @@ struct read_header_condition
};
// predicate is true when parser message is complete
template<bool isRequest, class Derived>
template<bool isRequest>
struct read_all_condition
{
basic_parser<isRequest, Derived>& parser;
basic_parser<isRequest>& parser;
template<class DynamicBuffer>
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<isRequest, Derived>& parser)
basic_parser<isRequest>& parser)
{
static_assert(
is_sync_read_stream<SyncReadStream>::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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& parser,
ReadHandler&& handler)
{
return beast::detail::async_read(
stream,
buffer,
detail::read_some_condition<
isRequest, Derived>{parser},
isRequest>{parser},
std::forward<ReadHandler>(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<isRequest, Derived>& parser)
basic_parser<isRequest>& parser)
{
static_assert(
is_sync_read_stream<SyncReadStream>::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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<ReadHandler>(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<isRequest, Derived>& parser)
basic_parser<isRequest>& parser)
{
static_assert(
is_sync_read_stream<SyncReadStream>::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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& parser,
ReadHandler&& handler)
{
static_assert(
@@ -464,7 +464,7 @@ async_read(
stream,
buffer,
detail::read_all_condition<
isRequest, Derived>{parser},
isRequest>{parser},
std::forward<ReadHandler>(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<isRequest, Body, Allocator> 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();

View File

@@ -47,8 +47,7 @@ template<
class Body,
class Allocator = std::allocator<char>>
class parser
: public basic_parser<isRequest,
parser<isRequest, Body, Allocator>>
: public basic_parser<isRequest>
{
static_assert(is_body<Body>::value,
"Body type requirements not met");
@@ -59,9 +58,6 @@ class parser
template<bool, class, class>
friend class parser;
using base_type = basic_parser<isRequest,
parser<isRequest, Body, Allocator>>;
message<isRequest, Body, basic_fields<Allocator>> m_;
typename Body::reader rd_;
bool rd_inited_ = false;
@@ -297,8 +293,6 @@ public:
}
private:
friend class basic_parser<isRequest, 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<bool, isRequest>{});
}
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<bool, ! isRequest>{});
}
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<std::uint64_t> 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);
}

View File

@@ -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<isRequest, Derived>& parser);
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<isRequest, Derived>& parser);
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<isRequest, Derived>& parser);
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& 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<isRequest, Derived>& parser,
basic_parser<isRequest>& parser,
ReadHandler&& handler);
//------------------------------------------------------------------------------

View File

@@ -26,6 +26,7 @@ the program, with the macro BOOST_BEAST_SPLIT_COMPILATION defined.
#endif
#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/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/static_buffer.ipp>
#include <boost/beast/http/impl/basic_parser.ipp>
#include <boost/beast/http/impl/error.ipp>
#include <boost/beast/http/impl/field.ipp>
#include <boost/beast/http/impl/status.ipp>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ namespace http {
template<bool isRequest>
class test_parser
: public basic_parser<isRequest, test_parser<isRequest>>
: public basic_parser<isRequest>
{
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<bool>(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 = {};
}
};

View File

@@ -70,11 +70,11 @@ public:
}
template<class ConstBufferSequence,
bool isRequest, class Derived>
bool isRequest>
static
std::size_t
feed(ConstBufferSequence const& buffers,
basic_parser<isRequest, Derived>& parser,
basic_parser<isRequest>& parser,
error_code& ec)
{
beast::buffers_suffix<
@@ -149,79 +149,131 @@ public:
template<bool isRequest>
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>
struct bench_parser : basic_parser<
isRequest, bench_parser<isRequest, Body, Fields>>
struct bench_parser : basic_parser<isRequest>
{
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<std::uint64_t> 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 = {};
}
};