From 3c1df2168531ad5580076ae08d529054689aeedd Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 5 Dec 2019 15:22:41 +0900 Subject: Imported Upstream version 1.70.0 --- boost/beast/http/basic_dynamic_body.hpp | 34 +- boost/beast/http/basic_file_body.hpp | 27 +- boost/beast/http/basic_parser.hpp | 419 +++++--- boost/beast/http/buffer_body.hpp | 35 +- boost/beast/http/chunk_encode.hpp | 77 +- boost/beast/http/detail/basic_parsed_list.hpp | 4 +- boost/beast/http/detail/basic_parser.hpp | 813 +------------- boost/beast/http/detail/basic_parser.ipp | 848 +++++++++++++++ boost/beast/http/detail/chunk_encode.hpp | 61 +- boost/beast/http/detail/rfc7230.hpp | 421 +------- boost/beast/http/detail/rfc7230.ipp | 388 +++++++ boost/beast/http/detail/type_traits.hpp | 10 +- boost/beast/http/dynamic_body.hpp | 4 +- boost/beast/http/empty_body.hpp | 22 +- boost/beast/http/error.hpp | 20 +- boost/beast/http/field.hpp | 6 +- boost/beast/http/fields.hpp | 17 +- boost/beast/http/file_body.hpp | 6 +- boost/beast/http/impl/basic_parser.hpp | 78 ++ boost/beast/http/impl/basic_parser.ipp | 234 ++-- boost/beast/http/impl/chunk_encode.hpp | 706 +++++++++++++ boost/beast/http/impl/chunk_encode.ipp | 707 ------------- boost/beast/http/impl/error.hpp | 37 + boost/beast/http/impl/error.ipp | 14 +- boost/beast/http/impl/field.ipp | 174 ++- boost/beast/http/impl/fields.hpp | 1405 +++++++++++++++++++++++++ boost/beast/http/impl/fields.ipp | 1399 ------------------------ boost/beast/http/impl/file_body_win32.hpp | 573 ++++++++++ boost/beast/http/impl/file_body_win32.ipp | 599 ----------- boost/beast/http/impl/message.hpp | 429 ++++++++ boost/beast/http/impl/message.ipp | 437 -------- boost/beast/http/impl/parser.hpp | 58 + boost/beast/http/impl/parser.ipp | 58 - boost/beast/http/impl/read.hpp | 559 ++++++++++ boost/beast/http/impl/read.ipp | 857 --------------- boost/beast/http/impl/rfc7230.hpp | 423 ++++++++ boost/beast/http/impl/rfc7230.ipp | 408 +------ boost/beast/http/impl/serializer.hpp | 431 ++++++++ boost/beast/http/impl/serializer.ipp | 432 -------- boost/beast/http/impl/status.ipp | 80 +- boost/beast/http/impl/verb.ipp | 27 +- boost/beast/http/impl/write.hpp | 950 +++++++++++++++++ boost/beast/http/impl/write.ipp | 1003 ------------------ boost/beast/http/message.hpp | 79 +- boost/beast/http/parser.hpp | 150 ++- boost/beast/http/read.hpp | 850 ++++++++------- boost/beast/http/rfc7230.hpp | 10 +- boost/beast/http/serializer.hpp | 39 +- boost/beast/http/span_body.hpp | 31 +- boost/beast/http/status.hpp | 18 +- boost/beast/http/string_body.hpp | 56 +- boost/beast/http/type_traits.hpp | 67 +- boost/beast/http/vector_body.hpp | 53 +- boost/beast/http/verb.hpp | 6 +- boost/beast/http/write.hpp | 170 +-- 55 files changed, 8395 insertions(+), 8424 deletions(-) create mode 100644 boost/beast/http/detail/basic_parser.ipp create mode 100644 boost/beast/http/detail/rfc7230.ipp create mode 100644 boost/beast/http/impl/basic_parser.hpp create mode 100644 boost/beast/http/impl/chunk_encode.hpp delete mode 100644 boost/beast/http/impl/chunk_encode.ipp create mode 100644 boost/beast/http/impl/error.hpp create mode 100644 boost/beast/http/impl/fields.hpp delete mode 100644 boost/beast/http/impl/fields.ipp create mode 100644 boost/beast/http/impl/file_body_win32.hpp delete mode 100644 boost/beast/http/impl/file_body_win32.ipp create mode 100644 boost/beast/http/impl/message.hpp delete mode 100644 boost/beast/http/impl/message.ipp create mode 100644 boost/beast/http/impl/parser.hpp delete mode 100644 boost/beast/http/impl/parser.ipp create mode 100644 boost/beast/http/impl/read.hpp delete mode 100644 boost/beast/http/impl/read.ipp create mode 100644 boost/beast/http/impl/rfc7230.hpp create mode 100644 boost/beast/http/impl/serializer.hpp delete mode 100644 boost/beast/http/impl/serializer.ipp create mode 100644 boost/beast/http/impl/write.hpp delete mode 100644 boost/beast/http/impl/write.ipp (limited to 'boost/beast/http') diff --git a/boost/beast/http/basic_dynamic_body.hpp b/boost/beast/http/basic_dynamic_body.hpp index ba7208b7d7..3fea37379b 100644 --- a/boost/beast/http/basic_dynamic_body.hpp +++ b/boost/beast/http/basic_dynamic_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -11,7 +11,7 @@ #define BOOST_BEAST_HTTP_BASIC_DYNAMIC_BODY_HPP #include -#include +#include #include #include #include @@ -24,9 +24,9 @@ namespace boost { namespace beast { namespace http { -/** A @b Body using a @b DynamicBuffer +/** A Body using a DynamicBuffer - This body uses a @b DynamicBuffer as a memory-based container + This body uses a DynamicBuffer as a memory-based container for holding message payloads. Messages using this body type may be serialized and parsed. */ @@ -34,8 +34,8 @@ template struct basic_dynamic_body { static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); /** The type of container used for the body @@ -59,10 +59,10 @@ struct basic_dynamic_body /** The algorithm for parsing the body - Meets the requirements of @b BodyReader. + Meets the requirements of BodyReader. */ #if BOOST_BEAST_DOXYGEN - using reader = implementation_defined; + using reader = __implementation_defined__; #else class reader { @@ -80,7 +80,7 @@ struct basic_dynamic_body init(boost::optional< std::uint64_t> const&, error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } template @@ -88,9 +88,7 @@ struct basic_dynamic_body put(ConstBufferSequence const& buffers, error_code& ec) { - using boost::asio::buffer_copy; - using boost::asio::buffer_size; - auto const n = buffer_size(buffers); + auto const n = buffer_bytes(buffers); if(body_.size() > body_.max_size() - n) { ec = error::buffer_overflow; @@ -104,7 +102,7 @@ struct basic_dynamic_body if(ec) return 0; auto const bytes_transferred = - buffer_copy(*mb, buffers); + net::buffer_copy(*mb, buffers); body_.commit(bytes_transferred); return bytes_transferred; } @@ -112,17 +110,17 @@ struct basic_dynamic_body void finish(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } }; #endif /** The algorithm for serializing the body - Meets the requirements of @b BodyWriter. + Meets the requirements of BodyWriter. */ #if BOOST_BEAST_DOXYGEN - using writer = implementation_defined; + using writer = __implementation_defined__; #else class writer { @@ -142,13 +140,13 @@ struct basic_dynamic_body void init(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } boost::optional> get(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; return {{body_.data(), false}}; } }; diff --git a/boost/beast/http/basic_file_body.hpp b/boost/beast/http/basic_file_body.hpp index 7dd104d445..152df8898b 100644 --- a/boost/beast/http/basic_file_body.hpp +++ b/boost/beast/http/basic_file_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -39,14 +38,14 @@ namespace http { content from a directory as part of a web service. @tparam File The implementation to use for accessing files. - This type must meet the requirements of @b File. + This type must meet the requirements of File. */ template struct basic_file_body { // Make sure the type meets the requirements static_assert(is_file::value, - "File requirements not met"); + "File type requirements not met"); /// The type of File this body uses using file_type = File; @@ -235,7 +234,7 @@ public: // The type of buffer sequence returned by `get`. // using const_buffers_type = - boost::asio::const_buffer; + net::const_buffer; // Constructor. // @@ -319,7 +318,7 @@ init(error_code& ec) // to indicate no error. // // We don't do anything fancy so set "no error" - ec.assign(0, ec.category()); + ec = {}; } // This function is called repeatedly by the serializer to @@ -349,7 +348,7 @@ get(error_code& ec) -> // into the library to get the generic category because // that saves us a possibly expensive atomic operation. // - ec.assign(0, ec.category()); + ec = {}; return boost::none; } @@ -373,7 +372,7 @@ get(error_code& ec) -> // we set this bool to `false` so we will not be called // again. // - ec.assign(0, ec.category()); + ec = {}; return {{ const_buffers_type{buf_, nread}, // buffer to return. remain_ > 0 // `true` if there are more buffers. @@ -474,7 +473,7 @@ init( // to indicate no error. // // We don't do anything fancy so set "no error" - ec.assign(0, ec.category()); + ec = {}; } // This will get called one or more times with body buffers @@ -492,11 +491,11 @@ put(ConstBufferSequence const& buffers, error_code& ec) // Loop over all the buffers in the sequence, // and write each one to the file. - for(auto it = boost::asio::buffer_sequence_begin(buffers); - it != boost::asio::buffer_sequence_end(buffers); ++it) + for(auto it = net::buffer_sequence_begin(buffers); + it != net::buffer_sequence_end(buffers); ++it) { // Write this buffer to the file - boost::asio::const_buffer buffer = *it; + net::const_buffer buffer = *it; nwritten += body_.file_.write( buffer.data(), buffer.size(), ec); if(ec) @@ -505,7 +504,7 @@ put(ConstBufferSequence const& buffers, error_code& ec) // Indicate success // This is required by the error_code specification - ec.assign(0, ec.category()); + ec = {}; return nwritten; } @@ -519,7 +518,7 @@ finish(error_code& ec) { // This has to be cleared before returning, to // indicate no error. The specification requires it. - ec.assign(0, ec.category()); + ec = {}; } //] diff --git a/boost/beast/http/basic_parser.hpp b/boost/beast/http/basic_parser.hpp index dfa1965ceb..650e810ab6 100644 --- a/boost/beast/http/basic_parser.hpp +++ b/boost/beast/http/basic_parser.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -38,142 +38,48 @@ namespace http { fields. The parser is optimized for the case where the input buffer sequence consists of a single contiguous buffer. The - @ref flat_buffer class is provided, which guarantees + @ref beast::basic_flat_buffer class is provided, which guarantees that the input sequence of the stream buffer will be represented by exactly one contiguous buffer. To ensure the optimum performance - of the parser, use @ref flat_buffer with HTTP algorithms + of the parser, use @ref beast::basic_flat_buffer with HTTP algorithms such as @ref read, @ref read_some, @ref async_read, and @ref async_read_some. Alternatively, the caller may use custom techniques to ensure that 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. - - Every callback must be provided by the derived class, or else - a compilation error will be generated. This exemplar shows - the signature and description of the callbacks required in - the derived class. - For each callback, the function will ensure that `!ec` is `true` - if there was no error or set to the appropriate error code if - there was one. If an error is set, the value is propagated to - the caller of the parser. - - @par Derived Class Requirements - @code - template - class derived - : public basic_parser> - { - private: - // The friend declaration is needed, - // otherwise the callbacks must be made public. - friend class basic_parser; - - /// Called after receiving the request-line (isRequest == true). - void - on_request_impl( - verb method, // The method verb, verb::unknown if no match - string_view method_str, // The method as a string - string_view target, // The request-target - int version, // The HTTP-version - error_code& ec); // The error returned to the caller, if any - - /// Called after receiving the start-line (isRequest == false). - void - on_response_impl( - int code, // The status-code - string_view reason, // The obsolete reason-phrase - int version, // The HTTP-version - error_code& ec); // The error returned to the caller, if any - - /// Called after receiving a header field. - void - on_field_impl( - field f, // The known-field enumeration constant - string_view name, // The field name string. - string_view value, // The field value - error_code& ec); // The error returned to the caller, if any - - /// Called after the complete header is received. - void - on_header_impl( - error_code& ec); // The error returned to the caller, if any - - /// Called just before processing the body, if a body exists. - void - on_body_init_impl( - boost::optional< - std::uint64_t> const& - content_length, // Content length if known, else `boost::none` - error_code& ec); // The error returned to the caller, if any - - /// Called for each piece of the body, if a body exists. - //! - //! This is used when there is no chunked transfer coding. - //! - //! The function returns the number of bytes consumed from the - //! input buffer. Any input octets not consumed will be will be - //! presented on subsequent calls. - //! - std::size_t - on_body_impl( - string_view s, // A portion of the body - error_code& ec); // The error returned to the caller, if any - - /// Called for each chunk header. - void - on_chunk_header_impl( - std::uint64_t size, // The size of the upcoming chunk, - // or zero for the last chunk - string_view extension, // The chunk extensions (may be empty) - error_code& ec); // The error returned to the caller, if any - - /// Called to deliver the chunk body. - //! - //! This is used when there is a chunked transfer coding. The - //! implementation will automatically remove the encoding before - //! calling this function. - //! - //! The function returns the number of bytes consumed from the - //! input buffer. Any input octets not consumed will be will be - //! presented on subsequent calls. - //! - std::size_t - on_chunk_body_impl( - std::uint64_t remain, // The number of bytes remaining in the chunk, - // including what is being passed here. - // or zero for the last chunk - string_view body, // The next piece of the chunk body - error_code& ec); // The error returned to the caller, if any - - /// Called when the complete message is parsed. - void - on_finish_impl(error_code& ec); - - public: - derived() = default; - }; - @endcode + or more calls to virtual functions, which the derived class must + implement. + + Every virtual function must be provided by the derived class, + or else a compilation error will be generated. The implementation + will make sure that `ec` is clear before each virtual function + is invoked. If a virtual function sets an error, it is propagated + out of the parser to the caller. @tparam isRequest A `bool` indicating whether the parser will be presented with request or response message. - @tparam Derived The derived class type. This is part of the - Curiously Recurring Template Pattern interface. - @note If the parser encounters a field value with obs-fold longer than 4 kilobytes in length, an error is generated. */ -template +template class basic_parser : private detail::basic_parser_base { - template - friend class basic_parser; + std::uint64_t body_limit_ = + default_body_limit(is_request{}); // max payload body + std::uint64_t len_ = 0; // size of chunk or body + std::uint64_t len0_ = 0; // content length if known + std::unique_ptr buf_; // temp storage + std::size_t buf_len_ = 0; // size of buf_ + std::size_t skip_ = 0; // resume search here + std::uint32_t header_limit_ = 8192; // max header size + unsigned short status_ = 0; // response status + state state_ = state::nothing_yet; // initial state + unsigned f_ = 0; // flags // limit on the size of the stack flat buffer static std::size_t constexpr max_stack_buffer = 8192; @@ -219,27 +125,13 @@ class basic_parser return 8 * 1024 * 1024; // 8MB } - std::uint64_t body_limit_ = - default_body_limit(is_request{}); // max payload body - std::uint64_t len_ = 0; // size of chunk or body - std::unique_ptr buf_; // temp storage - std::size_t buf_len_ = 0; // size of buf_ - std::size_t skip_ = 0; // resume search here - std::uint32_t header_limit_ = 8192; // max header size - unsigned short status_ = 0; // response status - state state_ = state::nothing_yet; // initial state - unsigned f_ = 0; // flags + template + friend class basic_parser; protected: /// Default constructor basic_parser() = default; - /// Move constructor - basic_parser(basic_parser &&) = default; - - /// Move assignment - basic_parser& operator=(basic_parser &&) = default; - /** Move constructor @note @@ -247,8 +139,10 @@ protected: After the move, the only valid operation on the moved-from object is destruction. */ - template - basic_parser(basic_parser&&); + basic_parser(basic_parser &&) = default; + + /// Move assignment + basic_parser& operator=(basic_parser &&) = default; public: /// `true` if this parser parses requests, `false` for responses. @@ -256,7 +150,7 @@ public: std::integral_constant; /// Destructor - ~basic_parser() = default; + virtual ~basic_parser() = default; /// Copy constructor basic_parser(basic_parser const&) = delete; @@ -264,30 +158,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 @@ -362,6 +232,18 @@ public: boost::optional content_length() const; + /** Returns the remaining content length if known + + If the message header specifies a Content-Length, + the return value will be the number of bytes remaining + in the payload body have not yet been parsed. + + @note The return value is undefined unless + @ref is_header_done would return `true`. + */ + boost::optional + content_length_remaining() const; + /** Returns `true` if the message semantics require an end of file. Depending on the contents of the header, the parser may @@ -508,10 +390,10 @@ public: the parser may not be restarted. @param buffers An object meeting the requirements of - @b ConstBufferSequence that represents the next chunk of + ConstBufferSequence that represents the next chunk of message data. If the length of this buffer sequence is one, the implementation will not allocate additional memory. - The class @ref beast::flat_buffer is provided as one way to + The class @ref beast::basic_flat_buffer is provided as one way to meet this requirement @param ec Set to the error, if any occurred. @@ -526,7 +408,7 @@ public: #if ! BOOST_BEAST_DOXYGEN std::size_t - put(boost::asio::const_buffer const& buffer, + put(net::const_buffer buffer, error_code& ec); #endif @@ -540,7 +422,7 @@ public: This is typically called when a read from the underlying stream object sets the error code to - `boost::asio::error::eof`. + `net::error::eof`. @note Only valid after parsing a complete header. @@ -549,19 +431,203 @@ public: void put_eof(error_code& ec); -private: - inline - Derived& - impl() - { - return *static_cast(this); - } +protected: + /** Called after receiving the request-line. + + This virtual function is invoked after receiving a request-line + when parsing HTTP requests. + It can only be called when `isRequest == true`. + + @param method The verb enumeration. If the method string is not + one of the predefined strings, this value will be @ref verb::unknown. + + @param method_str The unmodified string representing the verb. + + @param target The request-target. + + @param version The HTTP-version. This will be 10 for HTTP/1.0, + and 11 for HTTP/1.1. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_request_impl( + verb method, + string_view method_str, + string_view target, + int version, + error_code& ec) = 0; + + /** Called after receiving the status-line. + + This virtual function is invoked after receiving a status-line + when parsing HTTP responses. + It can only be called when `isRequest == false`. + + @param code The numeric status code. + + @param reason The reason-phrase. Note that this value is + now obsolete, and only provided for historical or diagnostic + purposes. + + @param version The HTTP-version. This will be 10 for HTTP/1.0, + and 11 for HTTP/1.1. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_response_impl( + int code, + string_view reason, + int version, + error_code& ec) = 0; + + /** Called once for each complete field in the HTTP header. + + This virtual function is invoked for each field that is received + while parsing an HTTP message. + + @param name The known field enum value. If the name of the field + is not recognized, this value will be @ref field::unknown. + + @param name_string The exact name of the field as received from + the input, represented as a string. + + @param value A string holding the value of the field. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_field_impl( + field name, + string_view name_string, + string_view value, + error_code& ec) = 0; + + /** Called once after the complete HTTP header is received. + + This virtual function is invoked once, after the complete HTTP + header is received while parsing a message. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_header_impl(error_code& ec) = 0; + + /** Called once before the body is processed. + + This virtual function is invoked once, before the content body is + processed (but after the complete header is received). + + @param content_length A value representing the content length in + bytes if the length is known (this can include a zero length). + Otherwise, the value will be `boost::none`. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_body_init_impl( + boost::optional const& content_length, + error_code& ec) = 0; + + /** Called each time additional data is received representing the content body. + + This virtual function is invoked for each piece of the body which is + received while parsing of a message. This function is only used when + no chunked transfer encoding is present. + @param body A string holding the additional body contents. This may + contain nulls or unprintable characters. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + + @see on_chunk_body_impl + */ + virtual + std::size_t + on_body_impl( + string_view body, + error_code& ec) = 0; + + /** Called each time a new chunk header of a chunk encoded body is received. + + This function is invoked each time a new chunk header is received. + The function is only used when the chunked transfer encoding is present. + + @param size The size of this chunk, in bytes. + + @param extensions A string containing the entire chunk extensions. + This may be empty, indicating no extensions are present. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_chunk_header_impl( + std::uint64_t size, + string_view extensions, + error_code& ec) = 0; + + /** Called each time additional data is received representing part of a body chunk. + + This virtual function is invoked for each piece of the body which is + received while parsing of a message. This function is only used when + no chunked transfer encoding is present. + + @param remain The number of bytes remaining in this chunk. This includes + the contents of passed `body`. If this value is zero, then this represents + the final chunk. + + @param body A string holding the additional body contents. This may + contain nulls or unprintable characters. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + + @return This function should return the number of bytes actually consumed + from the `body` value. Any bytes that are not consumed on this call + will be presented in a subsequent call. + + @see on_body_impl + */ + virtual + std::size_t + on_chunk_body_impl( + std::uint64_t remain, + string_view body, + error_code& ec) = 0; + + /** Called once when the complete message is received. + + This virtual function is invoked once, after successfully parsing + a complete HTTP message. + + @param ec An output parameter which the function may set to indicate + an error. The error will be clear before this function is invoked. + */ + virtual + void + on_finish_impl(error_code& ec) = 0; + +private: template std::size_t - put_from_stack(std::size_t size, + put_from_stack( + std::size_t size, ConstBufferSequence const& buffers, - error_code& ec); + error_code& ec); void maybe_need_more( @@ -616,6 +682,9 @@ private: } // beast } // boost +#include +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/boost/beast/http/buffer_body.hpp b/boost/beast/http/buffer_body.hpp index bd420d9f54..8988c2323f 100644 --- a/boost/beast/http/buffer_body.hpp +++ b/boost/beast/http/buffer_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -11,6 +11,7 @@ #define BOOST_BEAST_HTTP_BUFFER_BODY_HPP #include +#include #include #include #include @@ -22,7 +23,7 @@ namespace boost { namespace beast { namespace http { -/** A @b Body using a caller provided buffer +/** A Body using a caller provided buffer Messages using this body type may be serialized and parsed. To use this class, the caller must initialize the members @@ -93,10 +94,10 @@ struct buffer_body /** The algorithm for parsing the body - Meets the requirements of @b BodyReader. + Meets the requirements of BodyReader. */ #if BOOST_BEAST_DOXYGEN - using reader = implementation_defined; + using reader = __implementation_defined__; #else class reader { @@ -113,7 +114,7 @@ struct buffer_body void init(boost::optional const&, error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } template @@ -121,21 +122,19 @@ struct buffer_body put(ConstBufferSequence const& buffers, error_code& ec) { - using boost::asio::buffer_size; - using boost::asio::buffer_copy; if(! body_.data) { ec = error::need_buffer; return 0; } auto const bytes_transferred = - buffer_copy(boost::asio::buffer( + net::buffer_copy(net::buffer( body_.data, body_.size), buffers); body_.data = static_cast( body_.data) + bytes_transferred; body_.size -= bytes_transferred; - if(bytes_transferred == buffer_size(buffers)) - ec.assign(0, ec.category()); + if(bytes_transferred == buffer_bytes(buffers)) + ec = {}; else ec = error::need_buffer; return bytes_transferred; @@ -144,17 +143,17 @@ struct buffer_body void finish(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } }; #endif /** The algorithm for serializing the body - Meets the requirements of @b BodyWriter. + Meets the requirements of BodyWriter. */ #if BOOST_BEAST_DOXYGEN - using writer = implementation_defined; + using writer = __implementation_defined__; #else class writer { @@ -163,7 +162,7 @@ struct buffer_body public: using const_buffers_type = - boost::asio::const_buffer; + net::const_buffer; template explicit @@ -175,7 +174,7 @@ struct buffer_body void init(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } boost::optional< @@ -191,13 +190,13 @@ struct buffer_body } else { - ec.assign(0, ec.category()); + ec = {}; } return boost::none; } if(body_.data) { - ec.assign(0, ec.category()); + ec = {}; toggle_ = true; return {{const_buffers_type{ body_.data, body_.size}, body_.more}}; @@ -205,7 +204,7 @@ struct buffer_body if(body_.more) ec = error::need_buffer; else - ec.assign(0, ec.category()); + ec = {}; return boost::none; } }; diff --git a/boost/beast/http/chunk_encode.hpp b/boost/beast/http/chunk_encode.hpp index 4994765fc8..8b8ff651c7 100644 --- a/boost/beast/http/chunk_encode.hpp +++ b/boost/beast/http/chunk_encode.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -25,14 +25,14 @@ namespace http { /** A chunked encoding crlf - This implements a @b ConstBufferSequence holding the CRLF + This implements a ConstBufferSequence holding the CRLF (`"\r\n"`) used as a delimiter in a @em chunk. To use this class, pass an instance of it to a stream algorithm as the buffer sequence: @code // writes "\r\n" - boost::asio::write(stream, chunk_crlf{}); + net::write(stream, chunk_crlf{}); @endcode @see https://tools.ietf.org/html/rfc7230#section-4.1 @@ -44,27 +44,28 @@ struct chunk_crlf //----- - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence #if BOOST_BEAST_DOXYGEN - using value_type = implementation_defined; + using value_type = __implementation_defined__; #else - using value_type = detail::chunk_crlf_iter::value_type; + using value_type = net::const_buffer; #endif - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence using const_iterator = value_type const*; - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence chunk_crlf(chunk_crlf const&) = default; - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence const_iterator begin() const { - return &detail::chunk_crlf_iter::value; + static net::const_buffer const cb{"\r\n", 2}; + return &cb; } - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence const_iterator end() const { @@ -76,7 +77,7 @@ struct chunk_crlf /** A @em chunk header - This implements a @b ConstBufferSequence representing the + This implements a ConstBufferSequence representing the header of a @em chunk. The serialized format is as follows: @code chunk-header = 1*HEXDIG chunk-ext CRLF @@ -97,7 +98,7 @@ struct chunk_crlf stream algorithm as the buffer sequence: @code // writes "400;x\r\n" - boost::asio::write(stream, chunk_header{1024, "x"}); + net::write(stream, chunk_header{1024, "x"}); @endcode @see https://tools.ietf.org/html/rfc7230#section-4.1 @@ -106,7 +107,7 @@ class chunk_header { using view_type = buffers_cat_view< detail::chunk_size, // chunk-size - boost::asio::const_buffer, // chunk-extensions + net::const_buffer, // chunk-extensions chunk_crlf>; // CRLF std::shared_ptr< @@ -227,31 +228,31 @@ public: //----- - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence #if BOOST_BEAST_DOXYGEN - using value_type = implementation_defined; + using value_type = __implementation_defined__; #else using value_type = typename view_type::value_type; #endif - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence #if BOOST_BEAST_DOXYGEN - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else using const_iterator = typename view_type::const_iterator; #endif - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence chunk_header(chunk_header const&) = default; - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence const_iterator begin() const { return view_.begin(); } - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence const_iterator end() const { @@ -263,7 +264,7 @@ public: /** A @em chunk - This implements a @b ConstBufferSequence representing + This implements a ConstBufferSequence representing a @em chunk. The serialized format is as follows: @code chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF @@ -285,7 +286,7 @@ class chunk_body { using view_type = buffers_cat_view< detail::chunk_size, // chunk-size - boost::asio::const_buffer, // chunk-extensions + net::const_buffer, // chunk-extensions chunk_crlf, // CRLF ConstBufferSequence, // chunk-body chunk_crlf>; // CRLF @@ -421,28 +422,28 @@ public: //----- - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence #if BOOST_BEAST_DOXYGEN - using value_type = implementation_defined; + using value_type = __implementation_defined__; #else using value_type = typename view_type::value_type; #endif - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence #if BOOST_BEAST_DOXYGEN - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else using const_iterator = typename view_type::const_iterator; #endif - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence const_iterator begin() const { return view_.begin(); } - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence const_iterator end() const { @@ -459,7 +460,7 @@ class chunk_last { static_assert( is_fields::value || - boost::asio::is_const_buffer_sequence::value, + net::is_const_buffer_sequence::value, "Trailer requirements not met"); using buffers_type = typename @@ -523,33 +524,33 @@ public: //----- - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence chunk_last(chunk_last const&) = default; - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence #if BOOST_BEAST_DOXYGEN - using value_type = implementation_defined; + using value_type = __implementation_defined__; #else using value_type = typename view_type::value_type; #endif - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence #if BOOST_BEAST_DOXYGEN - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else using const_iterator = typename view_type::const_iterator; #endif - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence const_iterator begin() const { return view_.begin(); } - /// Required for @b ConstBufferSequence + /// Required for ConstBufferSequence const_iterator end() const { @@ -731,6 +732,6 @@ make_chunk_last( } // beast } // boost -#include +#include #endif diff --git a/boost/beast/http/detail/basic_parsed_list.hpp b/boost/beast/http/detail/basic_parsed_list.hpp index 5577fba752..53ce60976d 100644 --- a/boost/beast/http/detail/basic_parsed_list.hpp +++ b/boost/beast/http/detail/basic_parsed_list.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -36,7 +36,7 @@ public: /// A constant iterator to a list element. #if BOOST_BEAST_DOXYGEN - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else class const_iterator; #endif diff --git a/boost/beast/http/detail/basic_parser.hpp b/boost/beast/http/detail/basic_parser.hpp index 741de6edf0..f4e9083ffa 100644 --- a/boost/beast/http/detail/basic_parser.hpp +++ b/boost/beast/http/detail/basic_parser.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -12,14 +12,11 @@ #include #include -#include #include #include #include #include -#include #include -#include #include namespace boost { @@ -35,12 +32,6 @@ struct basic_parser_base // static std::size_t constexpr max_obs_fold = 4096; - template - struct is_unsigned_integer: - std::integral_constant::is_integer && - ! std::numeric_limits::is_signed> {}; - enum class state { nothing_yet = 0, @@ -56,63 +47,6 @@ struct basic_parser_base complete }; - static - bool - is_pathchar(char c) - { - // VFALCO This looks the same as the one below... - - // TEXT = - static bool constexpr tab[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 - }; - return tab[static_cast(c)]; - } - - static - inline - bool - unhex(unsigned char& d, char c) - { - static signed char constexpr tab[256] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48 - -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80 - -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 112 - - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 128 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 144 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 160 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 176 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 192 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 208 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 224 - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240 - }; - d = static_cast( - tab[static_cast(c)]); - return d != static_cast(-1); - } - static bool is_digit(char c) @@ -127,35 +61,16 @@ struct basic_parser_base return static_cast(c-32) < 95; } - template + BOOST_BEAST_DECL static - FwdIt - trim_front(FwdIt it, FwdIt const& end) - { - while(it != end) - { - if(*it != ' ' && *it != '\t') - break; - ++it; - } - return it; - } + char const* + trim_front(char const* it, char const* end); - template + BOOST_BEAST_DECL static - RanIt + char const* trim_back( - RanIt it, RanIt const& first) - { - while(it != first) - { - auto const c = it[-1]; - if(c != ' ' && c != '\t') - break; - --it; - } - return it; - } + char const* it, char const* first); static string_view @@ -167,736 +82,116 @@ struct basic_parser_base //-------------------------------------------------------------------------- + BOOST_BEAST_DECL + static + bool + is_pathchar(char c); + + BOOST_BEAST_DECL + static + bool + unhex(unsigned char& d, char c); + + BOOST_BEAST_DECL static std::pair find_fast( char const* buf, char const* buf_end, char const* ranges, - size_t ranges_size) - { - bool found = false; - boost::ignore_unused(buf_end, ranges, ranges_size); - return {buf, found}; - } + size_t ranges_size); - // VFALCO Can SIMD help this? + BOOST_BEAST_DECL static char const* find_eol( char const* it, char const* last, - error_code& ec) - { - for(;;) - { - if(it == last) - { - ec.assign(0, ec.category()); - return nullptr; - } - if(*it == '\r') - { - if(++it == last) - { - ec.assign(0, ec.category()); - return nullptr; - } - if(*it != '\n') - { - ec = error::bad_line_ending; - return nullptr; - } - ec.assign(0, ec.category()); - return ++it; - } - // VFALCO Should we handle the legacy case - // for lines terminated with a single '\n'? - ++it; - } - } + error_code& ec); + BOOST_BEAST_DECL static char const* - find_eom(char const* p, char const* last) - { - for(;;) - { - if(p + 4 > last) - return nullptr; - if(p[3] != '\n') - { - if(p[3] == '\r') - ++p; - else - p += 4; - } - else if(p[2] != '\r') - { - p += 4; - } - else if(p[1] != '\n') - { - p += 2; - } - else if(p[0] != '\r') - { - p += 2; - } - else - { - return p + 4; - } - } - } + find_eom(char const* p, char const* last); //-------------------------------------------------------------------------- + BOOST_BEAST_DECL static char const* parse_token_to_eol( char const* p, char const* last, char const*& token_last, - error_code& ec) - { - for(;; ++p) - { - if(p >= last) - { - ec = error::need_more; - return p; - } - if(BOOST_UNLIKELY(! is_print(*p))) - if((BOOST_LIKELY(static_cast< - unsigned char>(*p) < '\040') && - BOOST_LIKELY(*p != '\011')) || - BOOST_UNLIKELY(*p == '\177')) - goto found_control; - } - found_control: - if(BOOST_LIKELY(*p == '\r')) - { - if(++p >= last) - { - ec = error::need_more; - return last; - } - if(*p++ != '\n') - { - ec = error::bad_line_ending; - return last; - } - token_last = p - 2; - } - #if 0 - // VFALCO This allows `\n` by itself - // to terminate a line - else if(*p == '\n') - { - token_last = p; - ++p; - } - #endif - else - { - // invalid character - return nullptr; - } - return p; - } + error_code& ec); - template + BOOST_BEAST_DECL static - typename std::enable_if::value, bool>::type - parse_dec(Iter it, Iter last, T& v) - { - if(it == last) - return false; - T tmp = 0; - do - { - if((! is_digit(*it)) || - tmp > (std::numeric_limits::max)() / 10) - return false; - tmp *= 10; - T const d = *it - '0'; - if((std::numeric_limits::max)() - tmp < d) - return false; - tmp += d; - } - while(++it != last); - v = tmp; - return true; - } + bool + parse_dec(char const* it, char const* last, std::uint64_t& v); - template + BOOST_BEAST_DECL static - typename std::enable_if::value, bool>::type - parse_hex(Iter& it, T& v) - { - unsigned char d; - if(! unhex(d, *it)) - return false; - T tmp = 0; - do - { - if(tmp > (std::numeric_limits::max)() / 16) - return false; - tmp *= 16; - if((std::numeric_limits::max)() - tmp < d) - return false; - tmp += d; - } - while(unhex(d, *++it)); - v = tmp; - return true; - } + bool + parse_hex(char const*& it, std::uint64_t& v); + BOOST_BEAST_DECL static bool - parse_crlf(char const*& it) - { - if( it[0] != '\r' || it[1] != '\n') - return false; - it += 2; - return true; - } + parse_crlf(char const*& it); + BOOST_BEAST_DECL static void parse_method( char const*& it, char const* last, - string_view& result, error_code& ec) - { - // parse token SP - auto const first = it; - for(;; ++it) - { - if(it + 1 > last) - { - ec = error::need_more; - return; - } - if(! detail::is_token_char(*it)) - break; - } - if(it + 1 > last) - { - ec = error::need_more; - return; - } - if(*it != ' ') - { - ec = error::bad_method; - return; - } - if(it == first) - { - // cannot be empty - ec = error::bad_method; - return; - } - result = make_string(first, it++); - } + string_view& result, error_code& ec); + BOOST_BEAST_DECL static void parse_target( char const*& it, char const* last, - string_view& result, error_code& ec) - { - // parse target SP - auto const first = it; - for(;; ++it) - { - if(it + 1 > last) - { - ec = error::need_more; - return; - } - if(! is_pathchar(*it)) - break; - } - if(it + 1 > last) - { - ec = error::need_more; - return; - } - if(*it != ' ') - { - ec = error::bad_target; - return; - } - if(it == first) - { - // cannot be empty - ec = error::bad_target; - return; - } - result = make_string(first, it++); - } + string_view& result, error_code& ec); + BOOST_BEAST_DECL static void parse_version( char const*& it, char const* last, - int& result, error_code& ec) - { - if(it + 8 > last) - { - ec = error::need_more; - return; - } - if(*it++ != 'H') - { - ec = error::bad_version; - return; - } - if(*it++ != 'T') - { - ec = error::bad_version; - return; - } - if(*it++ != 'T') - { - ec = error::bad_version; - return; - } - if(*it++ != 'P') - { - ec = error::bad_version; - return; - } - if(*it++ != '/') - { - ec = error::bad_version; - return; - } - if(! is_digit(*it)) - { - ec = error::bad_version; - return; - } - result = 10 * (*it++ - '0'); - if(*it++ != '.') - { - ec = error::bad_version; - return; - } - if(! is_digit(*it)) - { - ec = error::bad_version; - return; - } - result += *it++ - '0'; - } + int& result, error_code& ec); + BOOST_BEAST_DECL static void parse_status( char const*& it, char const* last, - unsigned short& result, error_code& ec) - { - // parse 3(digit) SP - if(it + 4 > last) - { - ec = error::need_more; - return; - } - if(! is_digit(*it)) - { - ec = error::bad_status; - return; - } - result = 100 * (*it++ - '0'); - if(! is_digit(*it)) - { - ec = error::bad_status; - return; - } - result += 10 * (*it++ - '0'); - if(! is_digit(*it)) - { - ec = error::bad_status; - return; - } - result += *it++ - '0'; - if(*it++ != ' ') - { - ec = error::bad_status; - return; - } - } - + unsigned short& result, error_code& ec); + + BOOST_BEAST_DECL + static void parse_reason( char const*& it, char const* last, - string_view& result, error_code& ec) - { - auto const first = it; - char const* token_last = nullptr; - auto p = parse_token_to_eol( - it, last, token_last, ec); - if(ec) - return; - if(! p) - { - ec = error::bad_reason; - return; - } - result = make_string(first, token_last); - it = p; - } + string_view& result, error_code& ec); - template + BOOST_BEAST_DECL + static void parse_field( char const*& p, char const* last, string_view& name, string_view& value, - static_string& buf, - error_code& ec) - { - /* header-field = field-name ":" OWS field-value OWS - - field-name = token - field-value = *( field-content / obs-fold ) - field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] - field-vchar = VCHAR / obs-text - - obs-fold = CRLF 1*( SP / HTAB ) - ; obsolete line folding - ; see Section 3.2.4 - - token = 1* - CHAR = - sep = "(" | ")" | "<" | ">" | "@" - | "," | ";" | ":" | "\" | <"> - | "/" | "[" | "]" | "?" | "=" - | "{" | "}" | SP | HT - */ - static char const* is_token = - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" - "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" - "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - - // name - BOOST_ALIGNMENT(16) static const char ranges1[] = - "\x00 " /* control chars and up to SP */ - "\"\"" /* 0x22 */ - "()" /* 0x28,0x29 */ - ",," /* 0x2c */ - "//" /* 0x2f */ - ":@" /* 0x3a-0x40 */ - "[]" /* 0x5b-0x5d */ - "{\377"; /* 0x7b-0xff */ - auto first = p; - bool found; - std::tie(p, found) = find_fast( - p, last, ranges1, sizeof(ranges1)-1); - if(! found && p >= last) - { - ec = error::need_more; - return; - } - for(;;) - { - if(*p == ':') - break; - if(! is_token[static_cast< - unsigned char>(*p)]) - { - ec = error::bad_field; - return; - } - ++p; - if(p >= last) - { - ec = error::need_more; - return; - } - } - if(p == first) - { - // empty name - ec = error::bad_field; - return; - } - name = make_string(first, p); - ++p; // eat ':' - char const* token_last = nullptr; - for(;;) - { - // eat leading ' ' and '\t' - for(;;++p) - { - if(p + 1 > last) - { - ec = error::need_more; - return; - } - if(! (*p == ' ' || *p == '\t')) - break; - } - // parse to CRLF - first = p; - p = parse_token_to_eol(p, last, token_last, ec); - if(ec) - return; - if(! p) - { - ec = error::bad_value; - return; - } - // Look 1 char past the CRLF to handle obs-fold. - if(p + 1 > last) - { - ec = error::need_more; - return; - } - token_last = - trim_back(token_last, first); - if(*p != ' ' && *p != '\t') - { - value = make_string(first, token_last); - return; - } - ++p; - if(token_last != first) - break; - } - buf.resize(0); - buf.append(first, token_last); - BOOST_ASSERT(! buf.empty()); - try - { - for(;;) - { - // eat leading ' ' and '\t' - for(;;++p) - { - if(p + 1 > last) - { - ec = error::need_more; - return; - } - if(! (*p == ' ' || *p == '\t')) - break; - } - // parse to CRLF - first = p; - p = parse_token_to_eol(p, last, token_last, ec); - if(ec) - return; - if(! p) - { - ec = error::bad_value; - return; - } - // Look 1 char past the CRLF to handle obs-fold. - if(p + 1 > last) - { - ec = error::need_more; - return; - } - token_last = trim_back(token_last, first); - if(first != token_last) - { - buf.push_back(' '); - buf.append(first, token_last); - } - if(*p != ' ' && *p != '\t') - { - value = {buf.data(), buf.size()}; - return; - } - ++p; - } - } - catch(std::length_error const&) - { - ec = error::header_limit; - return; - } - } + static_string& buf, + error_code& ec); + BOOST_BEAST_DECL + static void parse_chunk_extensions( char const*& it, char const* last, - error_code& ec) - { - /* - chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] ) - BWS = *( SP / HTAB ) ; "Bad White Space" - chunk-ext-name = token - chunk-ext-val = token / quoted-string - token = 1*tchar - quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE - qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text - quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) - obs-text = %x80-FF - - https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667 - */ - loop: - if(it == last) - { - ec = error::need_more; - return; - } - if(*it != ' ' && *it != '\t' && *it != ';') - return; - // BWS - if(*it == ' ' || *it == '\t') - { - for(;;) - { - ++it; - if(it == last) - { - ec = error::need_more; - return; - } - if(*it != ' ' && *it != '\t') - break; - } - } - // ';' - if(*it != ';') - { - ec = error::bad_chunk_extension; - return; - } - semi: - ++it; // skip ';' - // BWS - for(;;) - { - if(it == last) - { - ec = error::need_more; - return; - } - if(*it != ' ' && *it != '\t') - break; - ++it; - } - // chunk-ext-name - if(! detail::is_token_char(*it)) - { - ec = error::bad_chunk_extension; - return; - } - for(;;) - { - ++it; - if(it == last) - { - ec = error::need_more; - return; - } - if(! detail::is_token_char(*it)) - break; - } - // BWS [ ";" / "=" ] - { - bool bws; - if(*it == ' ' || *it == '\t') - { - for(;;) - { - ++it; - if(it == last) - { - ec = error::need_more; - return; - } - if(*it != ' ' && *it != '\t') - break; - } - bws = true; - } - else - { - bws = false; - } - if(*it == ';') - goto semi; - if(*it != '=') - { - if(bws) - ec = error::bad_chunk_extension; - return; - } - ++it; // skip '=' - } - // BWS - for(;;) - { - if(it == last) - { - ec = error::need_more; - return; - } - if(*it != ' ' && *it != '\t') - break; - ++it; - } - // chunk-ext-val - if(*it != '"') - { - // token - if(! detail::is_token_char(*it)) - { - ec = error::bad_chunk_extension; - return; - } - for(;;) - { - ++it; - if(it == last) - { - ec = error::need_more; - return; - } - if(! detail::is_token_char(*it)) - break; - } - } - else - { - // quoted-string - for(;;) - { - ++it; - if(it == last) - { - ec = error::need_more; - return; - } - if(*it == '"') - break; - if(*it == '\\') - { - ++it; - if(it == last) - { - ec = error::need_more; - return; - } - } - } - ++it; - } - goto loop; - } + error_code& ec); }; } // detail @@ -904,4 +199,8 @@ struct basic_parser_base } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/boost/beast/http/detail/basic_parser.ipp b/boost/beast/http/detail/basic_parser.ipp new file mode 100644 index 0000000000..934d0dc505 --- /dev/null +++ b/boost/beast/http/detail/basic_parser.ipp @@ -0,0 +1,848 @@ +// +// 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_DETAIL_BASIC_PARSER_IPP +#define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_IPP + +#include +#include + +namespace boost { +namespace beast { +namespace http { +namespace detail { + +char const* +basic_parser_base:: +trim_front(char const* it, char const* end) +{ + while(it != end) + { + if(*it != ' ' && *it != '\t') + break; + ++it; + } + return it; +} + +char const* +basic_parser_base:: +trim_back( + char const* it, char const* first) +{ + while(it != first) + { + auto const c = it[-1]; + if(c != ' ' && c != '\t') + break; + --it; + } + return it; +} + +bool +basic_parser_base:: +is_pathchar(char c) +{ + // VFALCO This looks the same as the one below... + + // TEXT = + static bool constexpr tab[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 + }; + return tab[static_cast(c)]; +} + +bool +basic_parser_base:: +unhex(unsigned char& d, char c) +{ + static signed char constexpr tab[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48 + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80 + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 112 + + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 128 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 144 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 160 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 176 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 192 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 208 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 224 + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240 + }; + d = static_cast( + tab[static_cast(c)]); + return d != static_cast(-1); +} + +//-------------------------------------------------------------------------- + +std::pair +basic_parser_base:: +find_fast( + char const* buf, + char const* buf_end, + char const* ranges, + size_t ranges_size) +{ + bool found = false; + boost::ignore_unused(buf_end, ranges, ranges_size); + return {buf, found}; +} + +// VFALCO Can SIMD help this? +char const* +basic_parser_base:: +find_eol( + char const* it, char const* last, + error_code& ec) +{ + for(;;) + { + if(it == last) + { + ec = {}; + return nullptr; + } + if(*it == '\r') + { + if(++it == last) + { + ec = {}; + return nullptr; + } + if(*it != '\n') + { + ec = error::bad_line_ending; + return nullptr; + } + ec = {}; + return ++it; + } + // VFALCO Should we handle the legacy case + // for lines terminated with a single '\n'? + ++it; + } +} + +bool +basic_parser_base:: +parse_dec(char const* it, char const* last, std::uint64_t& v) +{ + if(it == last) + return false; + std::uint64_t tmp = 0; + do + { + if((! is_digit(*it)) || + tmp > (std::numeric_limits::max)() / 10) + return false; + tmp *= 10; + std::uint64_t const d = *it - '0'; + if((std::numeric_limits::max)() - tmp < d) + return false; + tmp += d; + } + while(++it != last); + v = tmp; + return true; +} + +bool +basic_parser_base:: +parse_hex(char const*& it, std::uint64_t& v) +{ + unsigned char d; + if(! unhex(d, *it)) + return false; + std::uint64_t tmp = 0; + do + { + if(tmp > (std::numeric_limits::max)() / 16) + return false; + tmp *= 16; + if((std::numeric_limits::max)() - tmp < d) + return false; + tmp += d; + } + while(unhex(d, *++it)); + v = tmp; + return true; +} + +char const* +basic_parser_base:: +find_eom(char const* p, char const* last) +{ + for(;;) + { + if(p + 4 > last) + return nullptr; + if(p[3] != '\n') + { + if(p[3] == '\r') + ++p; + else + p += 4; + } + else if(p[2] != '\r') + { + p += 4; + } + else if(p[1] != '\n') + { + p += 2; + } + else if(p[0] != '\r') + { + p += 2; + } + else + { + return p + 4; + } + } +} + +//-------------------------------------------------------------------------- + +char const* +basic_parser_base:: +parse_token_to_eol( + char const* p, + char const* last, + char const*& token_last, + error_code& ec) +{ + for(;; ++p) + { + if(p >= last) + { + ec = error::need_more; + return p; + } + if(BOOST_UNLIKELY(! is_print(*p))) + if((BOOST_LIKELY(static_cast< + unsigned char>(*p) < '\040') && + BOOST_LIKELY(*p != 9)) || + BOOST_UNLIKELY(*p == 127)) + goto found_control; + } +found_control: + if(BOOST_LIKELY(*p == '\r')) + { + if(++p >= last) + { + ec = error::need_more; + return last; + } + if(*p++ != '\n') + { + ec = error::bad_line_ending; + return last; + } + token_last = p - 2; + } +#if 0 + // VFALCO This allows `\n` by itself + // to terminate a line + else if(*p == '\n') + { + token_last = p; + ++p; + } +#endif + else + { + // invalid character + return nullptr; + } + return p; +} + +bool +basic_parser_base:: +parse_crlf(char const*& it) +{ + if( it[0] != '\r' || it[1] != '\n') + return false; + it += 2; + return true; +} + +void +basic_parser_base:: +parse_method( + char const*& it, char const* last, + string_view& result, error_code& ec) +{ + // parse token SP + auto const first = it; + for(;; ++it) + { + if(it + 1 > last) + { + ec = error::need_more; + return; + } + if(! detail::is_token_char(*it)) + break; + } + if(it + 1 > last) + { + ec = error::need_more; + return; + } + if(*it != ' ') + { + ec = error::bad_method; + return; + } + if(it == first) + { + // cannot be empty + ec = error::bad_method; + return; + } + result = make_string(first, it++); +} + +void +basic_parser_base:: +parse_target( + char const*& it, char const* last, + string_view& result, error_code& ec) +{ + // parse target SP + auto const first = it; + for(;; ++it) + { + if(it + 1 > last) + { + ec = error::need_more; + return; + } + if(! is_pathchar(*it)) + break; + } + if(it + 1 > last) + { + ec = error::need_more; + return; + } + if(*it != ' ') + { + ec = error::bad_target; + return; + } + if(it == first) + { + // cannot be empty + ec = error::bad_target; + return; + } + result = make_string(first, it++); +} + +void +basic_parser_base:: +parse_version( + char const*& it, char const* last, + int& result, error_code& ec) +{ + if(it + 8 > last) + { + ec = error::need_more; + return; + } + if(*it++ != 'H') + { + ec = error::bad_version; + return; + } + if(*it++ != 'T') + { + ec = error::bad_version; + return; + } + if(*it++ != 'T') + { + ec = error::bad_version; + return; + } + if(*it++ != 'P') + { + ec = error::bad_version; + return; + } + if(*it++ != '/') + { + ec = error::bad_version; + return; + } + if(! is_digit(*it)) + { + ec = error::bad_version; + return; + } + result = 10 * (*it++ - '0'); + if(*it++ != '.') + { + ec = error::bad_version; + return; + } + if(! is_digit(*it)) + { + ec = error::bad_version; + return; + } + result += *it++ - '0'; +} + +void +basic_parser_base:: +parse_status( + char const*& it, char const* last, + unsigned short& result, error_code& ec) +{ + // parse 3(digit) SP + if(it + 4 > last) + { + ec = error::need_more; + return; + } + if(! is_digit(*it)) + { + ec = error::bad_status; + return; + } + result = 100 * (*it++ - '0'); + if(! is_digit(*it)) + { + ec = error::bad_status; + return; + } + result += 10 * (*it++ - '0'); + if(! is_digit(*it)) + { + ec = error::bad_status; + return; + } + result += *it++ - '0'; + if(*it++ != ' ') + { + ec = error::bad_status; + return; + } +} + +void +basic_parser_base:: +parse_reason( + char const*& it, char const* last, + string_view& result, error_code& ec) +{ + auto const first = it; + char const* token_last = nullptr; + auto p = parse_token_to_eol( + it, last, token_last, ec); + if(ec) + return; + if(! p) + { + ec = error::bad_reason; + return; + } + result = make_string(first, token_last); + it = p; +} + +void +basic_parser_base:: +parse_field( + char const*& p, + char const* last, + string_view& name, + string_view& value, + static_string& buf, + error_code& ec) +{ +/* header-field = field-name ":" OWS field-value OWS + + field-name = token + field-value = *( field-content / obs-fold ) + field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] + field-vchar = VCHAR / obs-text + + obs-fold = CRLF 1*( SP / HTAB ) + ; obsolete line folding + ; see Section 3.2.4 + + token = 1* + CHAR = + sep = "(" | ")" | "<" | ">" | "@" + | "," | ";" | ":" | "\" | <"> + | "/" | "[" | "]" | "?" | "=" + | "{" | "}" | SP | HT +*/ + static char const* is_token = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" + "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" + "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + + // name + BOOST_ALIGNMENT(16) static const char ranges1[] = + "\x00 " /* control chars and up to SP */ + "\"\"" /* 0x22 */ + "()" /* 0x28,0x29 */ + ",," /* 0x2c */ + "//" /* 0x2f */ + ":@" /* 0x3a-0x40 */ + "[]" /* 0x5b-0x5d */ + "{\377"; /* 0x7b-0xff */ + auto first = p; + bool found; + std::tie(p, found) = find_fast( + p, last, ranges1, sizeof(ranges1)-1); + if(! found && p >= last) + { + ec = error::need_more; + return; + } + for(;;) + { + if(*p == ':') + break; + if(! is_token[static_cast< + unsigned char>(*p)]) + { + ec = error::bad_field; + return; + } + ++p; + if(p >= last) + { + ec = error::need_more; + return; + } + } + if(p == first) + { + // empty name + ec = error::bad_field; + return; + } + name = make_string(first, p); + ++p; // eat ':' + char const* token_last = nullptr; + for(;;) + { + // eat leading ' ' and '\t' + for(;;++p) + { + if(p + 1 > last) + { + ec = error::need_more; + return; + } + if(! (*p == ' ' || *p == '\t')) + break; + } + // parse to CRLF + first = p; + p = parse_token_to_eol(p, last, token_last, ec); + if(ec) + return; + if(! p) + { + ec = error::bad_value; + return; + } + // Look 1 char past the CRLF to handle obs-fold. + if(p + 1 > last) + { + ec = error::need_more; + return; + } + token_last = + trim_back(token_last, first); + if(*p != ' ' && *p != '\t') + { + value = make_string(first, token_last); + return; + } + ++p; + if(token_last != first) + break; + } + buf.resize(0); + buf.append(first, token_last); + BOOST_ASSERT(! buf.empty()); +#ifndef BOOST_NO_EXCEPTIONS + try +#endif + { + for(;;) + { + // eat leading ' ' and '\t' + for(;;++p) + { + if(p + 1 > last) + { + ec = error::need_more; + return; + } + if(! (*p == ' ' || *p == '\t')) + break; + } + // parse to CRLF + first = p; + p = parse_token_to_eol(p, last, token_last, ec); + if(ec) + return; + if(! p) + { + ec = error::bad_value; + return; + } + // Look 1 char past the CRLF to handle obs-fold. + if(p + 1 > last) + { + ec = error::need_more; + return; + } + token_last = trim_back(token_last, first); + if(first != token_last) + { + buf.push_back(' '); + buf.append(first, token_last); + } + if(*p != ' ' && *p != '\t') + { + value = {buf.data(), buf.size()}; + return; + } + ++p; + } + } +#ifndef BOOST_NO_EXCEPTIONS + catch(std::length_error const&) + { + ec = error::header_limit; + return; + } +#endif +} + + +void +basic_parser_base:: +parse_chunk_extensions( + char const*& it, + char const* last, + error_code& ec) +{ +/* + chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] ) + BWS = *( SP / HTAB ) ; "Bad White Space" + chunk-ext-name = token + chunk-ext-val = token / quoted-string + token = 1*tchar + quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text + quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + obs-text = %x80-FF + + https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667 +*/ +loop: + if(it == last) + { + ec = error::need_more; + return; + } + if(*it != ' ' && *it != '\t' && *it != ';') + return; + // BWS + if(*it == ' ' || *it == '\t') + { + for(;;) + { + ++it; + if(it == last) + { + ec = error::need_more; + return; + } + if(*it != ' ' && *it != '\t') + break; + } + } + // ';' + if(*it != ';') + { + ec = error::bad_chunk_extension; + return; + } +semi: + ++it; // skip ';' + // BWS + for(;;) + { + if(it == last) + { + ec = error::need_more; + return; + } + if(*it != ' ' && *it != '\t') + break; + ++it; + } + // chunk-ext-name + if(! detail::is_token_char(*it)) + { + ec = error::bad_chunk_extension; + return; + } + for(;;) + { + ++it; + if(it == last) + { + ec = error::need_more; + return; + } + if(! detail::is_token_char(*it)) + break; + } + // BWS [ ";" / "=" ] + { + bool bws; + if(*it == ' ' || *it == '\t') + { + for(;;) + { + ++it; + if(it == last) + { + ec = error::need_more; + return; + } + if(*it != ' ' && *it != '\t') + break; + } + bws = true; + } + else + { + bws = false; + } + if(*it == ';') + goto semi; + if(*it != '=') + { + if(bws) + ec = error::bad_chunk_extension; + return; + } + ++it; // skip '=' + } + // BWS + for(;;) + { + if(it == last) + { + ec = error::need_more; + return; + } + if(*it != ' ' && *it != '\t') + break; + ++it; + } + // chunk-ext-val + if(*it != '"') + { + // token + if(! detail::is_token_char(*it)) + { + ec = error::bad_chunk_extension; + return; + } + for(;;) + { + ++it; + if(it == last) + { + ec = error::need_more; + return; + } + if(! detail::is_token_char(*it)) + break; + } + } + else + { + // quoted-string + for(;;) + { + ++it; + if(it == last) + { + ec = error::need_more; + return; + } + if(*it == '"') + break; + if(*it == '\\') + { + ++it; + if(it == last) + { + ec = error::need_more; + return; + } + } + } + ++it; + } + goto loop; +} + +} // detail +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/detail/chunk_encode.hpp b/boost/beast/http/detail/chunk_encode.hpp index 87ac9ec41e..fe38e7a6c8 100644 --- a/boost/beast/http/detail/chunk_encode.hpp +++ b/boost/beast/http/detail/chunk_encode.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -10,7 +10,6 @@ #ifndef BOOST_BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP #define BOOST_BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP -#include #include #include #include @@ -26,15 +25,15 @@ namespace detail { struct chunk_extensions { virtual ~chunk_extensions() = default; - virtual boost::asio::const_buffer str() = 0; + virtual net::const_buffer str() = 0; }; - + template struct chunk_extensions_impl : chunk_extensions { ChunkExtensions ext_; - chunk_extensions_impl(ChunkExtensions&& ext) + chunk_extensions_impl(ChunkExtensions&& ext) noexcept : ext_(std::move(ext)) { } @@ -44,7 +43,7 @@ struct chunk_extensions_impl : chunk_extensions { } - boost::asio::const_buffer + net::const_buffer str() override { auto const s = ext_.str(); @@ -88,7 +87,7 @@ class chunk_size struct sequence { - boost::asio::const_buffer b; + net::const_buffer b; char data[1 + 2 * sizeof(std::size_t)]; explicit @@ -104,7 +103,7 @@ class chunk_size std::shared_ptr sp_; public: - using value_type = boost::asio::const_buffer; + using value_type = net::const_buffer; using const_iterator = value_type const*; @@ -136,34 +135,37 @@ public: /// Returns a buffer sequence holding a CRLF for chunk encoding inline -boost::asio::const_buffer +net::const_buffer const& chunk_crlf() { - return {"\r\n", 2}; + static net::const_buffer const cb{"\r\n", 2}; + return cb; } /// Returns a buffer sequence holding a final chunk header inline -boost::asio::const_buffer +net::const_buffer const& chunk_last() { - return {"0\r\n", 3}; + static net::const_buffer const cb{"0\r\n", 3}; + return cb; } //------------------------------------------------------------------------------ +#if 0 template struct chunk_crlf_iter_type { class value_type { char const s[2] = {'\r', '\n'}; - + public: value_type() = default; operator - boost::asio::const_buffer() const + net::const_buffer() const { return {s, sizeof(s)}; } @@ -176,44 +178,19 @@ typename chunk_crlf_iter_type::value_type chunk_crlf_iter_type::value; using chunk_crlf_iter = chunk_crlf_iter_type; +#endif //------------------------------------------------------------------------------ -template -struct chunk_size0_iter_type -{ - class value_type - { - char const s[3] = {'0', '\r', '\n'}; - - public: - value_type() = default; - - operator - boost::asio::const_buffer() const - { - return {s, sizeof(s)}; - } - }; - static value_type value; -}; - -template -typename chunk_size0_iter_type::value_type -chunk_size0_iter_type::value; - -using chunk_size0_iter = chunk_size0_iter_type; - struct chunk_size0 { - using value_type = chunk_size0_iter::value_type; - + using value_type = net::const_buffer; using const_iterator = value_type const*; const_iterator begin() const { - return &chunk_size0_iter::value; + return &chunk_last(); } const_iterator diff --git a/boost/beast/http/detail/rfc7230.hpp b/boost/beast/http/detail/rfc7230.hpp index afe35691ae..99f4af05e1 100644 --- a/boost/beast/http/detail/rfc7230.hpp +++ b/boost/beast/http/detail/rfc7230.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -19,300 +19,54 @@ namespace beast { namespace http { namespace detail { -inline +BOOST_BEAST_DECL bool -is_digit(char c) -{ - return c >= '0' && c <= '9'; -} +is_digit(char c); -inline +BOOST_BEAST_DECL char -is_alpha(char c) -{ - static char constexpr tab[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240 - }; - BOOST_STATIC_ASSERT(sizeof(tab) == 256); - return tab[static_cast(c)]; -} +is_alpha(char c); -inline +BOOST_BEAST_DECL char -is_text(char c) -{ - // TEXT = - static char constexpr tab[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 - }; - BOOST_STATIC_ASSERT(sizeof(tab) == 256); - return tab[static_cast(c)]; -} +is_text(char c); -inline +BOOST_BEAST_DECL char -is_token_char(char c) -{ - /* - tchar = "!" | "#" | "$" | "%" | "&" | - "'" | "*" | "+" | "-" | "." | - "^" | "_" | "`" | "|" | "~" | - DIGIT | ALPHA - */ - static char constexpr tab[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240 - }; - BOOST_STATIC_ASSERT(sizeof(tab) == 256); - return tab[static_cast(c)]; -} +is_token_char(char c); -inline +BOOST_BEAST_DECL char -is_qdchar(char c) -{ - /* - qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text - */ - static char constexpr tab[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 80 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 - }; - BOOST_STATIC_ASSERT(sizeof(tab) == 256); - return tab[static_cast(c)]; -} +is_qdchar(char c); -inline +BOOST_BEAST_DECL char -is_qpchar(char c) -{ - /* - quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) - obs-text = %x80-FF - */ - static char constexpr tab[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 - }; - BOOST_STATIC_ASSERT(sizeof(tab) == 256); - return tab[static_cast(c)]; -} +is_qpchar(char c); + // converts to lower case, // returns 0 if not a valid text char // -inline +BOOST_BEAST_DECL char -to_value_char(char c) -{ - // TEXT = - static unsigned char constexpr tab[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32 - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48 - 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64 - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80 - 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96 - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112 - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128 - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144 - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160 - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176 - 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192 - 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208 - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224 - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240 - }; - BOOST_STATIC_ASSERT(sizeof(tab) == 256); - return static_cast(tab[static_cast(c)]); -} +to_value_char(char c); // VFALCO TODO Make this return unsigned? -inline +BOOST_BEAST_DECL std::int8_t -unhex(char c) -{ - static signed char constexpr tab[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48 - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240 - }; - BOOST_STATIC_ASSERT(sizeof(tab) == 256); - return tab[static_cast(c)]; -} - -template -inline -void -skip_ows(FwdIt& it, FwdIt const& end) -{ - while(it != end) - { - if(*it != ' ' && *it != '\t') - break; - ++it; - } -} +unhex(char c); -template -inline +BOOST_BEAST_DECL void -skip_ows_rev( - RanIt& it, RanIt const& first) -{ - while(it != first) - { - auto const c = it[-1]; - if(c != ' ' && c != '\t') - break; - --it; - } -} - -// obs-fold = CRLF 1*( SP / HTAB ) -// return `false` on parse error -// -template -inline -bool -skip_obs_fold( - FwdIt& it, FwdIt const& last) -{ - for(;;) - { - if(*it != '\r') - return true; - if(++it == last) - return false; - if(*it != '\n') - return false; - if(++it == last) - return false; - if(*it != ' ' && *it != '\t') - return false; - for(;;) - { - if(++it == last) - return true; - if(*it != ' ' && *it != '\t') - return true; - } - } -} +skip_ows(char const*& it, char const* end); -template +BOOST_BEAST_DECL void -skip_token(FwdIt& it, FwdIt const& last) -{ - while(it != last && is_token_char(*it)) - ++it; -} +skip_token(char const*& it, char const* last); -inline +BOOST_BEAST_DECL string_view -trim(string_view s) -{ - auto first = s.begin(); - auto last = s.end(); - skip_ows(first, last); - while(first != last) - { - auto const c = *std::prev(last); - if(c != ' ' && c != '\t') - break; - --last; - } - if(first == last) - return {}; - return {&*first, - static_cast(last - first)}; -} +trim(string_view s); struct param_iter { @@ -329,94 +83,11 @@ struct param_iter return first == it; } - template + BOOST_BEAST_DECL void increment(); }; -template -void -param_iter:: -increment() -{ -/* - param-list = *( OWS ";" OWS param ) - param = token OWS [ "=" OWS ( token / quoted-string ) ] - quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE - qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text - quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) - obs-text = %x80-FF -*/ - auto const err = - [&] - { - it = first; - }; - v.first = {}; - v.second = {}; - detail::skip_ows(it, last); - first = it; - if(it == last) - return err(); - if(*it != ';') - return err(); - ++it; - detail::skip_ows(it, last); - if(it == last) - return err(); - // param - if(! detail::is_token_char(*it)) - return err(); - auto const p0 = it; - skip_token(++it, last); - auto const p1 = it; - v.first = { &*p0, static_cast(p1 - p0) }; - detail::skip_ows(it, last); - if(it == last) - return; - if(*it == ';') - return; - if(*it != '=') - return err(); - ++it; - detail::skip_ows(it, last); - if(it == last) - return; - if(*it == '"') - { - // quoted-string - auto const p2 = it; - ++it; - for(;;) - { - if(it == last) - return err(); - auto c = *it++; - if(c == '"') - break; - if(detail::is_qdchar(c)) - continue; - if(c != '\\') - return err(); - if(it == last) - return err(); - c = *it++; - if(! detail::is_qpchar(c)) - return err(); - } - v.second = { &*p2, static_cast(it - p2) }; - } - else - { - // token - if(! detail::is_token_char(*it)) - return err(); - auto const p2 = it; - skip_token(++it, last); - v.second = { &*p2, static_cast(it - p2) }; - } -} - /* #token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ] */ @@ -424,44 +95,10 @@ struct opt_token_list_policy { using value_type = string_view; + BOOST_BEAST_DECL bool operator()(value_type& v, - char const*& it, string_view s) const - { - v = {}; - auto need_comma = it != s.data(); - for(;;) - { - detail::skip_ows(it, (s.data() + s.size())); - if(it == (s.data() + s.size())) - { - it = nullptr; - return true; - } - auto const c = *it; - if(detail::is_token_char(c)) - { - if(need_comma) - return false; - auto const p0 = it; - for(;;) - { - ++it; - if(it == (s.data() + s.size())) - break; - if(! detail::is_token_char(*it)) - break; - } - v = string_view{p0, - static_cast(it - p0)}; - return true; - } - if(c != ',') - return false; - need_comma = false; - ++it; - } - } + char const*& it, string_view s) const; }; } // detail @@ -469,5 +106,9 @@ struct opt_token_list_policy } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + #endif diff --git a/boost/beast/http/detail/rfc7230.ipp b/boost/beast/http/detail/rfc7230.ipp new file mode 100644 index 0000000000..612eaf9a02 --- /dev/null +++ b/boost/beast/http/detail/rfc7230.ipp @@ -0,0 +1,388 @@ +// +// 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_DETAIL_RFC7230_IPP +#define BOOST_BEAST_HTTP_DETAIL_RFC7230_IPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { +namespace detail { + +bool +is_digit(char c) +{ + return c >= '0' && c <= '9'; +} + +char +is_alpha(char c) +{ + static char constexpr tab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240 + }; + BOOST_STATIC_ASSERT(sizeof(tab) == 256); + return tab[static_cast(c)]; +} + +char +is_text(char c) +{ + // TEXT = + static char constexpr tab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 + }; + BOOST_STATIC_ASSERT(sizeof(tab) == 256); + return tab[static_cast(c)]; +} + +char +is_token_char(char c) +{ + /* + tchar = "!" | "#" | "$" | "%" | "&" | + "'" | "*" | "+" | "-" | "." | + "^" | "_" | "`" | "|" | "~" | + DIGIT | ALPHA + */ + static char constexpr tab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240 + }; + BOOST_STATIC_ASSERT(sizeof(tab) == 256); + return tab[static_cast(c)]; +} + +char +is_qdchar(char c) +{ + /* + qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text + */ + static char constexpr tab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 + }; + BOOST_STATIC_ASSERT(sizeof(tab) == 256); + return tab[static_cast(c)]; +} + +char +is_qpchar(char c) +{ + /* + quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + obs-text = %x80-FF + */ + static char constexpr tab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240 + }; + BOOST_STATIC_ASSERT(sizeof(tab) == 256); + return tab[static_cast(c)]; +} + +// converts to lower case, +// returns 0 if not a valid text char +// +char +to_value_char(char c) +{ + // TEXT = + static unsigned char constexpr tab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48 + 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64 + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80 + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96 + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112 + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128 + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144 + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160 + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176 + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192 + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208 + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224 + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240 + }; + BOOST_STATIC_ASSERT(sizeof(tab) == 256); + return static_cast(tab[static_cast(c)]); +} + +// VFALCO TODO Make this return unsigned? +std::int8_t +unhex(char c) +{ + static signed char constexpr tab[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48 + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240 + }; + BOOST_STATIC_ASSERT(sizeof(tab) == 256); + return tab[static_cast(c)]; +} + +void +skip_ows(char const*& it, char const* end) +{ + while(it != end) + { + if(*it != ' ' && *it != '\t') + break; + ++it; + } +} + +void +skip_token(char const*& it, char const* last) +{ + while(it != last && is_token_char(*it)) + ++it; +} + +string_view +trim(string_view s) +{ + auto first = s.begin(); + auto last = s.end(); + skip_ows(first, last); + while(first != last) + { + auto const c = *std::prev(last); + if(c != ' ' && c != '\t') + break; + --last; + } + if(first == last) + return {}; + return {&*first, + static_cast(last - first)}; +} + + +BOOST_BEAST_DECL +void +param_iter:: +increment() +{ +/* + param-list = *( OWS ";" OWS param ) + param = token OWS [ "=" OWS ( token / quoted-string ) ] + quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text + quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + obs-text = %x80-FF +*/ + auto const err = + [&] + { + it = first; + }; + v.first = {}; + v.second = {}; + detail::skip_ows(it, last); + first = it; + if(it == last) + return err(); + if(*it != ';') + return err(); + ++it; + detail::skip_ows(it, last); + if(it == last) + return err(); + // param + if(! detail::is_token_char(*it)) + return err(); + auto const p0 = it; + skip_token(++it, last); + auto const p1 = it; + v.first = { &*p0, static_cast(p1 - p0) }; + detail::skip_ows(it, last); + if(it == last) + return; + if(*it == ';') + return; + if(*it != '=') + return err(); + ++it; + detail::skip_ows(it, last); + if(it == last) + return; + if(*it == '"') + { + // quoted-string + auto const p2 = it; + ++it; + for(;;) + { + if(it == last) + return err(); + auto c = *it++; + if(c == '"') + break; + if(detail::is_qdchar(c)) + continue; + if(c != '\\') + return err(); + if(it == last) + return err(); + c = *it++; + if(! detail::is_qpchar(c)) + return err(); + } + v.second = { &*p2, static_cast(it - p2) }; + } + else + { + // token + if(! detail::is_token_char(*it)) + return err(); + auto const p2 = it; + skip_token(++it, last); + v.second = { &*p2, static_cast(it - p2) }; + } +} + +bool +opt_token_list_policy::operator()(value_type& v, + char const*& it, string_view s) const +{ + v = {}; + auto need_comma = it != s.data(); + for(;;) + { + detail::skip_ows(it, (s.data() + s.size())); + if(it == (s.data() + s.size())) + { + it = nullptr; + return true; + } + auto const c = *it; + if(detail::is_token_char(c)) + { + if(need_comma) + return false; + auto const p0 = it; + for(;;) + { + ++it; + if(it == (s.data() + s.size())) + break; + if(! detail::is_token_char(*it)) + break; + } + v = string_view{p0, + static_cast(it - p0)}; + return true; + } + if(c != ',') + return false; + need_comma = false; + ++it; + } +} + +} // detail +} // http +} // beast +} // boost + +#endif // BOOST_BEAST_HTTP_DETAIL_RFC7230_IPP + diff --git a/boost/beast/http/detail/type_traits.hpp b/boost/beast/http/detail/type_traits.hpp index b849446b0f..1da4c4e414 100644 --- a/boost/beast/http/detail/type_traits.hpp +++ b/boost/beast/http/detail/type_traits.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -19,12 +19,12 @@ namespace beast { namespace http { template -struct header; +class header; template -struct message; +class message; -template +template class parser; namespace detail { @@ -80,7 +80,7 @@ struct has_value_type > : std::true_type {}; -/** Determine if a @b Body type has a size +/** Determine if a Body type has a size This metafunction is equivalent to `std::true_type` if Body contains a static member function called `size`. diff --git a/boost/beast/http/dynamic_body.hpp b/boost/beast/http/dynamic_body.hpp index 9c3df8a1d2..a55fff45ef 100644 --- a/boost/beast/http/dynamic_body.hpp +++ b/boost/beast/http/dynamic_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -19,7 +19,7 @@ namespace http { /** A dynamic message body represented by a @ref multi_buffer - Meets the requirements of @b Body. + Meets the requirements of Body. */ using dynamic_body = basic_dynamic_body; diff --git a/boost/beast/http/empty_body.hpp b/boost/beast/http/empty_body.hpp index d56c14b4ef..40ea24614b 100644 --- a/boost/beast/http/empty_body.hpp +++ b/boost/beast/http/empty_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -19,7 +19,7 @@ namespace boost { namespace beast { namespace http { -/** An empty @b Body +/** An empty Body This body is used to represent messages which do not have a message body. If this body is used with a parser, and the @@ -54,10 +54,10 @@ struct empty_body /** The algorithm for parsing the body - Meets the requirements of @b BodyReader. + Meets the requirements of BodyReader. */ #if BOOST_BEAST_DOXYGEN - using reader = implementation_defined; + using reader = __implementation_defined__; #else struct reader { @@ -70,7 +70,7 @@ struct empty_body void init(boost::optional const&, error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } template @@ -85,22 +85,22 @@ struct empty_body void finish(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } }; #endif /** The algorithm for serializing the body - Meets the requirements of @b BodyWriter. + Meets the requirements of BodyWriter. */ #if BOOST_BEAST_DOXYGEN - using writer = implementation_defined; + using writer = __implementation_defined__; #else struct writer { using const_buffers_type = - boost::asio::const_buffer; + net::const_buffer; template explicit @@ -111,13 +111,13 @@ struct empty_body void init(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } boost::optional> get(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; return boost::none; } }; diff --git a/boost/beast/http/error.hpp b/boost/beast/http/error.hpp index b3d9fb1188..d0c287ebc9 100644 --- a/boost/beast/http/error.hpp +++ b/boost/beast/http/error.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -23,7 +23,7 @@ enum class error /** The end of the stream was reached. This error is returned when attempting to read HTTP data, - and the stream returns the error `boost::asio::error::eof` + and the stream returns the error `net::error::eof` before any octets corresponding to a new HTTP message have been received. */ @@ -51,7 +51,7 @@ enum class error octets into a message container which has the @ref empty_body body type. - @see @ref empty_body + @see empty_body */ unexpected_body, @@ -144,13 +144,25 @@ enum class error bad_chunk_extension, /// An obs-fold exceeded an internal limit. - bad_obs_fold + bad_obs_fold, + + /** The parser is stale. + + This happens when attempting to re-use a parser that has + already completed parsing a message. Programs must construct + a new parser for each message. This can be easily done by + storing the parser in an boost or std::optional container. + */ + stale_parser }; } // http } // beast } // boost +#include +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/boost/beast/http/field.hpp b/boost/beast/http/field.hpp index 09d9dfffdf..69eb253fdf 100644 --- a/boost/beast/http/field.hpp +++ b/boost/beast/http/field.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -380,6 +380,7 @@ enum class field : unsigned short @param f The field to convert */ +BOOST_BEAST_DECL string_view to_string(field f); @@ -390,6 +391,7 @@ to_string(field f); @return The corresponding field, or @ref field::unknown if no known field matches. */ +BOOST_BEAST_DECL field string_to_field(string_view s); @@ -405,6 +407,8 @@ operator<<(std::ostream& os, field f) } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/boost/beast/http/fields.hpp b/boost/beast/http/fields.hpp index 8d526867dc..927191d755 100644 --- a/boost/beast/http/fields.hpp +++ b/boost/beast/http/fields.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -45,10 +45,9 @@ namespace http { is iterated the fields are presented in the order of insertion, with fields having the same name following each other consecutively. - Meets the requirements of @b Fields + Meets the requirements of Fields - @tparam Allocator The allocator to use. This must meet the - requirements of @b Allocator. + @tparam Allocator The allocator to use. */ template class basic_fields @@ -85,7 +84,7 @@ public: char* data() const; - boost::asio::const_buffer + net::const_buffer buffer() const; protected: @@ -117,7 +116,7 @@ public: The case-comparison operation is defined only for low-ASCII characters. */ #if BOOST_BEAST_DOXYGEN - using key_compare = implementation_defined; + using key_compare = __implementation_defined__; #else struct key_compare : beast::iless #endif @@ -164,7 +163,7 @@ public: /// The algorithm used to serialize the header #if BOOST_BEAST_DOXYGEN - using writer = implementation_defined; + using writer = __implementation_defined__; #else class writer; #endif @@ -277,7 +276,7 @@ public: public: /// A constant iterator to the field sequence. #if BOOST_BEAST_DOXYGEN - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else using const_iterator = typename list_t::const_iterator; #endif @@ -768,6 +767,6 @@ using fields = basic_fields>; } // beast } // boost -#include +#include #endif diff --git a/boost/beast/http/file_body.hpp b/boost/beast/http/file_body.hpp index 42e7a15330..a78d7901de 100644 --- a/boost/beast/http/file_body.hpp +++ b/boost/beast/http/file_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -30,6 +30,8 @@ using file_body = basic_file_body; } // beast } // boost -#include +#ifndef BOOST_BEAST_NO_FILE_BODY_WIN32 +#include +#endif #endif diff --git a/boost/beast/http/impl/basic_parser.hpp b/boost/beast/http/impl/basic_parser.hpp new file mode 100644 index 0000000000..e25be7ce08 --- /dev/null +++ b/boost/beast/http/impl/basic_parser.hpp @@ -0,0 +1,78 @@ +// +// 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_HPP +#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP + +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { + +template +template +std::size_t +basic_parser:: +put(ConstBufferSequence const& buffers, + error_code& ec) +{ + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + auto const p = net::buffer_sequence_begin(buffers); + auto const last = net::buffer_sequence_end(buffers); + if(p == last) + { + ec = {}; + return 0; + } + if(std::next(p) == last) + { + // single buffer + return put(net::const_buffer(*p), ec); + } + auto const size = buffer_bytes(buffers); + if(size <= max_stack_buffer) + return put_from_stack(size, buffers, ec); + if(size > buf_len_) + { + // reallocate + buf_ = boost::make_unique_noinit(size); + buf_len_ = size; + } + // flatten + net::buffer_copy(net::buffer( + buf_.get(), buf_len_), buffers); + return put(net::const_buffer{ + buf_.get(), buf_len_}, ec); +} + +template +template +std::size_t +basic_parser:: +put_from_stack(std::size_t size, + ConstBufferSequence const& buffers, + error_code& ec) +{ + char buf[max_stack_buffer]; + net::buffer_copy(net::mutable_buffer( + buf, sizeof(buf)), buffers); + return put(net::const_buffer{ + buf, size}, ec); +} + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/basic_parser.ipp b/boost/beast/http/impl/basic_parser.ipp index 7a46092861..99947ae1f2 100644 --- a/boost/beast/http/impl/basic_parser.ipp +++ b/boost/beast/http/impl/basic_parser.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -10,14 +10,14 @@ #ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP +#include +#include +#include +#include #include -#include #include #include -#include -#include #include -#include #include #include @@ -25,26 +25,9 @@ namespace boost { namespace beast { namespace http { -template -template -basic_parser:: -basic_parser(basic_parser< - isRequest, OtherDerived>&& other) - : body_limit_(other.body_limit_) - , len_(other.len_) - , buf_(std::move(other.buf_)) - , buf_len_(other.buf_len_) - , skip_(other.skip_) - , header_limit_(other.header_limit_) - , status_(other.status_) - , state_(other.state_) - , f_(other.f_) -{ -} - -template +template bool -basic_parser:: +basic_parser:: keep_alive() const { BOOST_ASSERT(is_header_done()); @@ -61,10 +44,21 @@ keep_alive() const return (f_ & flagNeedEOF) == 0; } -template +template boost::optional -basic_parser:: +basic_parser:: content_length() const +{ + BOOST_ASSERT(is_header_done()); + if(! (f_ & flagContentLength)) + return boost::none; + return len0_; +} + +template +boost::optional +basic_parser:: +content_length_remaining() const { BOOST_ASSERT(is_header_done()); if(! (f_ & flagContentLength)) @@ -72,9 +66,9 @@ content_length() const return len_; } -template +template void -basic_parser:: +basic_parser:: skip(bool v) { BOOST_ASSERT(! got_some()); @@ -84,59 +78,18 @@ skip(bool v) f_ &= ~flagSkipBody; } -template -template +template std::size_t -basic_parser:: -put(ConstBufferSequence const& buffers, - error_code& ec) -{ - static_assert(boost::asio::is_const_buffer_sequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - using boost::asio::buffer_copy; - using boost::asio::buffer_size; - auto const p = boost::asio::buffer_sequence_begin(buffers); - auto const last = boost::asio::buffer_sequence_end(buffers); - if(p == last) - { - ec.assign(0, ec.category()); - return 0; - } - if(std::next(p) == last) - { - // single buffer - return put(boost::asio::const_buffer(*p), ec); - } - auto const size = buffer_size(buffers); - if(size <= max_stack_buffer) - return put_from_stack(size, buffers, ec); - if(size > buf_len_) - { - // reallocate - buf_ = boost::make_unique_noinit(size); - buf_len_ = size; - } - // flatten - buffer_copy(boost::asio::buffer( - buf_.get(), buf_len_), buffers); - return put(boost::asio::const_buffer{ - buf_.get(), buf_len_}, ec); -} - -template -std::size_t -basic_parser:: -put(boost::asio::const_buffer const& buffer, +basic_parser:: +put(net::const_buffer buffer, error_code& ec) { BOOST_ASSERT(state_ != state::complete); - using boost::asio::buffer_size; auto p = static_cast(buffer.data()); auto n = buffer.size(); auto const p0 = p; auto const p1 = p0 + n; - ec.assign(0, ec.category()); + ec = {}; loop: switch(state_) { @@ -207,7 +160,7 @@ loop: case state::body0: BOOST_ASSERT(! skip_); - impl().on_body_init_impl(content_length(), ec); + this->on_body_init_impl(content_length(), ec); if(ec) goto done; state_ = state::body; @@ -222,7 +175,7 @@ loop: case state::body_to_eof0: BOOST_ASSERT(! skip_); - impl().on_body_init_impl(content_length(), ec); + this->on_body_init_impl(content_length(), ec); if(ec) goto done; state_ = state::body_to_eof; @@ -236,7 +189,7 @@ loop: break; case state::chunk_header0: - impl().on_body_init_impl(content_length(), ec); + this->on_body_init_impl(content_length(), ec); if(ec) goto done; state_ = state::chunk_header; @@ -255,7 +208,7 @@ loop: break; case state::complete: - ec.assign(0, ec.category()); + ec = {}; goto done; } if(p < p1 && ! is_done() && eager()) @@ -267,9 +220,9 @@ done: return static_cast(p - p0); } -template +template void -basic_parser:: +basic_parser:: put_eof(error_code& ec) { BOOST_ASSERT(got_some()); @@ -286,35 +239,19 @@ put_eof(error_code& ec) ec = error::partial_message; return; } - ec.assign(0, ec.category()); + ec = {}; return; } - impl().on_finish_impl(ec); + ec = {}; + this->on_finish_impl(ec); if(ec) return; state_ = state::complete; } -template -template -std::size_t -basic_parser:: -put_from_stack(std::size_t size, - ConstBufferSequence const& buffers, - error_code& ec) -{ - char buf[max_stack_buffer]; - using boost::asio::buffer; - using boost::asio::buffer_copy; - buffer_copy(buffer(buf, sizeof(buf)), buffers); - return put(boost::asio::const_buffer{ - buf, size}, ec); -} - -template -inline +template void -basic_parser:: +basic_parser:: maybe_need_more( char const* p, std::size_t n, error_code& ec) @@ -344,10 +281,9 @@ maybe_need_more( skip_ = 0; } -template -inline +template void -basic_parser:: +basic_parser:: parse_start_line( char const*& in, char const* last, error_code& ec, std::true_type) @@ -393,7 +329,7 @@ parse_start_line( if(version >= 11) f_ |= flagHTTP11; - impl().on_request_impl(string_to_verb(method), + this->on_request_impl(string_to_verb(method), method, target, version, ec); if(ec) return; @@ -402,10 +338,9 @@ parse_start_line( state_ = state::fields; } -template -inline +template void -basic_parser:: +basic_parser:: parse_start_line( char const*& in, char const* last, error_code& ec, std::false_type) @@ -452,7 +387,7 @@ parse_start_line( if(version >= 11) f_ |= flagHTTP11; - impl().on_response_impl( + this->on_response_impl( status_, reason, version, ec); if(ec) return; @@ -461,9 +396,9 @@ parse_start_line( state_ = state::fields; } -template +template void -basic_parser:: +basic_parser:: parse_fields(char const*& in, char const* last, error_code& ec) { @@ -493,17 +428,16 @@ parse_fields(char const*& in, do_field(f, value, ec); if(ec) return; - impl().on_field_impl(f, name, value, ec); + this->on_field_impl(f, name, value, ec); if(ec) return; in = p; } } -template -inline +template void -basic_parser:: +basic_parser:: finish_header(error_code& ec, std::true_type) { // RFC 7230 section 3.3 @@ -538,24 +472,25 @@ finish_header(error_code& ec, std::true_type) else { len_ = 0; + len0_ = 0; state_ = state::complete; } - impl().on_header_impl(ec); + ec = {}; + this->on_header_impl(ec); if(ec) return; if(state_ == state::complete) { - impl().on_finish_impl(ec); + this->on_finish_impl(ec); if(ec) return; } } -template -inline +template void -basic_parser:: +basic_parser:: finish_header(error_code& ec, std::false_type) { // RFC 7230 section 3.3 @@ -600,25 +535,26 @@ finish_header(error_code& ec, std::false_type) state_ = state::body_to_eof0; } - impl().on_header_impl(ec); + ec = {}; + this->on_header_impl(ec); if(ec) return; if(state_ == state::complete) { - impl().on_finish_impl(ec); + this->on_finish_impl(ec); if(ec) return; } } -template -inline +template void -basic_parser:: +basic_parser:: parse_body(char const*& p, std::size_t n, error_code& ec) { - n = impl().on_body_impl(string_view{p, + ec = {}; + n = this->on_body_impl(string_view{p, beast::detail::clamp(len_, n)}, ec); p += n; len_ -= n; @@ -626,16 +562,15 @@ parse_body(char const*& p, return; if(len_ > 0) return; - impl().on_finish_impl(ec); + this->on_finish_impl(ec); if(ec) return; state_ = state::complete; } -template -inline +template void -basic_parser:: +basic_parser:: parse_body_to_eof(char const*& p, std::size_t n, error_code& ec) { @@ -645,15 +580,16 @@ parse_body_to_eof(char const*& p, return; } body_limit_ = body_limit_ - n; - n = impl().on_body_impl(string_view{p, n}, ec); + ec = {}; + n = this->on_body_impl(string_view{p, n}, ec); p += n; if(ec) return; } -template +template void -basic_parser:: +basic_parser:: parse_chunk_header(char const*& p0, std::size_t n, error_code& ec) { @@ -729,7 +665,7 @@ parse_chunk_header(char const*& p0, return; } auto const ext = make_string(start, p); - impl().on_chunk_header_impl(size, ext, ec); + this->on_chunk_header_impl(size, ext, ec); if(ec) return; len_ = size; @@ -772,7 +708,7 @@ parse_chunk_header(char const*& p0, return; } auto const ext = make_string(start, p); - impl().on_chunk_header_impl(0, ext, ec); + this->on_chunk_header_impl(0, ext, ec); if(ec) return; p = eol; @@ -782,20 +718,20 @@ parse_chunk_header(char const*& p0, BOOST_ASSERT(p == eom); p0 = eom; - impl().on_finish_impl(ec); + this->on_finish_impl(ec); if(ec) return; state_ = state::complete; } -template -inline +template void -basic_parser:: +basic_parser:: parse_chunk_body(char const*& p, std::size_t n, error_code& ec) { - n = impl().on_chunk_body_impl( + ec = {}; + n = this->on_chunk_body_impl( len_, string_view{p, beast::detail::clamp(len_, n)}, ec); p += n; @@ -804,9 +740,9 @@ parse_chunk_body(char const*& p, state_ = state::chunk_header; } -template +template void -basic_parser:: +basic_parser:: do_field(field f, string_view value, error_code& ec) { @@ -841,7 +777,7 @@ do_field(field f, continue; } } - ec.assign(0, ec.category()); + ec = {}; return; } @@ -870,8 +806,9 @@ do_field(field f, return; } - ec.assign(0, ec.category()); + ec = {}; len_ = v; + len0_ = v; f_ |= flagContentLength; return; } @@ -893,7 +830,7 @@ do_field(field f, return; } - ec.assign(0, ec.category()); + ec = {}; auto const v = token_list{value}; auto const p = std::find_if(v.begin(), v.end(), [&](typename token_list::value_type const& s) @@ -912,14 +849,19 @@ do_field(field f, // Upgrade if(f == field::upgrade) { - ec.assign(0, ec.category()); + ec = {}; f_ |= flagUpgrade; return; } - ec.assign(0, ec.category()); + ec = {}; } +#ifdef BOOST_BEAST_SOURCE +template class http::basic_parser; +template class http::basic_parser; +#endif + } // http } // beast } // boost diff --git a/boost/beast/http/impl/chunk_encode.hpp b/boost/beast/http/impl/chunk_encode.hpp new file mode 100644 index 0000000000..0272e3abb3 --- /dev/null +++ b/boost/beast/http/impl/chunk_encode.hpp @@ -0,0 +1,706 @@ +// +// 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_CHUNK_ENCODE_HPP +#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { + +inline +chunk_header:: +chunk_header(std::size_t size) + : view_( + size, + net::const_buffer{nullptr, 0}, + chunk_crlf{}) +{ + BOOST_ASSERT(size > 0); +} + +inline +chunk_header:: +chunk_header( + std::size_t size, + string_view extensions) + : view_( + size, + net::const_buffer{ + extensions.data(), extensions.size()}, + chunk_crlf{}) +{ + BOOST_ASSERT(size > 0); +} + +template +chunk_header:: +chunk_header( + std::size_t size, + ChunkExtensions&& extensions) + : exts_(std::make_shared::type>>( + std::forward(extensions))) + , view_( + size, + exts_->str(), + chunk_crlf{}) +{ + static_assert( + detail::is_chunk_extensions::value, + "ChunkExtensions requirements not met"); + BOOST_ASSERT(size > 0); +} + +template +chunk_header:: +chunk_header( + std::size_t size, + ChunkExtensions&& extensions, + Allocator const& allocator) + : exts_(std::allocate_shared::type>>(allocator, + std::forward(extensions))) + , view_( + size, + exts_->str(), + chunk_crlf{}) +{ + static_assert( + detail::is_chunk_extensions::value, + "ChunkExtensions requirements not met"); + BOOST_ASSERT(size > 0); +} + +//------------------------------------------------------------------------------ + +template +chunk_body:: +chunk_body(ConstBufferSequence const& buffers) + : view_( + buffer_bytes(buffers), + net::const_buffer{nullptr, 0}, + chunk_crlf{}, + buffers, + chunk_crlf{}) +{ +} + +template +chunk_body:: +chunk_body( + ConstBufferSequence const& buffers, + string_view extensions) + : view_( + buffer_bytes(buffers), + net::const_buffer{ + extensions.data(), extensions.size()}, + chunk_crlf{}, + buffers, + chunk_crlf{}) +{ +} + +template +template +chunk_body:: +chunk_body( + ConstBufferSequence const& buffers, + ChunkExtensions&& extensions) + : exts_(std::make_shared::type>>( + std::forward(extensions))) + , view_( + buffer_bytes(buffers), + exts_->str(), + chunk_crlf{}, + buffers, + chunk_crlf{}) +{ +} + +template +template +chunk_body:: +chunk_body( + ConstBufferSequence const& buffers, + ChunkExtensions&& extensions, + Allocator const& allocator) + : exts_(std::allocate_shared::type>>(allocator, + std::forward(extensions))) + , view_( + buffer_bytes(buffers), + exts_->str(), + chunk_crlf{}, + buffers, + chunk_crlf{}) +{ +} + +//------------------------------------------------------------------------------ + +template +template +auto +chunk_last:: +prepare(Trailer const& trailer, Allocator const& allocator) -> + buffers_type +{ + auto sp = std::allocate_shared(allocator, trailer); + sp_ = sp; + return sp->get(); +} + +template +auto +chunk_last:: +prepare(Trailer const& trailer, std::true_type) -> + buffers_type +{ + auto sp = std::make_shared< + typename Trailer::writer>(trailer); + sp_ = sp; + return sp->get(); +} + +template +auto +chunk_last:: +prepare(Trailer const& trailer, std::false_type) -> + buffers_type +{ + return trailer; +} + +template +chunk_last:: +chunk_last() + : view_( + detail::chunk_size0{}, + Trailer{}) +{ +} + +template +chunk_last:: +chunk_last(Trailer const& trailer) + : view_( + detail::chunk_size0{}, + prepare(trailer, is_fields{})) +{ +} + +template +template +chunk_last:: +chunk_last( + DeducedTrailer const& trailer, Allocator const& allocator) + : view_( + detail::chunk_size0{}, + prepare(trailer, allocator)) +{ +} + +//------------------------------------------------------------------------------ + +template +class basic_chunk_extensions::const_iterator +{ + friend class basic_chunk_extensions; + + using iter_type = char const*; + + iter_type it_; + typename basic_chunk_extensions::value_type value_; + + explicit + const_iterator(iter_type it) + : it_(it) + { + } + + void + increment(); + +public: + using value_type = typename + basic_chunk_extensions::value_type; + using pointer = value_type const*; + using reference = value_type const&; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::forward_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + bool + operator==(const_iterator const& other) const + { + return it_ == other.it_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*(); + + pointer + operator->() + { + return &(**this); + } + + const_iterator& + operator++() + { + increment(); + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + increment(); + return temp; + } +}; + +template +void +basic_chunk_extensions:: +const_iterator:: +increment() +{ + using beast::detail::varint_read; + auto n = varint_read(it_); + it_ += n; + n = varint_read(it_); + it_ += n; +} + +template +auto +basic_chunk_extensions:: +const_iterator:: +operator*() -> + reference +{ + using beast::detail::varint_read; + auto it = it_; + auto n = varint_read(it); + value_.first = string_view{it, n}; + it += n; + n = varint_read(it); + value_.second = string_view{it, n}; + return value_; +} + +//------------------------------------------------------------------------------ + +template +template +FwdIt +basic_chunk_extensions:: +do_parse(FwdIt it, FwdIt last, error_code& ec) +{ +/* + chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] ) + BWS = *( SP / HTAB ) ; "Bad White Space" + chunk-ext-name = token + chunk-ext-val = token / quoted-string + token = 1*tchar + quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text + quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + obs-text = %x80-FF + + https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667 +*/ + using beast::detail::varint_size; + using beast::detail::varint_write; + using CharT = char; + using Traits = std::char_traits; + range_.reserve(static_cast( + std::distance(it, last) * 1.2)); + range_.resize(0); + auto const emit_string = + [this](FwdIt from, FwdIt to) + { + auto const len = + std::distance(from, to); + auto const offset = range_.size(); + range_.resize( + offset + + varint_size(len) + + len); + auto dest = &range_[offset]; + varint_write(dest, len); + Traits::copy(dest, from, len); + }; + auto const emit_string_plus_empty = + [this](FwdIt from, FwdIt to) + { + auto const len = + std::distance(from, to); + auto const offset = range_.size(); + range_.resize( + offset + + varint_size(len) + + len + + varint_size(0)); + auto dest = &range_[offset]; + varint_write(dest, len); + Traits::copy(dest, from, len); + dest += len; + varint_write(dest, 0); + }; + auto const emit_empty_string = + [this] + { + auto const offset = range_.size(); + range_.resize(offset + varint_size(0)); + auto dest = &range_[offset]; + varint_write(dest, 0); + }; +loop: + if(it == last) + { + ec = {}; + return it; + } + // BWS + if(*it == ' ' || *it == '\t') + { + for(;;) + { + ++it; + if(it == last) + { + ec = error::bad_chunk_extension; + return it; + } + if(*it != ' ' && *it != '\t') + break; + } + } + // ';' + if(*it != ';') + { + ec = error::bad_chunk_extension; + return it; + } +semi: + ++it; // skip ';' + // BWS + for(;;) + { + if(it == last) + { + ec = error::bad_chunk_extension; + return it; + } + if(*it != ' ' && *it != '\t') + break; + ++it; + } + // chunk-ext-name + { + if(! detail::is_token_char(*it)) + { + ec = error::bad_chunk_extension; + return it; + } + auto const first = it; + for(;;) + { + ++it; + if(it == last) + { + emit_string_plus_empty(first, it); + return it; + } + if(! detail::is_token_char(*it)) + break; + } + emit_string(first, it); + } + // BWS [ ";" / "=" ] + for(;;) + { + if(*it != ' ' && *it != '\t') + break; + ++it; + if(it == last) + { + ec = error::bad_chunk_extension; + return it; + } + } + if(*it == ';') + { + emit_empty_string(); + goto semi; + } + if(*it != '=') + { + ec = error::bad_chunk_extension; + return it; + } + ++it; // skip '=' + // BWS + for(;;) + { + if(it == last) + { + ec = error::bad_chunk_extension; + return it; + } + if(*it != ' ' && *it != '\t') + break; + ++it; + } + // chunk-ext-val + if(*it != '"') + { + // token + if(! detail::is_token_char(*it)) + { + ec = error::bad_chunk_extension; + return it; + } + auto const first = it; + for(;;) + { + ++it; + if(it == last) + break; + if(! detail::is_token_char(*it)) + break; + } + emit_string(first, it); + if(it == last) + return it; + } + else + { + // quoted-string + auto const first = ++it; // skip DQUOTE + // first pass, count chars + std::size_t len = 0; + for(;;) + { + if(it == last) + { + ec = error::bad_chunk_extension; + return it; + } + if(*it == '"') + break; + if(*it == '\\') + { + ++it; + if(it == last) + { + ec = error::bad_chunk_extension; + return it; + } + } + ++len; + ++it; + } + // now build the string + auto const offset = range_.size(); + range_.resize( + offset + + varint_size(len) + + len); + auto dest = &range_[offset]; + varint_write(dest, len); + it = first; + for(;;) + { + BOOST_ASSERT(it != last); + if(*it == '"') + break; + if(*it == '\\') + { + ++it; + BOOST_ASSERT(it != last); + } + Traits::assign(*dest++, *it++); + } + ++it; // skip DQUOTE + } + goto loop; +} + +template +void +basic_chunk_extensions:: +do_insert(string_view name, string_view value) +{ +/* + chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) + chunk-ext-name = token + chunk-ext-val = token / quoted-string + token = 1*tchar + quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE + qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text + quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) + obs-text = %x80-FF +*/ + if(value.empty()) + { + s_.reserve(1 + name.size()); + s_.push_back(';'); + s_.append(name.data(), name.size()); + return; + } + + bool is_token = true; + for(auto const c : value) + { + if(! detail::is_token_char(c)) + { + is_token = false; + break; + } + } + if(is_token) + { + // token + s_.reserve(1 + name.size() + 1 + value.size()); + s_.push_back(';'); + s_.append(name.data(), name.size()); + s_.push_back('='); + s_.append(value.data(), value.size()); + } + else + { + // quoted-string + s_.reserve( + 1 + name.size() + 1 + + 1 + value.size() + 20 + 1); + s_.push_back(';'); + s_.append(name.data(), name.size()); + s_.append("=\"", 2); + for(auto const c : value) + { + if(c == '\\') + s_.append(R"(\\)", 2); + else if(c == '\"') + s_.append(R"(\")", 2); + else + s_.push_back(c); + } + s_.push_back('"'); + } +} + +template +void +basic_chunk_extensions:: +parse(string_view s, error_code& ec) +{ + do_parse(s.data(), s.data() + s.size(), ec); + if(! ec) + { + s_.clear(); + for(auto const& v : *this) + do_insert(v.first, v.second); + } +} + +template +void +basic_chunk_extensions:: +insert(string_view name) +{ + do_insert(name, {}); + + using beast::detail::varint_size; + using beast::detail::varint_write; + auto const offset = range_.size(); + range_.resize( + offset + + varint_size(name.size()) + + name.size() + + varint_size(0)); + auto dest = &range_[offset]; + varint_write(dest, name.size()); + std::memcpy(dest, name.data(), name.size()); + dest += name.size(); + varint_write(dest, 0); +} + +template +void +basic_chunk_extensions:: +insert(string_view name, string_view value) +{ + do_insert(name, value); + + using beast::detail::varint_size; + using beast::detail::varint_write; + auto const offset = range_.size(); + range_.resize( + offset + + varint_size(name.size()) + + name.size() + + varint_size(value.size()) + + value.size()); + auto dest = &range_[offset]; + varint_write(dest, name.size()); + std::memcpy(dest, name.data(), name.size()); + dest += name.size(); + varint_write(dest, value.size()); + std::memcpy(dest, value.data(), value.size()); +} + +template +auto +basic_chunk_extensions:: +begin() const -> + const_iterator +{ + return const_iterator{range_.data()}; +} + +template +auto +basic_chunk_extensions:: +end() const -> + const_iterator +{ + return const_iterator{ + range_.data() + range_.size()}; +} + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/chunk_encode.ipp b/boost/beast/http/impl/chunk_encode.ipp deleted file mode 100644 index 51296041f7..0000000000 --- a/boost/beast/http/impl/chunk_encode.ipp +++ /dev/null @@ -1,707 +0,0 @@ -// -// Copyright (c) 2016-2017 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_CHUNK_ENCODE_IPP -#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_IPP - -#include -#include -#include -#include - -namespace boost { -namespace beast { -namespace http { - -inline -chunk_header:: -chunk_header(std::size_t size) - : view_( - size, - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}) -{ - BOOST_ASSERT(size > 0); -} - -inline -chunk_header:: -chunk_header( - std::size_t size, - string_view extensions) - : view_( - size, - boost::asio::const_buffer{ - extensions.data(), extensions.size()}, - chunk_crlf{}) -{ - BOOST_ASSERT(size > 0); -} - -template -chunk_header:: -chunk_header( - std::size_t size, - ChunkExtensions&& extensions) - : exts_(std::make_shared::type>>( - std::forward(extensions))) - , view_( - size, - exts_->str(), - chunk_crlf{}) -{ - static_assert( - detail::is_chunk_extensions::value, - "ChunkExtensions requirements not met"); - BOOST_ASSERT(size > 0); -} - -template -chunk_header:: -chunk_header( - std::size_t size, - ChunkExtensions&& extensions, - Allocator const& allocator) - : exts_(std::allocate_shared::type>>(allocator, - std::forward(extensions))) - , view_( - size, - exts_->str(), - chunk_crlf{}) -{ - static_assert( - detail::is_chunk_extensions::value, - "ChunkExtensions requirements not met"); - BOOST_ASSERT(size > 0); -} - -//------------------------------------------------------------------------------ - -template -chunk_body:: -chunk_body(ConstBufferSequence const& buffers) - : view_( - boost::asio::buffer_size(buffers), - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}, - buffers, - chunk_crlf{}) -{ -} - -template -chunk_body:: -chunk_body( - ConstBufferSequence const& buffers, - string_view extensions) - : view_( - boost::asio::buffer_size(buffers), - boost::asio::const_buffer{ - extensions.data(), extensions.size()}, - chunk_crlf{}, - buffers, - chunk_crlf{}) -{ -} - -template -template -chunk_body:: -chunk_body( - ConstBufferSequence const& buffers, - ChunkExtensions&& extensions) - : exts_(std::make_shared::type>>( - std::forward(extensions))) - , view_( - boost::asio::buffer_size(buffers), - exts_->str(), - chunk_crlf{}, - buffers, - chunk_crlf{}) -{ -} - -template -template -chunk_body:: -chunk_body( - ConstBufferSequence const& buffers, - ChunkExtensions&& extensions, - Allocator const& allocator) - : exts_(std::allocate_shared::type>>(allocator, - std::forward(extensions))) - , view_( - boost::asio::buffer_size(buffers), - exts_->str(), - chunk_crlf{}, - buffers, - chunk_crlf{}) -{ -} - -//------------------------------------------------------------------------------ - -template -template -auto -chunk_last:: -prepare(Trailer const& trailer, Allocator const& allocator) -> - buffers_type -{ - auto sp = std::allocate_shared(allocator, trailer); - sp_ = sp; - return sp->get(); -} - -template -auto -chunk_last:: -prepare(Trailer const& trailer, std::true_type) -> - buffers_type -{ - auto sp = std::make_shared< - typename Trailer::writer>(trailer); - sp_ = sp; - return sp->get(); -} - -template -auto -chunk_last:: -prepare(Trailer const& trailer, std::false_type) -> - buffers_type -{ - return trailer; -} - -template -chunk_last:: -chunk_last() - : view_( - detail::chunk_size0{}, - Trailer{}) -{ -} - -template -chunk_last:: -chunk_last(Trailer const& trailer) - : view_( - detail::chunk_size0{}, - prepare(trailer, is_fields{})) -{ -} - -template -template -chunk_last:: -chunk_last( - DeducedTrailer const& trailer, Allocator const& allocator) - : view_( - detail::chunk_size0{}, - prepare(trailer, allocator)) -{ -} - -//------------------------------------------------------------------------------ - -template -class basic_chunk_extensions::const_iterator -{ - friend class basic_chunk_extensions; - - using iter_type = char const*; - - iter_type it_; - typename basic_chunk_extensions::value_type value_; - - explicit - const_iterator(iter_type it) - : it_(it) - { - } - - void - increment(); - -public: - using value_type = typename - basic_chunk_extensions::value_type; - using pointer = value_type const*; - using reference = value_type const&; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::forward_iterator_tag; - - const_iterator() = default; - const_iterator(const_iterator&& other) = default; - const_iterator(const_iterator const& other) = default; - const_iterator& operator=(const_iterator&& other) = default; - const_iterator& operator=(const_iterator const& other) = default; - - bool - operator==(const_iterator const& other) const - { - return it_ == other.it_; - } - - bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*(); - - pointer - operator->() - { - return &(**this); - } - - const_iterator& - operator++() - { - increment(); - return *this; - } - - const_iterator - operator++(int) - { - auto temp = *this; - increment(); - return temp; - } -}; - -template -void -basic_chunk_extensions:: -const_iterator:: -increment() -{ - using beast::detail::varint_read; - auto n = varint_read(it_); - it_ += n; - n = varint_read(it_); - it_ += n; -} - -template -auto -basic_chunk_extensions:: -const_iterator:: -operator*() -> - reference -{ - using beast::detail::varint_read; - auto it = it_; - auto n = varint_read(it); - value_.first = string_view{it, n}; - it += n; - n = varint_read(it); - value_.second = string_view{it, n}; - return value_; -} - -//------------------------------------------------------------------------------ - -template -template -FwdIt -basic_chunk_extensions:: -do_parse(FwdIt it, FwdIt last, error_code& ec) -{ -/* - chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] ) - BWS = *( SP / HTAB ) ; "Bad White Space" - chunk-ext-name = token - chunk-ext-val = token / quoted-string - token = 1*tchar - quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE - qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text - quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) - obs-text = %x80-FF - - https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667 -*/ - using beast::detail::varint_size; - using beast::detail::varint_write; - using CharT = char; - using Traits = std::char_traits; - range_.reserve(static_cast( - std::distance(it, last) * 1.2)); - range_.resize(0); - auto const emit_string = - [this](FwdIt from, FwdIt to) - { - auto const len = - std::distance(from, to); - auto const offset = range_.size(); - range_.resize( - offset + - varint_size(len) + - len); - auto dest = &range_[offset]; - varint_write(dest, len); - Traits::copy(dest, from, len); - }; - auto const emit_string_plus_empty = - [this](FwdIt from, FwdIt to) - { - auto const len = - std::distance(from, to); - auto const offset = range_.size(); - range_.resize( - offset + - varint_size(len) + - len + - varint_size(0)); - auto dest = &range_[offset]; - varint_write(dest, len); - Traits::copy(dest, from, len); - dest += len; - varint_write(dest, 0); - }; - auto const emit_empty_string = - [this] - { - auto const offset = range_.size(); - range_.resize(offset + varint_size(0)); - auto dest = &range_[offset]; - varint_write(dest, 0); - }; -loop: - if(it == last) - { - ec.assign(0, ec.category()); - return it; - } - // BWS - if(*it == ' ' || *it == '\t') - { - for(;;) - { - ++it; - if(it == last) - { - ec = error::bad_chunk_extension; - return it; - } - if(*it != ' ' && *it != '\t') - break; - } - } - // ';' - if(*it != ';') - { - ec = error::bad_chunk_extension; - return it; - } -semi: - ++it; // skip ';' - // BWS - for(;;) - { - if(it == last) - { - ec = error::bad_chunk_extension; - return it; - } - if(*it != ' ' && *it != '\t') - break; - ++it; - } - // chunk-ext-name - { - if(! detail::is_token_char(*it)) - { - ec = error::bad_chunk_extension; - return it; - } - auto const first = it; - for(;;) - { - ++it; - if(it == last) - { - emit_string_plus_empty(first, it); - return it; - } - if(! detail::is_token_char(*it)) - break; - } - emit_string(first, it); - } - // BWS [ ";" / "=" ] - for(;;) - { - if(*it != ' ' && *it != '\t') - break; - ++it; - if(it == last) - { - ec = error::bad_chunk_extension; - return it; - } - } - if(*it == ';') - { - emit_empty_string(); - goto semi; - } - if(*it != '=') - { - ec = error::bad_chunk_extension; - return it; - } - ++it; // skip '=' - // BWS - for(;;) - { - if(it == last) - { - ec = error::bad_chunk_extension; - return it; - } - if(*it != ' ' && *it != '\t') - break; - ++it; - } - // chunk-ext-val - if(*it != '"') - { - // token - if(! detail::is_token_char(*it)) - { - ec = error::bad_chunk_extension; - return it; - } - auto const first = it; - for(;;) - { - ++it; - if(it == last) - break; - if(! detail::is_token_char(*it)) - break; - } - emit_string(first, it); - if(it == last) - return it; - } - else - { - // quoted-string - auto const first = ++it; // skip DQUOTE - // first pass, count chars - std::size_t len = 0; - for(;;) - { - if(it == last) - { - ec = error::bad_chunk_extension; - return it; - } - if(*it == '"') - break; - if(*it == '\\') - { - ++it; - if(it == last) - { - ec = error::bad_chunk_extension; - return it; - } - } - ++len; - ++it; - } - // now build the string - auto const offset = range_.size(); - range_.resize( - offset + - varint_size(len) + - len); - auto dest = &range_[offset]; - varint_write(dest, len); - it = first; - for(;;) - { - BOOST_ASSERT(it != last); - if(*it == '"') - break; - if(*it == '\\') - { - ++it; - BOOST_ASSERT(it != last); - } - Traits::assign(*dest++, *it++); - } - ++it; // skip DQUOTE - } - goto loop; -} - -template -void -basic_chunk_extensions:: -do_insert(string_view name, string_view value) -{ -/* - chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) - chunk-ext-name = token - chunk-ext-val = token / quoted-string - token = 1*tchar - quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE - qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text - quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) - obs-text = %x80-FF -*/ - if(value.empty()) - { - s_.reserve(1 + name.size()); - s_.push_back(';'); - s_.append(name.data(), name.size()); - return; - } - - bool is_token = true; - for(auto const c : value) - { - if(! detail::is_token_char(c)) - { - is_token = false; - break; - } - } - if(is_token) - { - // token - s_.reserve(1 + name.size() + 1 + value.size()); - s_.push_back(';'); - s_.append(name.data(), name.size()); - s_.push_back('='); - s_.append(value.data(), value.size()); - } - else - { - // quoted-string - s_.reserve( - 1 + name.size() + 1 + - 1 + value.size() + 20 + 1); - s_.push_back(';'); - s_.append(name.data(), name.size()); - s_.append("=\"", 2); - for(auto const c : value) - { - if(c == '\\') - s_.append(R"(\\)", 2); - else if(c == '\"') - s_.append(R"(\")", 2); - else - s_.push_back(c); - } - s_.push_back('"'); - } -} - -template -void -basic_chunk_extensions:: -parse(string_view s, error_code& ec) -{ - do_parse(s.data(), s.data() + s.size(), ec); - if(! ec) - { - s_.clear(); - for(auto const& v : *this) - do_insert(v.first, v.second); - } -} - -template -void -basic_chunk_extensions:: -insert(string_view name) -{ - do_insert(name, {}); - - using beast::detail::varint_size; - using beast::detail::varint_write; - auto const offset = range_.size(); - range_.resize( - offset + - varint_size(name.size()) + - name.size() + - varint_size(0)); - auto dest = &range_[offset]; - varint_write(dest, name.size()); - std::memcpy(dest, name.data(), name.size()); - dest += name.size(); - varint_write(dest, 0); -} - -template -void -basic_chunk_extensions:: -insert(string_view name, string_view value) -{ - do_insert(name, value); - - using beast::detail::varint_size; - using beast::detail::varint_write; - auto const offset = range_.size(); - range_.resize( - offset + - varint_size(name.size()) + - name.size() + - varint_size(value.size()) + - value.size()); - auto dest = &range_[offset]; - varint_write(dest, name.size()); - std::memcpy(dest, name.data(), name.size()); - dest += name.size(); - varint_write(dest, value.size()); - std::memcpy(dest, value.data(), value.size()); -} - -template -inline -auto -basic_chunk_extensions:: -begin() const -> - const_iterator -{ - return const_iterator{range_.data()}; -} - -template -inline -auto -basic_chunk_extensions:: -end() const -> - const_iterator -{ - return const_iterator{ - range_.data() + range_.size()}; -} - -} // http -} // beast -} // boost - -#endif diff --git a/boost/beast/http/impl/error.hpp b/boost/beast/http/impl/error.hpp new file mode 100644 index 0000000000..b4efaa05ca --- /dev/null +++ b/boost/beast/http/impl/error.hpp @@ -0,0 +1,37 @@ +// +// 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_ERROR_HPP +#define BOOST_BEAST_HTTP_IMPL_ERROR_HPP + +#include + +namespace boost { +namespace system { +template<> +struct is_error_code_enum<::boost::beast::http::error> +{ + static bool const value = true; +}; +} // system +} // boost + +namespace boost { +namespace beast { +namespace http { + +BOOST_BEAST_DECL +error_code +make_error_code(error ev); + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/error.ipp b/boost/beast/http/impl/error.ipp index b6320cf4c9..e0167b308e 100644 --- a/boost/beast/http/impl/error.ipp +++ b/boost/beast/http/impl/error.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -10,18 +10,10 @@ #ifndef BOOST_BEAST_HTTP_IMPL_ERROR_IPP #define BOOST_BEAST_HTTP_IMPL_ERROR_IPP +#include #include namespace boost { - -namespace system { -template<> -struct is_error_code_enum -{ - static bool const value = true; -}; -} // system - namespace beast { namespace http { namespace detail { @@ -63,6 +55,7 @@ public: case error::bad_chunk: return "bad chunk"; case error::bad_chunk_extension: return "bad chunk extension"; case error::bad_obs_fold: return "bad obs-fold"; + case error::stale_parser: return "stale parser"; default: return "beast.http error"; @@ -96,7 +89,6 @@ public: } // detail -inline error_code make_error_code(error ev) { diff --git a/boost/beast/http/impl/field.ipp b/boost/beast/http/impl/field.ipp index b61a0ba8a2..46af6f8552 100644 --- a/boost/beast/http/impl/field.ipp +++ b/boost/beast/http/impl/field.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -10,11 +10,10 @@ #ifndef BOOST_BEAST_HTTP_IMPL_FIELD_IPP #define BOOST_BEAST_HTTP_IMPL_FIELD_IPP -#include +#include #include #include -#include -#include +#include #include namespace boost { @@ -28,58 +27,66 @@ struct field_table using array_type = std::array; - struct hash + // Strings are converted to lowercase + static + std::uint32_t + digest(string_view s) { - std::size_t - operator()(string_view s) const + std::uint32_t r = 0; + std::size_t n = s.size(); + unsigned char const* p =reinterpret_cast< + unsigned char const*>(s.data()); + while(n >= 4) { - auto const n = s.size(); - return - beast::detail::ascii_tolower(s[0]) * - beast::detail::ascii_tolower(s[n/2]) ^ - beast::detail::ascii_tolower(s[n-1]); // hist[] = 331, 10, max_load_factor = 0.15f + std::uint32_t v; + std::memcpy(&v, p, 4); + r = r * 5 + ( v | 0x20202020 ); + p += 4; + n -= 4; } - }; + while( n > 0 ) + { + r = r * 5 + ( *p | 0x20 ); + ++p; + --n; + } + return r; + } - struct iequal + // This comparison is case-insensitive, and the + // strings must contain only valid http field characters. + static + bool + equals(string_view lhs, string_view rhs) { - // assumes inputs have equal length - bool - operator()( - string_view lhs, - string_view rhs) const + using Int = std::uint32_t; // or std::size_t + auto n = lhs.size(); + if(n != rhs.size()) + return false; + auto p1 = lhs.data(); + auto p2 = rhs.data(); + auto constexpr S = sizeof(Int); + auto constexpr Mask = static_cast( + 0xDFDFDFDFDFDFDFDF & ~Int{0}); + for(; n >= S; p1 += S, p2 += S, n -= S) { - auto p1 = lhs.data(); - auto p2 = rhs.data(); - auto pend = p1 + lhs.size(); - char a, b; - while(p1 < pend) - { - a = *p1++; - b = *p2++; - if(a != b) - goto slow; - } - return true; - - while(p1 < pend) - { - slow: - if( beast::detail::ascii_tolower(a) != - beast::detail::ascii_tolower(b)) - return false; - a = *p1++; - b = *p2++; - } - return true; + Int v1, v2; + std::memcpy( &v1, p1, S ); + std::memcpy( &v2, p2, S ); + if((v1 ^ v2) & Mask) + return false; } - }; - - using map_type = std::unordered_map< - string_view, field, hash, iequal>; + for(; n; ++p1, ++p2, --n) + if(( *p1 ^ *p2) & 0xDF) + return false; + return true; + } array_type by_name_; - std::vector by_size_; + + enum { N = 5155 }; + unsigned char map_[ N ][ 2 ] = {}; + /* From: @@ -442,58 +449,43 @@ struct field_table "Xref" }}) { - // find the longest field length - std::size_t high = 0; - for(auto const& s : by_name_) - if(high < s.size()) - high = s.size(); - // build by_size map - // skip field::unknown - by_size_.resize(high + 1); - for(auto& map : by_size_) - map.max_load_factor(.15f); - for(std::size_t i = 1; - i < by_name_.size(); ++i) + for(std::size_t i = 1, n = 256; i < n; ++i) { - auto const& s = by_name_[i]; - by_size_[s.size()].emplace( - s, static_cast(i)); + auto sv = by_name_[ i ]; + auto h = digest(sv); + auto j = h % N; + BOOST_ASSERT(map_[j][0] == 0); + map_[j][0] = static_cast(i); } -#if 0 - // This snippet calculates the performance - // of the hash function and map settings + for(std::size_t i = 256, n = by_name_.size(); i < n; ++i) { - std::vector hist; - for(auto const& map : by_size_) - { - for(std::size_t i = 0; i < map.bucket_count(); ++i) - { - auto const n = map.bucket_size(i); - if(n > 0) - { - if(hist.size() < n) - hist.resize(n); - ++hist[n-1]; - } - } - } + auto sv = by_name_[i]; + auto h = digest(sv); + auto j = h % N; + BOOST_ASSERT(map_[j][1] == 0); + map_[j][1] = static_cast(i - 255); } -#endif } field string_to_field(string_view s) const { - if(s.size() >= by_size_.size()) + auto h = digest(s); + auto j = h % N; + int i = map_[j][0]; + string_view s2 = by_name_[i]; + if(i != 0 && equals(s, s2)) + return static_cast(i); + i = map_[j][1]; + if(i == 0) return field::unknown; - auto const& map = by_size_[s.size()]; - if(map.empty()) - return field::unknown; - auto it = map.find(s); - if(it == map.end()) - return field::unknown; - return it->second; + i += 255; + s2 = by_name_[i]; + + if(equals(s, s2)) + return static_cast(i); + return field::unknown; } // @@ -522,7 +514,7 @@ struct field_table } }; -inline +BOOST_BEAST_DECL field_table const& get_field_table() { @@ -530,7 +522,7 @@ get_field_table() return tab; } -template +BOOST_BEAST_DECL string_view to_string(field f) { @@ -541,14 +533,12 @@ to_string(field f) } // detail -inline string_view to_string(field f) { return detail::to_string(f); } -inline field string_to_field(string_view s) { diff --git a/boost/beast/http/impl/fields.hpp b/boost/beast/http/impl/fields.hpp new file mode 100644 index 0000000000..9616af829c --- /dev/null +++ b/boost/beast/http/impl/fields.hpp @@ -0,0 +1,1405 @@ +// +// 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_FIELDS_HPP +#define BOOST_BEAST_HTTP_IMPL_FIELDS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000 + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 +#ifndef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR +#define BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR +#endif +#endif + +namespace boost { +namespace beast { +namespace http { + +template +class basic_fields::writer +{ +public: + using iter_type = typename list_t::const_iterator; + + struct field_iterator + { + iter_type it_; + + using value_type = net::const_buffer; + using pointer = value_type const*; + using reference = value_type const; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + field_iterator() = default; + field_iterator(field_iterator&& other) = default; + field_iterator(field_iterator const& other) = default; + field_iterator& operator=(field_iterator&& other) = default; + field_iterator& operator=(field_iterator const& other) = default; + + explicit + field_iterator(iter_type it) + : it_(it) + { + } + + bool + operator==(field_iterator const& other) const + { + return it_ == other.it_; + } + + bool + operator!=(field_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + return it_->buffer(); + } + + field_iterator& + operator++() + { + ++it_; + return *this; + } + + field_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + field_iterator& + operator--() + { + --it_; + return *this; + } + + field_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + }; + + class field_range + { + field_iterator first_; + field_iterator last_; + + public: + using const_iterator = + field_iterator; + + using value_type = + typename const_iterator::value_type; + + field_range(iter_type first, iter_type last) + : first_(first) + , last_(last) + { + } + + const_iterator + begin() const + { + return first_; + } + + const_iterator + end() const + { + return last_; + } + }; + + using view_type = buffers_cat_view< + net::const_buffer, + net::const_buffer, + net::const_buffer, + field_range, + chunk_crlf>; + + basic_fields const& f_; + boost::optional view_; + char buf_[13]; + +public: + using const_buffers_type = + beast::detail::buffers_ref; + + writer(basic_fields const& f, + unsigned version, verb v); + + writer(basic_fields const& f, + unsigned version, unsigned code); + + writer(basic_fields const& f); + + const_buffers_type + get() const + { + return const_buffers_type(*view_); + } +}; + +template +basic_fields::writer:: +writer(basic_fields const& f) + : f_(f) +{ + view_.emplace( + net::const_buffer{nullptr, 0}, + net::const_buffer{nullptr, 0}, + net::const_buffer{nullptr, 0}, + field_range(f_.list_.begin(), f_.list_.end()), + chunk_crlf()); +} + +template +basic_fields::writer:: +writer(basic_fields const& f, + unsigned version, verb v) + : f_(f) +{ +/* + request + "" + " " + " HTTP/X.Y\r\n" (11 chars) +*/ + string_view sv; + if(v == verb::unknown) + sv = f_.get_method_impl(); + else + sv = to_string(v); + + // target_or_reason_ has a leading SP + + buf_[0] = ' '; + buf_[1] = 'H'; + buf_[2] = 'T'; + buf_[3] = 'T'; + buf_[4] = 'P'; + buf_[5] = '/'; + buf_[6] = '0' + static_cast(version / 10); + buf_[7] = '.'; + buf_[8] = '0' + static_cast(version % 10); + buf_[9] = '\r'; + buf_[10]= '\n'; + + view_.emplace( + net::const_buffer{sv.data(), sv.size()}, + net::const_buffer{ + f_.target_or_reason_.data(), + f_.target_or_reason_.size()}, + net::const_buffer{buf_, 11}, + field_range(f_.list_.begin(), f_.list_.end()), + chunk_crlf()); +} + +template +basic_fields::writer:: +writer(basic_fields const& f, + unsigned version, unsigned code) + : f_(f) +{ +/* + response + "HTTP/X.Y ### " (13 chars) + "" + "\r\n" +*/ + buf_[0] = 'H'; + buf_[1] = 'T'; + buf_[2] = 'T'; + buf_[3] = 'P'; + buf_[4] = '/'; + buf_[5] = '0' + static_cast(version / 10); + buf_[6] = '.'; + buf_[7] = '0' + static_cast(version % 10); + buf_[8] = ' '; + buf_[9] = '0' + static_cast(code / 100); + buf_[10]= '0' + static_cast((code / 10) % 10); + buf_[11]= '0' + static_cast(code % 10); + buf_[12]= ' '; + + string_view sv; + if(! f_.target_or_reason_.empty()) + sv = f_.target_or_reason_; + else + sv = obsolete_reason(static_cast(code)); + + view_.emplace( + net::const_buffer{buf_, 13}, + net::const_buffer{sv.data(), sv.size()}, + net::const_buffer{"\r\n", 2}, + field_range(f_.list_.begin(), f_.list_.end()), + chunk_crlf{}); +} + +//------------------------------------------------------------------------------ + +template +char* +basic_fields:: +value_type:: +data() const +{ + return const_cast( + reinterpret_cast( + static_cast(this) + 1)); +} + +template +net::const_buffer +basic_fields:: +value_type:: +buffer() const +{ + return net::const_buffer{data(), + static_cast(off_) + len_ + 2}; +} + +template +basic_fields:: +value_type:: +value_type(field name, + string_view sname, string_view value) + : off_(static_cast(sname.size() + 2)) + , len_(static_cast(value.size())) + , f_(name) +{ + //BOOST_ASSERT(name == field::unknown || + // iequals(sname, to_string(name))); + char* p = data(); + p[off_-2] = ':'; + p[off_-1] = ' '; + p[off_ + len_] = '\r'; + p[off_ + len_ + 1] = '\n'; + sname.copy(p, sname.size()); + value.copy(p + off_, value.size()); +} + +template +field +basic_fields:: +value_type:: +name() const +{ + return f_; +} + +template +string_view const +basic_fields:: +value_type:: +name_string() const +{ + return {data(), + static_cast(off_ - 2)}; +} + +template +string_view const +basic_fields:: +value_type:: +value() const +{ + return {data() + off_, + static_cast(len_)}; +} + +template +basic_fields:: +element:: +element(field name, + string_view sname, string_view value) + : value_type(name, sname, value) +{ +} + +//------------------------------------------------------------------------------ + +template +basic_fields:: +~basic_fields() +{ + delete_list(); + realloc_string(method_, {}); + realloc_string( + target_or_reason_, {}); +} + +template +basic_fields:: +basic_fields(Allocator const& alloc) noexcept + : boost::empty_value(boost::empty_init_t(), alloc) +{ +} + +template +basic_fields:: +basic_fields(basic_fields&& other) noexcept + : boost::empty_value(boost::empty_init_t(), + std::move(other.get())) + , set_(std::move(other.set_)) + , list_(std::move(other.list_)) + , method_(boost::exchange(other.method_, {})) + , target_or_reason_(boost::exchange(other.target_or_reason_, {})) +{ +} + +template +basic_fields:: +basic_fields(basic_fields&& other, Allocator const& alloc) + : boost::empty_value(boost::empty_init_t(), alloc) +{ + if(this->get() != other.get()) + { + copy_all(other); + other.clear_all(); + } + else + { + set_ = std::move(other.set_); + list_ = std::move(other.list_); + method_ = other.method_; + target_or_reason_ = other.target_or_reason_; + } +} + +template +basic_fields:: +basic_fields(basic_fields const& other) + : boost::empty_value(boost::empty_init_t(), alloc_traits:: + select_on_container_copy_construction(other.get())) +{ + copy_all(other); +} + +template +basic_fields:: +basic_fields(basic_fields const& other, + Allocator const& alloc) + : boost::empty_value(boost::empty_init_t(), alloc) +{ + copy_all(other); +} + +template +template +basic_fields:: +basic_fields(basic_fields const& other) +{ + copy_all(other); +} + +template +template +basic_fields:: +basic_fields(basic_fields const& other, + Allocator const& alloc) + : boost::empty_value(boost::empty_init_t(), alloc) +{ + copy_all(other); +} + +template +auto +basic_fields:: +operator=(basic_fields&& other) noexcept( + alloc_traits::propagate_on_container_move_assignment::value) + -> basic_fields& +{ + static_assert(is_nothrow_move_assignable::value, + "Allocator must be noexcept assignable."); + if(this == &other) + return *this; + move_assign(other, std::integral_constant{}); + return *this; +} + +template +auto +basic_fields:: +operator=(basic_fields const& other) -> + basic_fields& +{ + copy_assign(other, std::integral_constant{}); + return *this; +} + +template +template +auto +basic_fields:: +operator=(basic_fields const& other) -> + basic_fields& +{ + clear_all(); + copy_all(other); + return *this; +} + +//------------------------------------------------------------------------------ +// +// Element access +// +//------------------------------------------------------------------------------ + +template +string_view const +basic_fields:: +at(field name) const +{ + BOOST_ASSERT(name != field::unknown); + auto const it = find(name); + if(it == end()) + BOOST_THROW_EXCEPTION(std::out_of_range{ + "field not found"}); + return it->value(); +} + +template +string_view const +basic_fields:: +at(string_view name) const +{ + auto const it = find(name); + if(it == end()) + BOOST_THROW_EXCEPTION(std::out_of_range{ + "field not found"}); + return it->value(); +} + +template +string_view const +basic_fields:: +operator[](field name) const +{ + BOOST_ASSERT(name != field::unknown); + auto const it = find(name); + if(it == end()) + return {}; + return it->value(); +} + +template +string_view const +basic_fields:: +operator[](string_view name) const +{ + auto const it = find(name); + if(it == end()) + return {}; + return it->value(); +} + +//------------------------------------------------------------------------------ +// +// Modifiers +// +//------------------------------------------------------------------------------ + +template +void +basic_fields:: +clear() +{ + delete_list(); + set_.clear(); + list_.clear(); +} + +template +inline +void +basic_fields:: +insert(field name, string_param const& value) +{ + BOOST_ASSERT(name != field::unknown); + insert(name, to_string(name), value); +} + +template +void +basic_fields:: +insert(string_view sname, string_param const& value) +{ + auto const name = + string_to_field(sname); + insert(name, sname, value); +} + +template +void +basic_fields:: +insert(field name, + string_view sname, string_param const& value) +{ + auto& e = new_element(name, sname, + static_cast(value)); + auto const before = + set_.upper_bound(sname, key_compare{}); + if(before == set_.begin()) + { + BOOST_ASSERT(count(sname) == 0); + set_.insert_before(before, e); + list_.push_back(e); + return; + } + auto const last = std::prev(before); + // VFALCO is it worth comparing `field name` first? + if(! iequals(sname, last->name_string())) + { + BOOST_ASSERT(count(sname) == 0); + set_.insert_before(before, e); + list_.push_back(e); + return; + } + // keep duplicate fields together in the list + set_.insert_before(before, e); + list_.insert(++list_.iterator_to(*last), e); +} + +template +void +basic_fields:: +set(field name, string_param const& value) +{ + BOOST_ASSERT(name != field::unknown); + set_element(new_element(name, to_string(name), + static_cast(value))); +} + +template +void +basic_fields:: +set(string_view sname, string_param const& value) +{ + set_element(new_element( + string_to_field(sname), sname, + static_cast(value))); +} + +template +auto +basic_fields:: +erase(const_iterator pos) -> + const_iterator +{ + auto next = pos; + auto& e = *next++; + set_.erase(e); + list_.erase(pos); + delete_element(const_cast(e)); + return next; +} + +template +std::size_t +basic_fields:: +erase(field name) +{ + BOOST_ASSERT(name != field::unknown); + return erase(to_string(name)); +} + +template +std::size_t +basic_fields:: +erase(string_view name) +{ + std::size_t n =0; + set_.erase_and_dispose(name, key_compare{}, + [&](element* e) + { + ++n; + list_.erase(list_.iterator_to(*e)); + delete_element(*e); + }); + return n; +} + +template +void +basic_fields:: +swap(basic_fields& other) +{ + swap(other, std::integral_constant{}); +} + +template +void +swap( + basic_fields& lhs, + basic_fields& rhs) +{ + lhs.swap(rhs); +} + +//------------------------------------------------------------------------------ +// +// Lookup +// +//------------------------------------------------------------------------------ + +template +inline +std::size_t +basic_fields:: +count(field name) const +{ + BOOST_ASSERT(name != field::unknown); + return count(to_string(name)); +} + +template +std::size_t +basic_fields:: +count(string_view name) const +{ + return set_.count(name, key_compare{}); +} + +template +inline +auto +basic_fields:: +find(field name) const -> + const_iterator +{ + BOOST_ASSERT(name != field::unknown); + return find(to_string(name)); +} + +template +auto +basic_fields:: +find(string_view name) const -> + const_iterator +{ + auto const it = set_.find( + name, key_compare{}); + if(it == set_.end()) + return list_.end(); + return list_.iterator_to(*it); +} + +template +inline +auto +basic_fields:: +equal_range(field name) const -> + std::pair +{ + BOOST_ASSERT(name != field::unknown); + return equal_range(to_string(name)); +} + +template +auto +basic_fields:: +equal_range(string_view name) const -> + std::pair +{ + auto result = + set_.equal_range(name, key_compare{}); + if(result.first == result.second) + return {list_.end(), list_.end()}; + return { + list_.iterator_to(*result.first), + ++list_.iterator_to(*(--result.second))}; +} + +//------------------------------------------------------------------------------ + +namespace detail { + +// Filter a token list +// +template +void +filter_token_list( + String& s, + string_view value, + Pred&& pred) +{ + token_list te{value}; + auto it = te.begin(); + auto last = te.end(); + if(it == last) + return; + while(pred(*it)) + if(++it == last) + return; + s.append(it->data(), it->size()); + while(++it != last) + { + if(! pred(*it)) + { + s.append(", "); + s.append(it->data(), it->size()); + } + } +} + +// Filter the last item in a token list +template +void +filter_token_list_last( + String& s, + string_view value, + Pred&& pred) +{ + token_list te{value}; + if(te.begin() != te.end()) + { + auto it = te.begin(); + auto next = std::next(it); + if(next == te.end()) + { + if(! pred(*it)) + s.append(it->data(), it->size()); + return; + } + s.append(it->data(), it->size()); + for(;;) + { + it = next; + next = std::next(it); + if(next == te.end()) + { + if(! pred(*it)) + { + s.append(", "); + s.append(it->data(), it->size()); + } + return; + } + s.append(", "); + s.append(it->data(), it->size()); + } + } +} + +template +void +keep_alive_impl( + String& s, string_view value, + unsigned version, bool keep_alive) +{ + if(version < 11) + { + if(keep_alive) + { + // remove close + filter_token_list(s, value, + [](string_view s) + { + return iequals(s, "close"); + }); + // add keep-alive + if(s.empty()) + s.append("keep-alive"); + else if(! token_list{value}.exists("keep-alive")) + s.append(", keep-alive"); + } + else + { + // remove close and keep-alive + filter_token_list(s, value, + [](string_view s) + { + return + iequals(s, "close") || + iequals(s, "keep-alive"); + }); + } + } + else + { + if(keep_alive) + { + // remove close and keep-alive + filter_token_list(s, value, + [](string_view s) + { + return + iequals(s, "close") || + iequals(s, "keep-alive"); + }); + } + else + { + // remove keep-alive + filter_token_list(s, value, + [](string_view s) + { + return iequals(s, "keep-alive"); + }); + // add close + if(s.empty()) + s.append("close"); + else if(! token_list{value}.exists("close")) + s.append(", close"); + } + } +} + +} // detail + +//------------------------------------------------------------------------------ + +// Fields + +template +inline +string_view +basic_fields:: +get_method_impl() const +{ + return method_; +} + +template +inline +string_view +basic_fields:: +get_target_impl() const +{ + if(target_or_reason_.empty()) + return target_or_reason_; + return { + target_or_reason_.data() + 1, + target_or_reason_.size() - 1}; +} + +template +inline +string_view +basic_fields:: +get_reason_impl() const +{ + return target_or_reason_; +} + +template +bool +basic_fields:: +get_chunked_impl() const +{ + auto const te = token_list{ + (*this)[field::transfer_encoding]}; + for(auto it = te.begin(); it != te.end();) + { + auto const next = std::next(it); + if(next == te.end()) + return iequals(*it, "chunked"); + it = next; + } + return false; +} + +template +bool +basic_fields:: +get_keep_alive_impl(unsigned version) const +{ + auto const it = find(field::connection); + if(version < 11) + { + if(it == end()) + return false; + return token_list{ + it->value()}.exists("keep-alive"); + } + if(it == end()) + return true; + return ! token_list{ + it->value()}.exists("close"); +} + +template +bool +basic_fields:: +has_content_length_impl() const +{ + return count(field::content_length) > 0; +} + +template +inline +void +basic_fields:: +set_method_impl(string_view s) +{ + realloc_string(method_, s); +} + +template +inline +void +basic_fields:: +set_target_impl(string_view s) +{ + realloc_target( + target_or_reason_, s); +} + +template +inline +void +basic_fields:: +set_reason_impl(string_view s) +{ + realloc_string( + target_or_reason_, s); +} + +template +void +basic_fields:: +set_chunked_impl(bool value) +{ + auto it = find(field::transfer_encoding); + if(value) + { + // append "chunked" + if(it == end()) + { + set(field::transfer_encoding, "chunked"); + return; + } + auto const te = token_list{it->value()}; + for(auto itt = te.begin();;) + { + auto const next = std::next(itt); + if(next == te.end()) + { + if(iequals(*itt, "chunked")) + return; // already set + break; + } + itt = next; + } + static_string buf; + if(! beast::detail::sum_exceeds( + it->value().size(), 9u, buf.max_size())) + { + buf.append(it->value().data(), it->value().size()); + buf.append(", chunked", 9); + set(field::transfer_encoding, buf); + } + else + { + #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 + std::string s; + #else + using A = + typename beast::detail::allocator_traits< + Allocator>::template rebind_alloc; + std::basic_string< + char, + std::char_traits, + A> s{A{this->get()}}; + #endif + s.reserve(it->value().size() + 9); + s.append(it->value().data(), it->value().size()); + s.append(", chunked", 9); + set(field::transfer_encoding, s); + } + return; + } + // filter "chunked" + if(it == end()) + return; +#ifndef BOOST_NO_EXCEPTIONS + try + { + static_string buf; + detail::filter_token_list_last(buf, it->value(), + [](string_view s) + { + return iequals(s, "chunked"); + }); + if(! buf.empty()) + set(field::transfer_encoding, buf); + else + erase(field::transfer_encoding); + } + catch(std::length_error const&) +#endif + { + #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 + std::string s; + #else + using A = + typename beast::detail::allocator_traits< + Allocator>::template rebind_alloc; + std::basic_string< + char, + std::char_traits, + A> s{A{this->get()}}; + #endif + s.reserve(it->value().size()); + detail::filter_token_list_last(s, it->value(), + [](string_view s) + { + return iequals(s, "chunked"); + }); + if(! s.empty()) + set(field::transfer_encoding, s); + else + erase(field::transfer_encoding); + } +} + +template +void +basic_fields:: +set_content_length_impl( + boost::optional const& value) +{ + if(! value) + erase(field::content_length); + else + set(field::content_length, *value); +} + +template +void +basic_fields:: +set_keep_alive_impl( + unsigned version, bool keep_alive) +{ + // VFALCO What about Proxy-Connection ? + auto const value = (*this)[field::connection]; +#ifndef BOOST_NO_EXCEPTIONS + try + { + static_string buf; + detail::keep_alive_impl( + buf, value, version, keep_alive); + if(buf.empty()) + erase(field::connection); + else + set(field::connection, buf); + } + catch(std::length_error const&) +#endif + { + #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 + std::string s; + #else + using A = + typename beast::detail::allocator_traits< + Allocator>::template rebind_alloc; + std::basic_string< + char, + std::char_traits, + A> s{A{this->get()}}; + #endif + s.reserve(value.size()); + detail::keep_alive_impl( + s, value, version, keep_alive); + if(s.empty()) + erase(field::connection); + else + set(field::connection, s); + } +} + +//------------------------------------------------------------------------------ + +template +auto +basic_fields:: +new_element(field name, + string_view sname, string_view value) -> + element& +{ + if(sname.size() + 2 > + (std::numeric_limits::max)()) + BOOST_THROW_EXCEPTION(std::length_error{ + "field name too large"}); + if(value.size() + 2 > + (std::numeric_limits::max)()) + BOOST_THROW_EXCEPTION(std::length_error{ + "field value too large"}); + value = detail::trim(value); + std::uint16_t const off = + static_cast(sname.size() + 2); + std::uint16_t const len = + static_cast(value.size()); + auto a = rebind_type{this->get()}; + auto const p = alloc_traits::allocate(a, + (sizeof(element) + off + len + 2 + sizeof(align_type) - 1) / + sizeof(align_type)); + return *(::new(p) element(name, sname, value)); +} + +template +void +basic_fields:: +delete_element(element& e) +{ + auto a = rebind_type{this->get()}; + auto const n = + (sizeof(element) + e.off_ + e.len_ + 2 + sizeof(align_type) - 1) / + sizeof(align_type); + e.~element(); + alloc_traits::deallocate(a, &e, n); + //reinterpret_cast(&e), n); +} + +template +void +basic_fields:: +set_element(element& e) +{ + auto it = set_.lower_bound( + e.name_string(), key_compare{}); + if(it == set_.end() || ! iequals( + e.name_string(), it->name_string())) + { + set_.insert_before(it, e); + list_.push_back(e); + return; + } + for(;;) + { + auto next = it; + ++next; + set_.erase(it); + list_.erase(list_.iterator_to(*it)); + delete_element(*it); + it = next; + if(it == set_.end() || + ! iequals(e.name_string(), it->name_string())) + break; + } + set_.insert_before(it, e); + list_.push_back(e); +} + +template +void +basic_fields:: +realloc_string(string_view& dest, string_view s) +{ + if(dest.empty() && s.empty()) + return; + auto a = typename beast::detail::allocator_traits< + Allocator>::template rebind_alloc< + char>(this->get()); + char* p = nullptr; + if(! s.empty()) + { + p = a.allocate(s.size()); + s.copy(p, s.size()); + } + if(! dest.empty()) + a.deallocate(const_cast( + dest.data()), dest.size()); + if(p) + dest = {p, s.size()}; + else + dest = {}; +} + +template +void +basic_fields:: +realloc_target( + string_view& dest, string_view s) +{ + // The target string are stored with an + // extra space at the beginning to help + // the writer class. + if(dest.empty() && s.empty()) + return; + auto a = typename beast::detail::allocator_traits< + Allocator>::template rebind_alloc< + char>(this->get()); + char* p = nullptr; + if(! s.empty()) + { + p = a.allocate(1 + s.size()); + p[0] = ' '; + s.copy(p + 1, s.size()); + } + if(! dest.empty()) + a.deallocate(const_cast( + dest.data()), dest.size()); + if(p) + dest = {p, 1 + s.size()}; + else + dest = {}; +} + +template +template +void +basic_fields:: +copy_all(basic_fields const& other) +{ + for(auto const& e : other.list_) + insert(e.name(), e.name_string(), e.value()); + realloc_string(method_, other.method_); + realloc_string(target_or_reason_, + other.target_or_reason_); +} + +template +void +basic_fields:: +clear_all() +{ + clear(); + realloc_string(method_, {}); + realloc_string(target_or_reason_, {}); +} + +template +void +basic_fields:: +delete_list() +{ + for(auto it = list_.begin(); it != list_.end();) + delete_element(*it++); +} + +//------------------------------------------------------------------------------ + +template +inline +void +basic_fields:: +move_assign(basic_fields& other, std::true_type) +{ + clear_all(); + set_ = std::move(other.set_); + list_ = std::move(other.list_); + method_ = other.method_; + target_or_reason_ = other.target_or_reason_; + other.method_ = {}; + other.target_or_reason_ = {}; + this->get() = other.get(); +} + +template +inline +void +basic_fields:: +move_assign(basic_fields& other, std::false_type) +{ + clear_all(); + if(this->get() != other.get()) + { + copy_all(other); + other.clear_all(); + } + else + { + set_ = std::move(other.set_); + list_ = std::move(other.list_); + method_ = other.method_; + target_or_reason_ = other.target_or_reason_; + other.method_ = {}; + other.target_or_reason_ = {}; + } +} + +template +inline +void +basic_fields:: +copy_assign(basic_fields const& other, std::true_type) +{ + clear_all(); + this->get() = other.get(); + copy_all(other); +} + +template +inline +void +basic_fields:: +copy_assign(basic_fields const& other, std::false_type) +{ + clear_all(); + copy_all(other); +} + +template +inline +void +basic_fields:: +swap(basic_fields& other, std::true_type) +{ + using std::swap; + swap(this->get(), other.get()); + swap(set_, other.set_); + swap(list_, other.list_); + swap(method_, other.method_); + swap(target_or_reason_, other.target_or_reason_); +} + +template +inline +void +basic_fields:: +swap(basic_fields& other, std::false_type) +{ + BOOST_ASSERT(this->get() == other.get()); + using std::swap; + swap(set_, other.set_); + swap(list_, other.list_); + swap(method_, other.method_); + swap(target_or_reason_, other.target_or_reason_); +} + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/fields.ipp b/boost/beast/http/impl/fields.ipp deleted file mode 100644 index f34a8e7471..0000000000 --- a/boost/beast/http/impl/fields.ipp +++ /dev/null @@ -1,1399 +0,0 @@ -// -// Copyright (c) 2016-2017 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_FIELDS_IPP -#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000 - // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 -#ifndef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR -#define BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR -#endif -#endif - -namespace boost { -namespace beast { -namespace http { - -template -class basic_fields::writer -{ -public: - using iter_type = typename list_t::const_iterator; - - struct field_iterator - { - iter_type it_; - - using value_type = boost::asio::const_buffer; - using pointer = value_type const*; - using reference = value_type const; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - field_iterator() = default; - field_iterator(field_iterator&& other) = default; - field_iterator(field_iterator const& other) = default; - field_iterator& operator=(field_iterator&& other) = default; - field_iterator& operator=(field_iterator const& other) = default; - - explicit - field_iterator(iter_type it) - : it_(it) - { - } - - bool - operator==(field_iterator const& other) const - { - return it_ == other.it_; - } - - bool - operator!=(field_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const - { - return it_->buffer(); - } - - field_iterator& - operator++() - { - ++it_; - return *this; - } - - field_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - - field_iterator& - operator--() - { - --it_; - return *this; - } - - field_iterator - operator--(int) - { - auto temp = *this; - --(*this); - return temp; - } - }; - - class field_range - { - field_iterator first_; - field_iterator last_; - - public: - using const_iterator = - field_iterator; - - using value_type = - typename const_iterator::value_type; - - field_range(iter_type first, iter_type last) - : first_(first) - , last_(last) - { - } - - const_iterator - begin() const - { - return first_; - } - - const_iterator - end() const - { - return last_; - } - }; - - using view_type = buffers_cat_view< - boost::asio::const_buffer, - boost::asio::const_buffer, - boost::asio::const_buffer, - field_range, - chunk_crlf>; - - basic_fields const& f_; - boost::optional view_; - char buf_[13]; - -public: - using const_buffers_type = - beast::detail::buffers_ref; - - writer(basic_fields const& f, - unsigned version, verb v); - - writer(basic_fields const& f, - unsigned version, unsigned code); - - writer(basic_fields const& f); - - const_buffers_type - get() const - { - return const_buffers_type(*view_); - } -}; - -template -basic_fields::writer:: -writer(basic_fields const& f) - : f_(f) -{ - view_.emplace( - boost::asio::const_buffer{nullptr, 0}, - boost::asio::const_buffer{nullptr, 0}, - boost::asio::const_buffer{nullptr, 0}, - field_range(f_.list_.begin(), f_.list_.end()), - chunk_crlf()); -} - -template -basic_fields::writer:: -writer(basic_fields const& f, - unsigned version, verb v) - : f_(f) -{ -/* - request - "" - " " - " HTTP/X.Y\r\n" (11 chars) -*/ - string_view sv; - if(v == verb::unknown) - sv = f_.get_method_impl(); - else - sv = to_string(v); - - // target_or_reason_ has a leading SP - - buf_[0] = ' '; - buf_[1] = 'H'; - buf_[2] = 'T'; - buf_[3] = 'T'; - buf_[4] = 'P'; - buf_[5] = '/'; - buf_[6] = '0' + static_cast(version / 10); - buf_[7] = '.'; - buf_[8] = '0' + static_cast(version % 10); - buf_[9] = '\r'; - buf_[10]= '\n'; - - view_.emplace( - boost::asio::const_buffer{sv.data(), sv.size()}, - boost::asio::const_buffer{ - f_.target_or_reason_.data(), - f_.target_or_reason_.size()}, - boost::asio::const_buffer{buf_, 11}, - field_range(f_.list_.begin(), f_.list_.end()), - chunk_crlf()); -} - -template -basic_fields::writer:: -writer(basic_fields const& f, - unsigned version, unsigned code) - : f_(f) -{ -/* - response - "HTTP/X.Y ### " (13 chars) - "" - "\r\n" -*/ - buf_[0] = 'H'; - buf_[1] = 'T'; - buf_[2] = 'T'; - buf_[3] = 'P'; - buf_[4] = '/'; - buf_[5] = '0' + static_cast(version / 10); - buf_[6] = '.'; - buf_[7] = '0' + static_cast(version % 10); - buf_[8] = ' '; - buf_[9] = '0' + static_cast(code / 100); - buf_[10]= '0' + static_cast((code / 10) % 10); - buf_[11]= '0' + static_cast(code % 10); - buf_[12]= ' '; - - string_view sv; - if(! f_.target_or_reason_.empty()) - sv = f_.target_or_reason_; - else - sv = obsolete_reason(static_cast(code)); - - view_.emplace( - boost::asio::const_buffer{buf_, 13}, - boost::asio::const_buffer{sv.data(), sv.size()}, - boost::asio::const_buffer{"\r\n", 2}, - field_range(f_.list_.begin(), f_.list_.end()), - chunk_crlf{}); -} - -//------------------------------------------------------------------------------ - -template -char* -basic_fields:: -value_type:: -data() const -{ - return const_cast( - reinterpret_cast( - static_cast(this) + 1)); -} - -template -boost::asio::const_buffer -basic_fields:: -value_type:: -buffer() const -{ - return boost::asio::const_buffer{data(), - static_cast(off_) + len_ + 2}; -} - -template -basic_fields:: -value_type:: -value_type(field name, - string_view sname, string_view value) - : off_(static_cast(sname.size() + 2)) - , len_(static_cast(value.size())) - , f_(name) -{ - //BOOST_ASSERT(name == field::unknown || - // iequals(sname, to_string(name))); - char* p = data(); - p[off_-2] = ':'; - p[off_-1] = ' '; - p[off_ + len_] = '\r'; - p[off_ + len_ + 1] = '\n'; - sname.copy(p, sname.size()); - value.copy(p + off_, value.size()); -} - -template -field -basic_fields:: -value_type:: -name() const -{ - return f_; -} - -template -string_view const -basic_fields:: -value_type:: -name_string() const -{ - return {data(), - static_cast(off_ - 2)}; -} - -template -string_view const -basic_fields:: -value_type:: -value() const -{ - return {data() + off_, - static_cast(len_)}; -} - -template -basic_fields:: -element:: -element(field name, - string_view sname, string_view value) - : value_type(name, sname, value) -{ -} - -//------------------------------------------------------------------------------ - -template -basic_fields:: -~basic_fields() -{ - delete_list(); - realloc_string(method_, {}); - realloc_string( - target_or_reason_, {}); -} - -template -basic_fields:: -basic_fields(Allocator const& alloc) noexcept - : boost::empty_value(boost::empty_init_t(), alloc) -{ -} - -template -basic_fields:: -basic_fields(basic_fields&& other) noexcept - : boost::empty_value(boost::empty_init_t(), - std::move(other.get())) - , set_(std::move(other.set_)) - , list_(std::move(other.list_)) - , method_(boost::exchange(other.method_, {})) - , target_or_reason_(boost::exchange(other.target_or_reason_, {})) -{ -} - -template -basic_fields:: -basic_fields(basic_fields&& other, Allocator const& alloc) - : boost::empty_value(boost::empty_init_t(), alloc) -{ - if(this->get() != other.get()) - { - copy_all(other); - other.clear_all(); - } - else - { - set_ = std::move(other.set_); - list_ = std::move(other.list_); - method_ = other.method_; - target_or_reason_ = other.target_or_reason_; - } -} - -template -basic_fields:: -basic_fields(basic_fields const& other) - : boost::empty_value(boost::empty_init_t(), alloc_traits:: - select_on_container_copy_construction(other.get())) -{ - copy_all(other); -} - -template -basic_fields:: -basic_fields(basic_fields const& other, - Allocator const& alloc) - : boost::empty_value(boost::empty_init_t(), alloc) -{ - copy_all(other); -} - -template -template -basic_fields:: -basic_fields(basic_fields const& other) -{ - copy_all(other); -} - -template -template -basic_fields:: -basic_fields(basic_fields const& other, - Allocator const& alloc) - : boost::empty_value(boost::empty_init_t(), alloc) -{ - copy_all(other); -} - -template -auto -basic_fields:: -operator=(basic_fields&& other) noexcept( - alloc_traits::propagate_on_container_move_assignment::value) - -> basic_fields& -{ - static_assert(is_nothrow_move_assignable::value, - "Allocator must be noexcept assignable."); - if(this == &other) - return *this; - move_assign(other, std::integral_constant{}); - return *this; -} - -template -auto -basic_fields:: -operator=(basic_fields const& other) -> - basic_fields& -{ - copy_assign(other, std::integral_constant{}); - return *this; -} - -template -template -auto -basic_fields:: -operator=(basic_fields const& other) -> - basic_fields& -{ - clear_all(); - copy_all(other); - return *this; -} - -//------------------------------------------------------------------------------ -// -// Element access -// -//------------------------------------------------------------------------------ - -template -string_view const -basic_fields:: -at(field name) const -{ - BOOST_ASSERT(name != field::unknown); - auto const it = find(name); - if(it == end()) - BOOST_THROW_EXCEPTION(std::out_of_range{ - "field not found"}); - return it->value(); -} - -template -string_view const -basic_fields:: -at(string_view name) const -{ - auto const it = find(name); - if(it == end()) - BOOST_THROW_EXCEPTION(std::out_of_range{ - "field not found"}); - return it->value(); -} - -template -string_view const -basic_fields:: -operator[](field name) const -{ - BOOST_ASSERT(name != field::unknown); - auto const it = find(name); - if(it == end()) - return {}; - return it->value(); -} - -template -string_view const -basic_fields:: -operator[](string_view name) const -{ - auto const it = find(name); - if(it == end()) - return {}; - return it->value(); -} - -//------------------------------------------------------------------------------ -// -// Modifiers -// -//------------------------------------------------------------------------------ - -template -void -basic_fields:: -clear() -{ - delete_list(); - set_.clear(); - list_.clear(); -} - -template -inline -void -basic_fields:: -insert(field name, string_param const& value) -{ - BOOST_ASSERT(name != field::unknown); - insert(name, to_string(name), value); -} - -template -void -basic_fields:: -insert(string_view sname, string_param const& value) -{ - auto const name = - string_to_field(sname); - insert(name, sname, value); -} - -template -void -basic_fields:: -insert(field name, - string_view sname, string_param const& value) -{ - auto& e = new_element(name, sname, - static_cast(value)); - auto const before = - set_.upper_bound(sname, key_compare{}); - if(before == set_.begin()) - { - BOOST_ASSERT(count(sname) == 0); - set_.insert_before(before, e); - list_.push_back(e); - return; - } - auto const last = std::prev(before); - // VFALCO is it worth comparing `field name` first? - if(! iequals(sname, last->name_string())) - { - BOOST_ASSERT(count(sname) == 0); - set_.insert_before(before, e); - list_.push_back(e); - return; - } - // keep duplicate fields together in the list - set_.insert_before(before, e); - list_.insert(++list_.iterator_to(*last), e); -} - -template -void -basic_fields:: -set(field name, string_param const& value) -{ - BOOST_ASSERT(name != field::unknown); - set_element(new_element(name, to_string(name), - static_cast(value))); -} - -template -void -basic_fields:: -set(string_view sname, string_param const& value) -{ - set_element(new_element( - string_to_field(sname), sname, - static_cast(value))); -} - -template -auto -basic_fields:: -erase(const_iterator pos) -> - const_iterator -{ - auto next = pos; - auto& e = *next++; - set_.erase(e); - list_.erase(pos); - delete_element(const_cast(e)); - return next; -} - -template -std::size_t -basic_fields:: -erase(field name) -{ - BOOST_ASSERT(name != field::unknown); - return erase(to_string(name)); -} - -template -std::size_t -basic_fields:: -erase(string_view name) -{ - std::size_t n =0; - set_.erase_and_dispose(name, key_compare{}, - [&](element* e) - { - ++n; - list_.erase(list_.iterator_to(*e)); - delete_element(*e); - }); - return n; -} - -template -void -basic_fields:: -swap(basic_fields& other) -{ - swap(other, std::integral_constant{}); -} - -template -void -swap( - basic_fields& lhs, - basic_fields& rhs) -{ - lhs.swap(rhs); -} - -//------------------------------------------------------------------------------ -// -// Lookup -// -//------------------------------------------------------------------------------ - -template -inline -std::size_t -basic_fields:: -count(field name) const -{ - BOOST_ASSERT(name != field::unknown); - return count(to_string(name)); -} - -template -std::size_t -basic_fields:: -count(string_view name) const -{ - return set_.count(name, key_compare{}); -} - -template -inline -auto -basic_fields:: -find(field name) const -> - const_iterator -{ - BOOST_ASSERT(name != field::unknown); - return find(to_string(name)); -} - -template -auto -basic_fields:: -find(string_view name) const -> - const_iterator -{ - auto const it = set_.find( - name, key_compare{}); - if(it == set_.end()) - return list_.end(); - return list_.iterator_to(*it); -} - -template -inline -auto -basic_fields:: -equal_range(field name) const -> - std::pair -{ - BOOST_ASSERT(name != field::unknown); - return equal_range(to_string(name)); -} - -template -auto -basic_fields:: -equal_range(string_view name) const -> - std::pair -{ - auto result = - set_.equal_range(name, key_compare{}); - if(result.first == result.second) - return {list_.end(), list_.end()}; - return { - list_.iterator_to(*result.first), - ++list_.iterator_to(*(--result.second))}; -} - -//------------------------------------------------------------------------------ - -namespace detail { - -// Filter a token list -// -template -void -filter_token_list( - String& s, - string_view value, - Pred&& pred) -{ - token_list te{value}; - auto it = te.begin(); - auto last = te.end(); - if(it == last) - return; - while(pred(*it)) - if(++it == last) - return; - s.append(it->data(), it->size()); - while(++it != last) - { - if(! pred(*it)) - { - s.append(", "); - s.append(it->data(), it->size()); - } - } -} - -// Filter the last item in a token list -template -void -filter_token_list_last( - String& s, - string_view value, - Pred&& pred) -{ - token_list te{value}; - if(te.begin() != te.end()) - { - auto it = te.begin(); - auto next = std::next(it); - if(next == te.end()) - { - if(! pred(*it)) - s.append(it->data(), it->size()); - return; - } - s.append(it->data(), it->size()); - for(;;) - { - it = next; - next = std::next(it); - if(next == te.end()) - { - if(! pred(*it)) - { - s.append(", "); - s.append(it->data(), it->size()); - } - return; - } - s.append(", "); - s.append(it->data(), it->size()); - } - } -} - -template -void -keep_alive_impl( - String& s, string_view value, - unsigned version, bool keep_alive) -{ - if(version < 11) - { - if(keep_alive) - { - // remove close - filter_token_list(s, value, - [](string_view s) - { - return iequals(s, "close"); - }); - // add keep-alive - if(s.empty()) - s.append("keep-alive"); - else if(! token_list{value}.exists("keep-alive")) - s.append(", keep-alive"); - } - else - { - // remove close and keep-alive - filter_token_list(s, value, - [](string_view s) - { - return - iequals(s, "close") || - iequals(s, "keep-alive"); - }); - } - } - else - { - if(keep_alive) - { - // remove close and keep-alive - filter_token_list(s, value, - [](string_view s) - { - return - iequals(s, "close") || - iequals(s, "keep-alive"); - }); - } - else - { - // remove keep-alive - filter_token_list(s, value, - [](string_view s) - { - return iequals(s, "keep-alive"); - }); - // add close - if(s.empty()) - s.append("close"); - else if(! token_list{value}.exists("close")) - s.append(", close"); - } - } -} - -} // detail - -//------------------------------------------------------------------------------ - -// Fields - -template -inline -string_view -basic_fields:: -get_method_impl() const -{ - return method_; -} - -template -inline -string_view -basic_fields:: -get_target_impl() const -{ - if(target_or_reason_.empty()) - return target_or_reason_; - return { - target_or_reason_.data() + 1, - target_or_reason_.size() - 1}; -} - -template -inline -string_view -basic_fields:: -get_reason_impl() const -{ - return target_or_reason_; -} - -template -bool -basic_fields:: -get_chunked_impl() const -{ - auto const te = token_list{ - (*this)[field::transfer_encoding]}; - for(auto it = te.begin(); it != te.end();) - { - auto const next = std::next(it); - if(next == te.end()) - return iequals(*it, "chunked"); - it = next; - } - return false; -} - -template -bool -basic_fields:: -get_keep_alive_impl(unsigned version) const -{ - auto const it = find(field::connection); - if(version < 11) - { - if(it == end()) - return false; - return token_list{ - it->value()}.exists("keep-alive"); - } - if(it == end()) - return true; - return ! token_list{ - it->value()}.exists("close"); -} - -template -bool -basic_fields:: -has_content_length_impl() const -{ - return count(field::content_length) > 0; -} - -template -inline -void -basic_fields:: -set_method_impl(string_view s) -{ - realloc_string(method_, s); -} - -template -inline -void -basic_fields:: -set_target_impl(string_view s) -{ - realloc_target( - target_or_reason_, s); -} - -template -inline -void -basic_fields:: -set_reason_impl(string_view s) -{ - realloc_string( - target_or_reason_, s); -} - -template -void -basic_fields:: -set_chunked_impl(bool value) -{ - auto it = find(field::transfer_encoding); - if(value) - { - // append "chunked" - if(it == end()) - { - set(field::transfer_encoding, "chunked"); - return; - } - auto const te = token_list{it->value()}; - for(auto itt = te.begin();;) - { - auto const next = std::next(itt); - if(next == te.end()) - { - if(iequals(*itt, "chunked")) - return; // already set - break; - } - itt = next; - } - static_string buf; - if(it->value().size() <= buf.size() + 9) - { - buf.append(it->value().data(), it->value().size()); - buf.append(", chunked", 9); - set(field::transfer_encoding, buf); - } - else - { - #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR - // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 - std::string s; - #else - using A = - typename beast::detail::allocator_traits< - Allocator>::template rebind_alloc; - std::basic_string< - char, - std::char_traits, - A> s{A{this->get()}}; - #endif - s.reserve(it->value().size() + 9); - s.append(it->value().data(), it->value().size()); - s.append(", chunked", 9); - set(field::transfer_encoding, s); - } - return; - } - // filter "chunked" - if(it == end()) - return; - try - { - static_string buf; - detail::filter_token_list_last(buf, it->value(), - [](string_view s) - { - return iequals(s, "chunked"); - }); - if(! buf.empty()) - set(field::transfer_encoding, buf); - else - erase(field::transfer_encoding); - } - catch(std::length_error const&) - { - #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR - // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 - std::string s; - #else - using A = - typename beast::detail::allocator_traits< - Allocator>::template rebind_alloc; - std::basic_string< - char, - std::char_traits, - A> s{A{this->get()}}; - #endif - s.reserve(it->value().size()); - detail::filter_token_list_last(s, it->value(), - [](string_view s) - { - return iequals(s, "chunked"); - }); - if(! s.empty()) - set(field::transfer_encoding, s); - else - erase(field::transfer_encoding); - } -} - -template -void -basic_fields:: -set_content_length_impl( - boost::optional const& value) -{ - if(! value) - erase(field::content_length); - else - set(field::content_length, *value); -} - -template -void -basic_fields:: -set_keep_alive_impl( - unsigned version, bool keep_alive) -{ - // VFALCO What about Proxy-Connection ? - auto const value = (*this)[field::connection]; - try - { - static_string buf; - detail::keep_alive_impl( - buf, value, version, keep_alive); - if(buf.empty()) - erase(field::connection); - else - set(field::connection, buf); - } - catch(std::length_error const&) - { - #ifdef BOOST_BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR - // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 - std::string s; - #else - using A = - typename beast::detail::allocator_traits< - Allocator>::template rebind_alloc; - std::basic_string< - char, - std::char_traits, - A> s{A{this->get()}}; - #endif - s.reserve(value.size()); - detail::keep_alive_impl( - s, value, version, keep_alive); - if(s.empty()) - erase(field::connection); - else - set(field::connection, s); - } -} - -//------------------------------------------------------------------------------ - -template -auto -basic_fields:: -new_element(field name, - string_view sname, string_view value) -> - element& -{ - if(sname.size() + 2 > - (std::numeric_limits::max)()) - BOOST_THROW_EXCEPTION(std::length_error{ - "field name too large"}); - if(value.size() + 2 > - (std::numeric_limits::max)()) - BOOST_THROW_EXCEPTION(std::length_error{ - "field value too large"}); - value = detail::trim(value); - std::uint16_t const off = - static_cast(sname.size() + 2); - std::uint16_t const len = - static_cast(value.size()); - auto a = rebind_type{this->get()}; - auto const p = alloc_traits::allocate(a, - (sizeof(element) + off + len + 2 + sizeof(align_type) - 1) / - sizeof(align_type)); - return *(new(p) element(name, sname, value)); -} - -template -void -basic_fields:: -delete_element(element& e) -{ - auto a = rebind_type{this->get()}; - auto const n = - (sizeof(element) + e.off_ + e.len_ + 2 + sizeof(align_type) - 1) / - sizeof(align_type); - e.~element(); - alloc_traits::deallocate(a, &e, n); - //reinterpret_cast(&e), n); -} - -template -void -basic_fields:: -set_element(element& e) -{ - auto it = set_.lower_bound( - e.name_string(), key_compare{}); - if(it == set_.end() || ! iequals( - e.name_string(), it->name_string())) - { - set_.insert_before(it, e); - list_.push_back(e); - return; - } - for(;;) - { - auto next = it; - ++next; - set_.erase(it); - list_.erase(list_.iterator_to(*it)); - delete_element(*it); - it = next; - if(it == set_.end() || - ! iequals(e.name_string(), it->name_string())) - break; - } - set_.insert_before(it, e); - list_.push_back(e); -} - -template -void -basic_fields:: -realloc_string(string_view& dest, string_view s) -{ - if(dest.empty() && s.empty()) - return; - auto a = typename beast::detail::allocator_traits< - Allocator>::template rebind_alloc< - char>(this->get()); - char* p = nullptr; - if(! s.empty()) - { - p = a.allocate(s.size()); - s.copy(p, s.size()); - } - if(! dest.empty()) - a.deallocate(const_cast( - dest.data()), dest.size()); - if(p) - dest = {p, s.size()}; - else - dest = {}; -} - -template -void -basic_fields:: -realloc_target( - string_view& dest, string_view s) -{ - // The target string are stored with an - // extra space at the beginning to help - // the writer class. - if(dest.empty() && s.empty()) - return; - auto a = typename beast::detail::allocator_traits< - Allocator>::template rebind_alloc< - char>(this->get()); - char* p = nullptr; - if(! s.empty()) - { - p = a.allocate(1 + s.size()); - p[0] = ' '; - s.copy(p + 1, s.size()); - } - if(! dest.empty()) - a.deallocate(const_cast( - dest.data()), dest.size()); - if(p) - dest = {p, 1 + s.size()}; - else - dest = {}; -} - -template -template -void -basic_fields:: -copy_all(basic_fields const& other) -{ - for(auto const& e : other.list_) - insert(e.name(), e.name_string(), e.value()); - realloc_string(method_, other.method_); - realloc_string(target_or_reason_, - other.target_or_reason_); -} - -template -void -basic_fields:: -clear_all() -{ - clear(); - realloc_string(method_, {}); - realloc_string(target_or_reason_, {}); -} - -template -void -basic_fields:: -delete_list() -{ - for(auto it = list_.begin(); it != list_.end();) - delete_element(*it++); -} - -//------------------------------------------------------------------------------ - -template -inline -void -basic_fields:: -move_assign(basic_fields& other, std::true_type) -{ - clear_all(); - set_ = std::move(other.set_); - list_ = std::move(other.list_); - method_ = other.method_; - target_or_reason_ = other.target_or_reason_; - other.method_ = {}; - other.target_or_reason_ = {}; - this->get() = other.get(); -} - -template -inline -void -basic_fields:: -move_assign(basic_fields& other, std::false_type) -{ - clear_all(); - if(this->get() != other.get()) - { - copy_all(other); - other.clear_all(); - } - else - { - set_ = std::move(other.set_); - list_ = std::move(other.list_); - method_ = other.method_; - target_or_reason_ = other.target_or_reason_; - other.method_ = {}; - other.target_or_reason_ = {}; - } -} - -template -inline -void -basic_fields:: -copy_assign(basic_fields const& other, std::true_type) -{ - clear_all(); - this->get() = other.get(); - copy_all(other); -} - -template -inline -void -basic_fields:: -copy_assign(basic_fields const& other, std::false_type) -{ - clear_all(); - copy_all(other); -} - -template -inline -void -basic_fields:: -swap(basic_fields& other, std::true_type) -{ - using std::swap; - swap(this->get(), other.get()); - swap(set_, other.set_); - swap(list_, other.list_); - swap(method_, other.method_); - swap(target_or_reason_, other.target_or_reason_); -} - -template -inline -void -basic_fields:: -swap(basic_fields& other, std::false_type) -{ - BOOST_ASSERT(this->get() == other.get()); - using std::swap; - swap(set_, other.set_); - swap(list_, other.list_); - swap(method_, other.method_); - swap(target_or_reason_, other.target_or_reason_); -} - -} // http -} // beast -} // boost - -#endif diff --git a/boost/beast/http/impl/file_body_win32.hpp b/boost/beast/http/impl/file_body_win32.hpp new file mode 100644 index 0000000000..fc74c49772 --- /dev/null +++ b/boost/beast/http/impl/file_body_win32.hpp @@ -0,0 +1,573 @@ +// +// 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_FILE_BODY_WIN32_HPP +#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP + +#if BOOST_BEAST_USE_WIN32_FILE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { + +namespace detail { +template +class write_some_win32_op; +} // detail + +template<> +struct basic_file_body +{ + using file_type = file_win32; + + class writer; + class reader; + + //-------------------------------------------------------------------------- + + class value_type + { + friend class writer; + friend class reader; + friend struct basic_file_body; + + template + friend class detail::write_some_win32_op; + template< + class Protocol, class Executor, + bool isRequest, class Fields> + friend + std::size_t + write_some( + net::basic_stream_socket& sock, + serializer, Fields>& sr, + error_code& ec); + + file_win32 file_; + std::uint64_t size_ = 0; // cached file size + std::uint64_t first_; // starting offset of the range + std::uint64_t last_; // ending offset of the range + + public: + ~value_type() = default; + value_type() = default; + value_type(value_type&& other) = default; + value_type& operator=(value_type&& other) = default; + + bool + is_open() const + { + return file_.is_open(); + } + + std::uint64_t + size() const + { + return size_; + } + + void + close(); + + void + open(char const* path, file_mode mode, error_code& ec); + + void + reset(file_win32&& file, error_code& ec); + }; + + //-------------------------------------------------------------------------- + + class writer + { + template + friend class detail::write_some_win32_op; + template< + class Protocol, class Executor, + bool isRequest, class Fields> + friend + std::size_t + write_some( + net::basic_stream_socket& sock, + serializer, Fields>& sr, + error_code& ec); + + value_type& body_; // The body we are reading from + std::uint64_t pos_; // The current position in the file + char buf_[4096]; // Small buffer for reading + + public: + using const_buffers_type = + net::const_buffer; + + template + writer(header&, value_type& b) + : body_(b) + { + } + + void + init(error_code&) + { + BOOST_ASSERT(body_.file_.is_open()); + pos_ = body_.first_; + } + + boost::optional> + get(error_code& ec) + { + std::size_t const n = (std::min)(sizeof(buf_), + beast::detail::clamp(body_.last_ - pos_)); + if(n == 0) + { + ec = {}; + return boost::none; + } + auto const nread = body_.file_.read(buf_, n, ec); + if(ec) + return boost::none; + BOOST_ASSERT(nread != 0); + pos_ += nread; + ec = {}; + return {{ + {buf_, nread}, // buffer to return. + pos_ < body_.last_}}; // `true` if there are more buffers. + } + }; + + //-------------------------------------------------------------------------- + + class reader + { + value_type& body_; + + public: + template + explicit + reader(header&, value_type& b) + : body_(b) + { + } + + void + init(boost::optional< + std::uint64_t> const& content_length, + error_code& ec) + { + // VFALCO We could reserve space in the file + boost::ignore_unused(content_length); + BOOST_ASSERT(body_.file_.is_open()); + ec = {}; + } + + template + std::size_t + put(ConstBufferSequence const& buffers, + error_code& ec) + { + std::size_t nwritten = 0; + for(auto buffer : beast::buffers_range_ref(buffers)) + { + nwritten += body_.file_.write( + buffer.data(), buffer.size(), ec); + if(ec) + return nwritten; + } + ec = {}; + return nwritten; + } + + void + finish(error_code& ec) + { + ec = {}; + } + }; + + //-------------------------------------------------------------------------- + + static + std::uint64_t + size(value_type const& body) + { + return body.size(); + } +}; + +//------------------------------------------------------------------------------ + +inline +void +basic_file_body:: +value_type:: +close() +{ + error_code ignored; + file_.close(ignored); +} + +inline +void +basic_file_body:: +value_type:: +open(char const* path, file_mode mode, error_code& ec) +{ + file_.open(path, mode, ec); + if(ec) + return; + size_ = file_.size(ec); + if(ec) + { + close(); + return; + } + first_ = 0; + last_ = size_; +} + +inline +void +basic_file_body:: +value_type:: +reset(file_win32&& file, error_code& ec) +{ + if(file_.is_open()) + { + error_code ignored; + file_.close(ignored); + } + file_ = std::move(file); + if(file_.is_open()) + { + size_ = file_.size(ec); + if(ec) + { + close(); + return; + } + first_ = 0; + last_ = size_; + } +} + +//------------------------------------------------------------------------------ + +namespace detail { + +template +boost::winapi::DWORD_ +lowPart(Unsigned n) +{ + return static_cast< + boost::winapi::DWORD_>( + n & 0xffffffff); +} + +template +boost::winapi::DWORD_ +highPart(Unsigned n, std::true_type) +{ + return static_cast< + boost::winapi::DWORD_>( + (n>>32)&0xffffffff); +} + +template +boost::winapi::DWORD_ +highPart(Unsigned, std::false_type) +{ + return 0; +} + +template +boost::winapi::DWORD_ +highPart(Unsigned n) +{ + return highPart(n, std::integral_constant< + bool, (sizeof(Unsigned)>4)>{}); +} + +class null_lambda +{ +public: + template + void + operator()(error_code&, + ConstBufferSequence const&) const + { + BOOST_ASSERT(false); + } +}; + +//------------------------------------------------------------------------------ + +#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR + +template< + class Protocol, class Executor, + bool isRequest, class Fields, + class Handler> +class write_some_win32_op + : public beast::async_base +{ + net::basic_stream_socket< + Protocol, Executor>& sock_; + serializer, Fields>& sr_; + std::size_t bytes_transferred_ = 0; + bool header_ = false; + +public: + template + write_some_win32_op( + Handler_&& h, + net::basic_stream_socket< + Protocol, Executor>& s, + serializer,Fields>& sr) + : async_base< + Handler, Executor>( + std::forward(h), + s.get_executor()) + , sock_(s) + , sr_(sr) + { + (*this)(); + } + + void + operator()() + { + if(! sr_.is_header_done()) + { + header_ = true; + sr_.split(true); + return detail::async_write_some_impl( + sock_, sr_, std::move(*this)); + } + if(sr_.get().chunked()) + { + return detail::async_write_some_impl( + sock_, sr_, std::move(*this)); + } + auto& w = sr_.writer_impl(); + boost::winapi::DWORD_ const nNumberOfBytesToWrite = + static_cast( + (std::min)( + (std::min)(w.body_.last_ - w.pos_, sr_.limit()), + (std::numeric_limits::max)())); + net::windows::overlapped_ptr overlapped{ + sock_.get_executor(), std::move(*this)}; + // Note that we have moved *this, so we cannot access + // the handler since it is now moved-from. We can still + // access simple things like references and built-in types. + auto& ov = *overlapped.get(); + ov.Offset = lowPart(w.pos_); + ov.OffsetHigh = highPart(w.pos_); + auto const bSuccess = ::TransmitFile( + sock_.native_handle(), + sr_.get().body().file_.native_handle(), + nNumberOfBytesToWrite, + 0, + overlapped.get(), + nullptr, + 0); + auto const dwError = boost::winapi::GetLastError(); + if(! bSuccess && dwError != + boost::winapi::ERROR_IO_PENDING_) + { + // VFALCO This needs review, is 0 the right number? + // completed immediately (with error?) + overlapped.complete(error_code{static_cast(dwError), + system_category()}, 0); + return; + } + overlapped.release(); + } + + void + operator()( + error_code ec, + std::size_t bytes_transferred = 0) + { + bytes_transferred_ += bytes_transferred; + if(! ec) + { + if(header_) + { + header_ = false; + return (*this)(); + } + auto& w = sr_.writer_impl(); + w.pos_ += bytes_transferred; + BOOST_ASSERT(w.pos_ <= w.body_.last_); + if(w.pos_ >= w.body_.last_) + { + sr_.next(ec, null_lambda{}); + BOOST_ASSERT(! ec); + BOOST_ASSERT(sr_.is_done()); + } + } + this->complete_now(ec, bytes_transferred_); + } +}; + +struct run_write_some_win32_op +{ + template< + class Protocol, class Executor, + bool isRequest, class Fields, + class WriteHandler> + void + operator()( + WriteHandler&& h, + net::basic_stream_socket< + Protocol, Executor>* s, + serializer, Fields>* sr) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable::value, + "WriteHandler type requirements not met"); + + write_some_win32_op< + Protocol, Executor, + isRequest, Fields, + typename std::decay::type>( + std::forward(h), *s, *sr); + } +}; + +#endif + +} // detail + +//------------------------------------------------------------------------------ + +template< + class Protocol, class Executor, + bool isRequest, class Fields> +std::size_t +write_some( + net::basic_stream_socket< + Protocol, Executor>& sock, + serializer, Fields>& sr, + error_code& ec) +{ + if(! sr.is_header_done()) + { + sr.split(true); + auto const bytes_transferred = + detail::write_some_impl(sock, sr, ec); + if(ec) + return bytes_transferred; + return bytes_transferred; + } + if(sr.get().chunked()) + { + auto const bytes_transferred = + detail::write_some_impl(sock, sr, ec); + if(ec) + return bytes_transferred; + return bytes_transferred; + } + auto& w = sr.writer_impl(); + w.body_.file_.seek(w.pos_, ec); + if(ec) + return 0; + boost::winapi::DWORD_ const nNumberOfBytesToWrite = + static_cast( + (std::min)( + (std::min)(w.body_.last_ - w.pos_, sr.limit()), + (std::numeric_limits::max)())); + auto const bSuccess = ::TransmitFile( + sock.native_handle(), + w.body_.file_.native_handle(), + nNumberOfBytesToWrite, + 0, + nullptr, + nullptr, + 0); + if(! bSuccess) + { + ec.assign(static_cast( + boost::winapi::GetLastError()), + system_category()); + return 0; + } + w.pos_ += nNumberOfBytesToWrite; + BOOST_ASSERT(w.pos_ <= w.body_.last_); + if(w.pos_ < w.body_.last_) + { + ec = {}; + } + else + { + sr.next(ec, detail::null_lambda{}); + BOOST_ASSERT(! ec); + BOOST_ASSERT(sr.is_done()); + } + return nNumberOfBytesToWrite; +} + +#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR + +template< + class Protocol, class Executor, + bool isRequest, class Fields, + class WriteHandler> +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) +async_write_some( + net::basic_stream_socket< + Protocol, Executor>& sock, + serializer, Fields>& sr, + WriteHandler&& handler) +{ + return net::async_initiate< + WriteHandler, + void(error_code, std::size_t)>( + detail::run_write_some_win32_op{}, + handler, + &sock, + &sr); +} + +#endif + +} // http +} // beast +} // boost + +#endif + +#endif diff --git a/boost/beast/http/impl/file_body_win32.ipp b/boost/beast/http/impl/file_body_win32.ipp deleted file mode 100644 index 6357cf4983..0000000000 --- a/boost/beast/http/impl/file_body_win32.ipp +++ /dev/null @@ -1,599 +0,0 @@ -// -// Copyright (c) 2016-2017 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_FILE_BODY_WIN32_IPP -#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP - -#if BOOST_BEAST_USE_WIN32_FILE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast { -namespace http { - -namespace detail { -template -class write_some_win32_op; -} // detail - -template<> -struct basic_file_body -{ - using file_type = file_win32; - - class writer; - class reader; - - //-------------------------------------------------------------------------- - - class value_type - { - friend class writer; - friend class reader; - friend struct basic_file_body; - - template - friend class detail::write_some_win32_op; - template< - class Protocol, bool isRequest, class Fields> - friend - std::size_t - write_some( - boost::asio::basic_stream_socket& sock, - serializer, Fields>& sr, - error_code& ec); - - file_win32 file_; - std::uint64_t size_ = 0; // cached file size - std::uint64_t first_; // starting offset of the range - std::uint64_t last_; // ending offset of the range - - public: - ~value_type() = default; - value_type() = default; - value_type(value_type&& other) = default; - value_type& operator=(value_type&& other) = default; - - bool - is_open() const - { - return file_.is_open(); - } - - std::uint64_t - size() const - { - return size_; - } - - void - close(); - - void - open(char const* path, file_mode mode, error_code& ec); - - void - reset(file_win32&& file, error_code& ec); - }; - - //-------------------------------------------------------------------------- - - class writer - { - template - friend class detail::write_some_win32_op; - template< - class Protocol, bool isRequest, class Fields> - friend - std::size_t - write_some( - boost::asio::basic_stream_socket& sock, - serializer, Fields>& sr, - error_code& ec); - - value_type& body_; // The body we are reading from - std::uint64_t pos_; // The current position in the file - char buf_[4096]; // Small buffer for reading - - public: - using const_buffers_type = - boost::asio::const_buffer; - - template - writer(header&, value_type& b) - : body_(b) - { - } - - void - init(error_code&) - { - BOOST_ASSERT(body_.file_.is_open()); - pos_ = body_.first_; - } - - boost::optional> - get(error_code& ec) - { - std::size_t const n = (std::min)(sizeof(buf_), - beast::detail::clamp(body_.last_ - pos_)); - if(n == 0) - { - ec.assign(0, ec.category()); - return boost::none; - } - auto const nread = body_.file_.read(buf_, n, ec); - if(ec) - return boost::none; - BOOST_ASSERT(nread != 0); - pos_ += nread; - ec.assign(0, ec.category()); - return {{ - {buf_, nread}, // buffer to return. - pos_ < body_.last_}}; // `true` if there are more buffers. - } - }; - - //-------------------------------------------------------------------------- - - class reader - { - value_type& body_; - - public: - template - explicit - reader(header&, value_type& b) - : body_(b) - { - } - - void - init(boost::optional< - std::uint64_t> const& content_length, - error_code& ec) - { - // VFALCO We could reserve space in the file - boost::ignore_unused(content_length); - BOOST_ASSERT(body_.file_.is_open()); - ec.assign(0, ec.category()); - } - - template - std::size_t - put(ConstBufferSequence const& buffers, - error_code& ec) - { - std::size_t nwritten = 0; - for(auto buffer : beast::detail::buffers_range(buffers)) - { - nwritten += body_.file_.write( - buffer.data(), buffer.size(), ec); - if(ec) - return nwritten; - } - ec.assign(0, ec.category()); - return nwritten; - } - - void - finish(error_code& ec) - { - ec.assign(0, ec.category()); - } - }; - - //-------------------------------------------------------------------------- - - static - std::uint64_t - size(value_type const& body) - { - return body.size(); - } -}; - -//------------------------------------------------------------------------------ - -inline -void -basic_file_body:: -value_type:: -close() -{ - error_code ignored; - file_.close(ignored); -} - -inline -void -basic_file_body:: -value_type:: -open(char const* path, file_mode mode, error_code& ec) -{ - file_.open(path, mode, ec); - if(ec) - return; - size_ = file_.size(ec); - if(ec) - { - close(); - return; - } - first_ = 0; - last_ = size_; -} - -inline -void -basic_file_body:: -value_type:: -reset(file_win32&& file, error_code& ec) -{ - if(file_.is_open()) - { - error_code ignored; - file_.close(ignored); - } - file_ = std::move(file); - if(file_.is_open()) - { - size_ = file_.size(ec); - if(ec) - { - close(); - return; - } - first_ = 0; - last_ = size_; - } -} - -//------------------------------------------------------------------------------ - -namespace detail { - -template -inline -boost::winapi::DWORD_ -lowPart(Unsigned n) -{ - return static_cast< - boost::winapi::DWORD_>( - n & 0xffffffff); -} - -template -inline -boost::winapi::DWORD_ -highPart(Unsigned n, std::true_type) -{ - return static_cast< - boost::winapi::DWORD_>( - (n>>32)&0xffffffff); -} - -template -inline -boost::winapi::DWORD_ -highPart(Unsigned, std::false_type) -{ - return 0; -} - -template -inline -boost::winapi::DWORD_ -highPart(Unsigned n) -{ - return highPart(n, std::integral_constant< - bool, (sizeof(Unsigned)>4)>{}); -} - -class null_lambda -{ -public: - template - void - operator()(error_code&, - ConstBufferSequence const&) const - { - BOOST_ASSERT(false); - } -}; - -//------------------------------------------------------------------------------ - -#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR - -template< - class Protocol, class Handler, - bool isRequest, class Fields> -class write_some_win32_op -{ - boost::asio::basic_stream_socket& sock_; - boost::asio::executor_work_guard&>().get_executor())> wg_; - serializer, Fields>& sr_; - std::size_t bytes_transferred_ = 0; - Handler h_; - bool header_ = false; - -public: - write_some_win32_op(write_some_win32_op&&) = default; - write_some_win32_op(write_some_win32_op const&) = delete; - - template - write_some_win32_op( - DeducedHandler&& h, - boost::asio::basic_stream_socket& s, - serializer,Fields>& sr) - : sock_(s) - , wg_(sock_.get_executor()) - , sr_(sr) - , h_(std::forward(h)) - { - } - - using allocator_type = - boost::asio::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (boost::asio::get_associated_allocator)(h_); - } - - using executor_type = - boost::asio::associated_executor_t&>().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (boost::asio::get_associated_executor)( - h_, sock_.get_executor()); - } - - void - operator()(); - - void - operator()( - error_code ec, - std::size_t bytes_transferred = 0); - - friend - bool asio_handler_is_continuation(write_some_win32_op* op) - { - using boost::asio::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - template - friend - void asio_handler_invoke(Function&& f, write_some_win32_op* op) - { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } -}; - -template< - class Protocol, class Handler, - bool isRequest, class Fields> -void -write_some_win32_op< - Protocol, Handler, isRequest, Fields>:: -operator()() -{ - if(! sr_.is_header_done()) - { - header_ = true; - sr_.split(true); - return detail::async_write_some_impl( - sock_, sr_, std::move(*this)); - } - if(sr_.get().chunked()) - { - return detail::async_write_some_impl( - sock_, sr_, std::move(*this)); - } - auto& w = sr_.writer_impl(); - boost::winapi::DWORD_ const nNumberOfBytesToWrite = - static_cast( - (std::min)( - (std::min)(w.body_.last_ - w.pos_, sr_.limit()), - (std::numeric_limits::max)())); - boost::asio::windows::overlapped_ptr overlapped{ - sock_.get_executor().context(), std::move(*this)}; - // Note that we have moved *this, so we cannot access - // the handler since it is now moved-from. We can still - // access simple things like references and built-in types. - auto& ov = *overlapped.get(); - ov.Offset = lowPart(w.pos_); - ov.OffsetHigh = highPart(w.pos_); - auto const bSuccess = ::TransmitFile( - sock_.native_handle(), - sr_.get().body().file_.native_handle(), - nNumberOfBytesToWrite, - 0, - overlapped.get(), - nullptr, - 0); - auto const dwError = boost::winapi::GetLastError(); - if(! bSuccess && dwError != - boost::winapi::ERROR_IO_PENDING_) - { - // VFALCO This needs review, is 0 the right number? - // completed immediately (with error?) - overlapped.complete(error_code{static_cast(dwError), - system_category()}, 0); - return; - } - overlapped.release(); -} - -template< - class Protocol, class Handler, - bool isRequest, class Fields> -void -write_some_win32_op< - Protocol, Handler, isRequest, Fields>:: -operator()( - error_code ec, std::size_t bytes_transferred) -{ - bytes_transferred_ += bytes_transferred; - if(! ec) - { - if(header_) - { - header_ = false; - return (*this)(); - } - auto& w = sr_.writer_impl(); - w.pos_ += bytes_transferred; - BOOST_ASSERT(w.pos_ <= w.body_.last_); - if(w.pos_ >= w.body_.last_) - { - sr_.next(ec, null_lambda{}); - BOOST_ASSERT(! ec); - BOOST_ASSERT(sr_.is_done()); - } - } - h_(ec, bytes_transferred_); -} - -#endif - -} // detail - -//------------------------------------------------------------------------------ - -template -std::size_t -write_some( - boost::asio::basic_stream_socket& sock, - serializer, Fields>& sr, - error_code& ec) -{ - if(! sr.is_header_done()) - { - sr.split(true); - auto const bytes_transferred = - detail::write_some_impl(sock, sr, ec); - if(ec) - return bytes_transferred; - return bytes_transferred; - } - if(sr.get().chunked()) - { - auto const bytes_transferred = - detail::write_some_impl(sock, sr, ec); - if(ec) - return bytes_transferred; - return bytes_transferred; - } - auto& w = sr.writer_impl(); - w.body_.file_.seek(w.pos_, ec); - if(ec) - return 0; - boost::winapi::DWORD_ const nNumberOfBytesToWrite = - static_cast( - (std::min)( - (std::min)(w.body_.last_ - w.pos_, sr.limit()), - (std::numeric_limits::max)())); - auto const bSuccess = ::TransmitFile( - sock.native_handle(), - w.body_.file_.native_handle(), - nNumberOfBytesToWrite, - 0, - nullptr, - nullptr, - 0); - if(! bSuccess) - { - ec.assign(static_cast( - boost::winapi::GetLastError()), - system_category()); - return 0; - } - w.pos_ += nNumberOfBytesToWrite; - BOOST_ASSERT(w.pos_ <= w.body_.last_); - if(w.pos_ < w.body_.last_) - { - ec.assign(0, ec.category()); - } - else - { - sr.next(ec, detail::null_lambda{}); - BOOST_ASSERT(! ec); - BOOST_ASSERT(sr.is_done()); - } - return nNumberOfBytesToWrite; -} - -#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR - -template< - class Protocol, - bool isRequest, class Fields, - class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) -async_write_some( - boost::asio::basic_stream_socket& sock, - serializer, Fields>& sr, - WriteHandler&& handler) -{ - BOOST_BEAST_HANDLER_INIT( - WriteHandler, void(error_code, std::size_t)); - detail::write_some_win32_op< - Protocol, - BOOST_ASIO_HANDLER_TYPE(WriteHandler, - void(error_code, std::size_t)), - isRequest, Fields>{ - std::move(init.completion_handler), sock, sr}(); - return init.result.get(); -} - -#endif - -} // http -} // beast -} // boost - -#endif - -#endif diff --git a/boost/beast/http/impl/message.hpp b/boost/beast/http/impl/message.hpp new file mode 100644 index 0000000000..805d01f09a --- /dev/null +++ b/boost/beast/http/impl/message.hpp @@ -0,0 +1,429 @@ +// +// 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_MESSAGE_HPP +#define BOOST_BEAST_HTTP_IMPL_MESSAGE_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { + +template +template +header:: +header(Arg1&& arg1, ArgN&&... argn) + : Fields(std::forward(arg1), + std::forward(argn)...) +{ +} + +template +verb +header:: +method() const +{ + return method_; +} + +template +void +header:: +method(verb v) +{ + if(v == verb::unknown) + BOOST_THROW_EXCEPTION( + std::invalid_argument{"unknown method"}); + method_ = v; + this->set_method_impl({}); +} + +template +string_view +header:: +method_string() const +{ + if(method_ != verb::unknown) + return to_string(method_); + return this->get_method_impl(); +} + +template +void +header:: +method_string(string_view s) +{ + method_ = string_to_verb(s); + if(method_ != verb::unknown) + this->set_method_impl({}); + else + this->set_method_impl(s); +} + +template +string_view +header:: +target() const +{ + return this->get_target_impl(); +} + +template +void +header:: +target(string_view s) +{ + this->set_target_impl(s); +} + +template +void +swap( + header& h1, + header& h2) +{ + using std::swap; + swap( + static_cast(h1), + static_cast(h2)); + swap(h1.version_, h2.version_); + swap(h1.method_, h2.method_); +} + +//------------------------------------------------------------------------------ + +template +template +header:: +header(Arg1&& arg1, ArgN&&... argn) + : Fields(std::forward(arg1), + std::forward(argn)...) +{ +} + +template +status +header:: +result() const +{ + return int_to_status( + static_cast(result_)); +} + +template +void +header:: +result(status v) +{ + result_ = v; +} + +template +void +header:: +result(unsigned v) +{ + if(v > 999) + BOOST_THROW_EXCEPTION( + std::invalid_argument{ + "invalid status-code"}); + result_ = static_cast(v); +} + +template +unsigned +header:: +result_int() const +{ + return static_cast(result_); +} + +template +string_view +header:: +reason() const +{ + auto const s = this->get_reason_impl(); + if(! s.empty()) + return s; + return obsolete_reason(result_); +} + +template +void +header:: +reason(string_view s) +{ + this->set_reason_impl(s); +} + +template +void +swap( + header& h1, + header& h2) +{ + using std::swap; + swap( + static_cast(h1), + static_cast(h2)); + swap(h1.version_, h2.version_); + swap(h1.result_, h2.result_); +} + +//------------------------------------------------------------------------------ + +template +template +message:: +message(header_type&& h, BodyArgs&&... body_args) + : header_type(std::move(h)) + , boost::empty_value< + typename Body::value_type>(boost::empty_init_t(), + std::forward(body_args)...) +{ +} + +template +template +message:: +message(header_type const& h, BodyArgs&&... body_args) + : header_type(h) + , boost::empty_value< + typename Body::value_type>(boost::empty_init_t(), + std::forward(body_args)...) +{ +} + +template +template +message:: +message(verb method, string_view target, Version version) + : header_type(method, target, version) +{ +} + +template +template +message:: +message(verb method, string_view target, + Version version, BodyArg&& body_arg) + : header_type(method, target, version) + , boost::empty_value< + typename Body::value_type>(boost::empty_init_t(), + std::forward(body_arg)) +{ +} + +template +template +message:: +message( + verb method, string_view target, Version version, + BodyArg&& body_arg, + FieldsArg&& fields_arg) + : header_type(method, target, version, + std::forward(fields_arg)) + , boost::empty_value< + typename Body::value_type>(boost::empty_init_t(), + std::forward(body_arg)) +{ +} + +template +template +message:: +message(status result, Version version) + : header_type(result, version) +{ +} + +template +template +message:: +message(status result, Version version, + BodyArg&& body_arg) + : header_type(result, version) + , boost::empty_value< + typename Body::value_type>(boost::empty_init_t(), + std::forward(body_arg)) +{ +} + +template +template +message:: +message(status result, Version version, + BodyArg&& body_arg, FieldsArg&& fields_arg) + : header_type(result, version, + std::forward(fields_arg)) + , boost::empty_value< + typename Body::value_type>(boost::empty_init_t(), + std::forward(body_arg)) +{ +} + +template +message:: +message(std::piecewise_construct_t) +{ +} + +template +template +message:: +message(std::piecewise_construct_t, + std::tuple body_args) + : message(std::piecewise_construct, + body_args, + mp11::make_index_sequence< + sizeof...(BodyArgs)>{}) +{ +} + +template +template +message:: +message(std::piecewise_construct_t, + std::tuple body_args, + std::tuple fields_args) + : message(std::piecewise_construct, + body_args, + fields_args, + mp11::make_index_sequence< + sizeof...(BodyArgs)>{}, + mp11::make_index_sequence< + sizeof...(FieldsArgs)>{}) +{ +} + +template +void +message:: +chunked(bool value) +{ + this->set_chunked_impl(value); + this->set_content_length_impl(boost::none); +} + +template +void +message:: +content_length( + boost::optional const& value) +{ + this->set_content_length_impl(value); + this->set_chunked_impl(false); +} + +template +boost::optional +message:: +payload_size() const +{ + return payload_size(detail::is_body_sized{}); +} + +template +bool +message:: +need_eof(std::false_type) const +{ + // VFALCO Do we need a way to let the caller say "the body is intentionally skipped"? + if( this->result() == status::no_content || + this->result() == status::not_modified || + to_status_class(this->result()) == + status_class::informational || + has_content_length() || + chunked()) + return ! keep_alive(); + return true; +} + +template +void +message:: +prepare_payload(std::true_type) +{ + auto const n = payload_size(); + if(this->method() == verb::trace && (! n || *n > 0)) + BOOST_THROW_EXCEPTION(std::invalid_argument{ + "invalid request body"}); + if(n) + { + if(*n > 0 || + this->method() == verb::options || + this->method() == verb::put || + this->method() == verb::post) + { + this->content_length(n); + } + else + { + this->chunked(false); + } + } + else if(this->version() == 11) + { + this->chunked(true); + } + else + { + this->chunked(false); + } +} + +template +void +message:: +prepare_payload(std::false_type) +{ + auto const n = payload_size(); + if( (! n || *n > 0) && ( + (status_class(this->result()) == status_class::informational || + this->result() == status::no_content || + this->result() == status::not_modified))) + { + // The response body MUST be empty for this case + BOOST_THROW_EXCEPTION(std::invalid_argument{ + "invalid response body"}); + } + if(n) + this->content_length(n); + else if(this->version() == 11) + this->chunked(true); + else + this->chunked(false); +} + +//------------------------------------------------------------------------------ + +template +void +swap( + message& m1, + message& m2) +{ + using std::swap; + swap( + static_cast&>(m1), + static_cast&>(m2)); + swap(m1.body(), m2.body()); +} + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/message.ipp b/boost/beast/http/impl/message.ipp deleted file mode 100644 index a2a10402f9..0000000000 --- a/boost/beast/http/impl/message.ipp +++ /dev/null @@ -1,437 +0,0 @@ -// -// Copyright (c) 2016-2017 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_MESSAGE_IPP -#define BOOST_BEAST_HTTP_IMPL_MESSAGE_IPP - -#include -#include -#include -#include -#include - -namespace boost { -namespace beast { -namespace http { - -template -template -header:: -header(Arg1&& arg1, ArgN&&... argn) - : Fields(std::forward(arg1), - std::forward(argn)...) -{ -} - -template -inline -verb -header:: -method() const -{ - return method_; -} - -template -void -header:: -method(verb v) -{ - if(v == verb::unknown) - BOOST_THROW_EXCEPTION( - std::invalid_argument{"unknown method"}); - method_ = v; - this->set_method_impl({}); -} - -template -string_view -header:: -method_string() const -{ - if(method_ != verb::unknown) - return to_string(method_); - return this->get_method_impl(); -} - -template -void -header:: -method_string(string_view s) -{ - method_ = string_to_verb(s); - if(method_ != verb::unknown) - this->set_method_impl({}); - else - this->set_method_impl(s); -} - -template -inline -string_view -header:: -target() const -{ - return this->get_target_impl(); -} - -template -inline -void -header:: -target(string_view s) -{ - this->set_target_impl(s); -} - -template -void -swap( - header& h1, - header& h2) -{ - using std::swap; - swap( - static_cast(h1), - static_cast(h2)); - swap(h1.version_, h2.version_); - swap(h1.method_, h2.method_); -} - -//------------------------------------------------------------------------------ - -template -template -header:: -header(Arg1&& arg1, ArgN&&... argn) - : Fields(std::forward(arg1), - std::forward(argn)...) -{ -} - -template -inline -status -header:: -result() const -{ - return int_to_status( - static_cast(result_)); -} - -template -inline -void -header:: -result(status v) -{ - result_ = v; -} - -template -inline -void -header:: -result(unsigned v) -{ - if(v > 999) - BOOST_THROW_EXCEPTION( - std::invalid_argument{ - "invalid status-code"}); - result_ = static_cast(v); -} - -template -inline -unsigned -header:: -result_int() const -{ - return static_cast(result_); -} - -template -string_view -header:: -reason() const -{ - auto const s = this->get_reason_impl(); - if(! s.empty()) - return s; - return obsolete_reason(result_); -} - -template -inline -void -header:: -reason(string_view s) -{ - this->set_reason_impl(s); -} - -template -void -swap( - header& h1, - header& h2) -{ - using std::swap; - swap( - static_cast(h1), - static_cast(h2)); - swap(h1.version_, h2.version_); - swap(h1.result_, h2.result_); -} - -//------------------------------------------------------------------------------ - -template -template -message:: -message(header_type&& h, BodyArgs&&... body_args) - : header_type(std::move(h)) - , boost::empty_value< - typename Body::value_type>(boost::empty_init_t(), - std::forward(body_args)...) -{ -} - -template -template -message:: -message(header_type const& h, BodyArgs&&... body_args) - : header_type(h) - , boost::empty_value< - typename Body::value_type>(boost::empty_init_t(), - std::forward(body_args)...) -{ -} - -template -template -message:: -message(verb method, string_view target, Version version) - : header_type(method, target, version) -{ -} - -template -template -message:: -message(verb method, string_view target, - Version version, BodyArg&& body_arg) - : header_type(method, target, version) - , boost::empty_value< - typename Body::value_type>(boost::empty_init_t(), - std::forward(body_arg)) -{ -} - -template -template -message:: -message( - verb method, string_view target, Version version, - BodyArg&& body_arg, - FieldsArg&& fields_arg) - : header_type(method, target, version, - std::forward(fields_arg)) - , boost::empty_value< - typename Body::value_type>(boost::empty_init_t(), - std::forward(body_arg)) -{ -} - -template -template -message:: -message(status result, Version version) - : header_type(result, version) -{ -} - -template -template -message:: -message(status result, Version version, - BodyArg&& body_arg) - : header_type(result, version) - , boost::empty_value< - typename Body::value_type>(boost::empty_init_t(), - std::forward(body_arg)) -{ -} - -template -template -message:: -message(status result, Version version, - BodyArg&& body_arg, FieldsArg&& fields_arg) - : header_type(result, version, - std::forward(fields_arg)) - , boost::empty_value< - typename Body::value_type>(boost::empty_init_t(), - std::forward(body_arg)) -{ -} - -template -message:: -message(std::piecewise_construct_t) -{ -} - -template -template -message:: -message(std::piecewise_construct_t, - std::tuple body_args) - : message(std::piecewise_construct, - body_args, - beast::detail::make_index_sequence< - sizeof...(BodyArgs)>{}) -{ -} - -template -template -message:: -message(std::piecewise_construct_t, - std::tuple body_args, - std::tuple fields_args) - : message(std::piecewise_construct, - body_args, - fields_args, - beast::detail::make_index_sequence< - sizeof...(BodyArgs)>{}, - beast::detail::make_index_sequence< - sizeof...(FieldsArgs)>{}) -{ -} - -template -void -message:: -chunked(bool value) -{ - this->set_chunked_impl(value); - this->set_content_length_impl(boost::none); -} - -template -void -message:: -content_length( - boost::optional const& value) -{ - this->set_content_length_impl(value); - this->set_chunked_impl(false); -} - -template -boost::optional -message:: -payload_size() const -{ - return payload_size(detail::is_body_sized{}); -} - -template -bool -message:: -need_eof(std::false_type) const -{ - // VFALCO Do we need a way to let the caller say "the body is intentionally skipped"? - if( this->result() == status::no_content || - this->result() == status::not_modified || - to_status_class(this->result()) == - status_class::informational || - has_content_length() || - chunked()) - return ! keep_alive(); - return true; -} - -template -void -message:: -prepare_payload(std::true_type) -{ - auto const n = payload_size(); - if(this->method() == verb::trace && (! n || *n > 0)) - BOOST_THROW_EXCEPTION(std::invalid_argument{ - "invalid request body"}); - if(n) - { - if(*n > 0 || - this->method() == verb::options || - this->method() == verb::put || - this->method() == verb::post) - { - this->content_length(n); - } - else - { - this->chunked(false); - } - } - else if(this->version() == 11) - { - this->chunked(true); - } - else - { - this->chunked(false); - } -} - -template -void -message:: -prepare_payload(std::false_type) -{ - auto const n = payload_size(); - if( (! n || *n > 0) && ( - (status_class(this->result()) == status_class::informational || - this->result() == status::no_content || - this->result() == status::not_modified))) - { - // The response body MUST be empty for this case - BOOST_THROW_EXCEPTION(std::invalid_argument{ - "invalid response body"}); - } - if(n) - this->content_length(n); - else if(this->version() == 11) - this->chunked(true); - else - this->chunked(false); -} - -//------------------------------------------------------------------------------ - -template -void -swap( - message& m1, - message& m2) -{ - using std::swap; - swap( - static_cast&>(m1), - static_cast&>(m2)); - swap(m1.body(), m2.body()); -} - -} // http -} // beast -} // boost - -#endif diff --git a/boost/beast/http/impl/parser.hpp b/boost/beast/http/impl/parser.hpp new file mode 100644 index 0000000000..066ac5fea1 --- /dev/null +++ b/boost/beast/http/impl/parser.hpp @@ -0,0 +1,58 @@ +// +// 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_PARSER_HPP +#define BOOST_BEAST_HTTP_IMPL_PARSER_HPP + +#include +#include + +namespace boost { +namespace beast { +namespace http { + +template +parser:: +parser() + : rd_(m_.base(), m_.body()) +{ +} + +template +template +parser:: +parser(Arg1&& arg1, ArgN&&... argn) + : m_( + std::forward(arg1), + std::forward(argn)...) + , rd_(m_.base(), m_.body()) +{ + m_.clear(); +} + +template +template +parser:: +parser( + parser&& other, + Args&&... args) + : basic_parser(std::move(other)) + , m_(other.release(), std::forward(args)...) + , rd_(m_.base(), m_.body()) +{ + if(other.rd_inited_) + BOOST_THROW_EXCEPTION(std::invalid_argument{ + "moved-from parser has a body"}); +} + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/parser.ipp b/boost/beast/http/impl/parser.ipp deleted file mode 100644 index 45bef1c6ac..0000000000 --- a/boost/beast/http/impl/parser.ipp +++ /dev/null @@ -1,58 +0,0 @@ -// -// Copyright (c) 2016-2017 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_PARSER_IPP -#define BOOST_BEAST_HTTP_IMPL_PARSER_IPP - -#include -#include - -namespace boost { -namespace beast { -namespace http { - -template -parser:: -parser() - : rd_(m_.base(), m_.body()) -{ -} - -template -template -parser:: -parser(Arg1&& arg1, ArgN&&... argn) - : m_( - std::forward(arg1), - std::forward(argn)...) - , rd_(m_.base(), m_.body()) -{ - m_.clear(); -} - -template -template -parser:: -parser( - parser&& other, - Args&&... args) - : base_type(std::move(other)) - , m_(other.release(), std::forward(args)...) - , rd_(m_.base(), m_.body()) -{ - if(other.rd_inited_) - BOOST_THROW_EXCEPTION(std::invalid_argument{ - "moved-from parser has a body"}); -} - -} // http -} // beast -} // boost - -#endif diff --git a/boost/beast/http/impl/read.hpp b/boost/beast/http/impl/read.hpp new file mode 100644 index 0000000000..faea67eb25 --- /dev/null +++ b/boost/beast/http/impl/read.hpp @@ -0,0 +1,559 @@ +// +// 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_READ_HPP +#define BOOST_BEAST_HTTP_IMPL_READ_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { + +namespace detail { + +// The default maximum number of bytes to transfer in a single operation. +std::size_t constexpr default_max_transfer_size = 65536; + +template< + class DynamicBuffer, + bool isRequest, + class Condition> +std::size_t +parse_until( + DynamicBuffer& buffer, + basic_parser& parser, + error_code& ec, + Condition cond) +{ + if(ec == net::error::eof) + { + if(parser.got_some()) + { + // Caller sees EOF on next read + ec = {}; + parser.put_eof(ec); + BOOST_ASSERT(ec || parser.is_done()); + } + else + { + ec = error::end_of_stream; + } + return 0; + } + if(ec) + { + // Upgrade the error if we have a partial message. + // This causes SSL short reads (and every other error) + // to be converted into something else, allowing the + // caller to distinguish an SSL short read which + // represents a safe connection closure, versus + // a closure with data loss. + if(parser.got_some() && ! parser.is_done()) + ec = error::partial_message; + return 0; + } + if(parser.is_done()) + return 0; + if(buffer.size() > 0) + { + auto const bytes_used = + parser.put(buffer.data(), ec); + // total = total + bytes_used; // VFALCO Can't do this in a condition + buffer.consume(bytes_used); + if(ec == http::error::need_more) + { + if(buffer.size() >= buffer.max_size()) + { + ec = http::error::buffer_overflow; + return 0; + } + ec = {}; + } + else if(ec || cond()) + { + return 0; + } + } + return default_max_transfer_size; +} + +// predicate is true on any forward parser progress +template +struct read_some_condition +{ + basic_parser& parser; + + template + std::size_t + operator()(error_code& ec, std::size_t, + DynamicBuffer& buffer) + { + return detail::parse_until( + buffer, parser, ec, + [] + { + return true; + }); + } +}; + +// predicate is true when parser header is complete +template +struct read_header_condition +{ + basic_parser& parser; + + template + std::size_t + operator()(error_code& ec, std::size_t, + DynamicBuffer& buffer) + { + return detail::parse_until( + buffer, parser, ec, + [this] + { + return parser.is_header_done(); + }); + } +}; + +// predicate is true when parser message is complete +template +struct read_all_condition +{ + basic_parser& parser; + + template + std::size_t + operator()(error_code& ec, std::size_t, + DynamicBuffer& buffer) + { + return detail::parse_until( + buffer, parser, ec, + [this] + { + return parser.is_done(); + }); + } +}; + +//------------------------------------------------------------------------------ + +template< + class Stream, class DynamicBuffer, + bool isRequest, class Body, class Allocator, + class Handler> +class read_msg_op + : public beast::stable_async_base< + Handler, beast::executor_type> + , public net::coroutine +{ + using parser_type = + parser; + + using message_type = + typename parser_type::value_type; + + struct data + { + Stream& s; + message_type& m; + parser_type p; + + data( + Stream& s_, + message_type& m_) + : s(s_) + , m(m_) + , p(std::move(m)) + { + } + }; + + data& d_; + +public: + template + read_msg_op( + Handler_&& h, + Stream& s, + DynamicBuffer& b, + message_type& m) + : stable_async_base< + Handler, beast::executor_type>( + std::forward(h), s.get_executor()) + , d_(beast::allocate_stable( + *this, s, m)) + { + http::async_read(d_.s, b, d_.p, std::move(*this)); + } + + void + operator()( + error_code ec, + std::size_t bytes_transferred) + { + if(! ec) + d_.m = d_.p.release(); + this->complete_now(ec, bytes_transferred); + } +}; + +struct run_read_msg_op +{ + template< + class ReadHandler, + class AsyncReadStream, + class DynamicBuffer, + bool isRequest, class Body, class Allocator> + void + operator()( + ReadHandler&& h, + AsyncReadStream* s, + DynamicBuffer* b, + message>* m) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable::value, + "ReadHandler type requirements not met"); + + read_msg_op< + AsyncReadStream, + DynamicBuffer, + isRequest, Body, Allocator, + typename std::decay::type>( + std::forward(h), *s, *b, *m); + } +}; + +} // detail + +//------------------------------------------------------------------------------ + +template< + class SyncReadStream, + class DynamicBuffer, + bool isRequest> +std::size_t +read_some( + SyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser) +{ + static_assert( + is_sync_read_stream::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + error_code ec; + auto const bytes_transferred = + http::read_some(stream, buffer, parser, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncReadStream, + class DynamicBuffer, + bool isRequest> +std::size_t +read_some( + SyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser, + error_code& ec) +{ + static_assert( + is_sync_read_stream::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + return beast::detail::read(stream, buffer, + detail::read_some_condition< + isRequest>{parser}, ec); +} + +template< + class AsyncReadStream, + class DynamicBuffer, + bool isRequest, + class ReadHandler> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +async_read_some( + AsyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser, + ReadHandler&& handler) +{ + return beast::detail::async_read( + stream, + buffer, + detail::read_some_condition< + isRequest>{parser}, + std::forward(handler)); +} + +//------------------------------------------------------------------------------ + +template< + class SyncReadStream, + class DynamicBuffer, + bool isRequest> +std::size_t +read_header( + SyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser) +{ + static_assert( + is_sync_read_stream::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + error_code ec; + auto const bytes_transferred = + http::read_header(stream, buffer, parser, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncReadStream, + class DynamicBuffer, + bool isRequest> +std::size_t +read_header( + SyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser, + error_code& ec) +{ + static_assert( + is_sync_read_stream::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + parser.eager(false); + return beast::detail::read(stream, buffer, + detail::read_header_condition< + isRequest>{parser}, ec); +} + +template< + class AsyncReadStream, + class DynamicBuffer, + bool isRequest, + class ReadHandler> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +async_read_header( + AsyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser, + ReadHandler&& handler) +{ + parser.eager(false); + return beast::detail::async_read( + stream, + buffer, + detail::read_header_condition< + isRequest>{parser}, + std::forward(handler)); +} + +//------------------------------------------------------------------------------ + +template< + class SyncReadStream, + class DynamicBuffer, + bool isRequest> +std::size_t +read( + SyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser) +{ + static_assert( + is_sync_read_stream::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + error_code ec; + auto const bytes_transferred = + http::read(stream, buffer, parser, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncReadStream, + class DynamicBuffer, + bool isRequest> +std::size_t +read( + SyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser, + error_code& ec) +{ + static_assert( + is_sync_read_stream::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + parser.eager(true); + return beast::detail::read(stream, buffer, + detail::read_all_condition< + isRequest>{parser}, ec); +} + +template< + class AsyncReadStream, + class DynamicBuffer, + bool isRequest, + class ReadHandler> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +async_read( + AsyncReadStream& stream, + DynamicBuffer& buffer, + basic_parser& parser, + ReadHandler&& handler) +{ + static_assert( + is_async_read_stream::value, + "AsyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + parser.eager(true); + return beast::detail::async_read( + stream, + buffer, + detail::read_all_condition< + isRequest>{parser}, + std::forward(handler)); +} + +//------------------------------------------------------------------------------ + +template< + class SyncReadStream, + class DynamicBuffer, + bool isRequest, class Body, class Allocator> +std::size_t +read( + SyncReadStream& stream, + DynamicBuffer& buffer, + message>& msg) +{ + static_assert( + is_sync_read_stream::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_reader::value, + "BodyReader type requirements not met"); + error_code ec; + auto const bytes_transferred = + http::read(stream, buffer, msg, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncReadStream, + class DynamicBuffer, + bool isRequest, class Body, class Allocator> +std::size_t +read( + SyncReadStream& stream, + DynamicBuffer& buffer, + message>& msg, + error_code& ec) +{ + static_assert( + is_sync_read_stream::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_reader::value, + "BodyReader type requirements not met"); + parser p(std::move(msg)); + p.eager(true); + auto const bytes_transferred = + http::read(stream, buffer, p, ec); + if(ec) + return bytes_transferred; + msg = p.release(); + return bytes_transferred; +} + +template< + class AsyncReadStream, + class DynamicBuffer, + bool isRequest, class Body, class Allocator, + class ReadHandler> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +async_read( + AsyncReadStream& stream, + DynamicBuffer& buffer, + message>& msg, + ReadHandler&& handler) +{ + static_assert( + is_async_read_stream::value, + "AsyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer::value, + "DynamicBuffer type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_reader::value, + "BodyReader type requirements not met"); + return net::async_initiate< + ReadHandler, + void(error_code, std::size_t)>( + detail::run_read_msg_op{}, + handler, &stream, &buffer, &msg); +} + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/read.ipp b/boost/beast/http/impl/read.ipp deleted file mode 100644 index 42ce175d8c..0000000000 --- a/boost/beast/http/impl/read.ipp +++ /dev/null @@ -1,857 +0,0 @@ -// -// Copyright (c) 2016-2017 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_READ_IPP_HPP -#define BOOST_BEAST_HTTP_IMPL_READ_IPP_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast { -namespace http { - -namespace detail { - -//------------------------------------------------------------------------------ - -template -class read_some_op - : public boost::asio::coroutine -{ - Stream& s_; - boost::asio::executor_work_guard().get_executor())> wg_; - DynamicBuffer& b_; - basic_parser& p_; - std::size_t bytes_transferred_ = 0; - Handler h_; - bool cont_ = false; - -public: - read_some_op(read_some_op&&) = default; - read_some_op(read_some_op const&) = delete; - - template - read_some_op(DeducedHandler&& h, Stream& s, - DynamicBuffer& b, basic_parser& p) - : s_(s) - , wg_(s_.get_executor()) - , b_(b) - , p_(p) - , h_(std::forward(h)) - { - } - - using allocator_type = - boost::asio::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (boost::asio::get_associated_allocator)(h_); - } - - using executor_type = boost::asio::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (boost::asio::get_associated_executor)( - h_, s_.get_executor()); - } - - void - operator()( - error_code ec, - std::size_t bytes_transferred = 0, - bool cont = true); - - friend - bool asio_handler_is_continuation(read_some_op* op) - { - using boost::asio::asio_handler_is_continuation; - return op->cont_ ? true : - asio_handler_is_continuation( - std::addressof(op->h_)); - } - - template - friend - void asio_handler_invoke(Function&& f, read_some_op* op) - { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } -}; - -template -void -read_some_op:: -operator()( - error_code ec, - std::size_t bytes_transferred, - bool cont) -{ - cont_ = cont; - BOOST_ASIO_CORO_REENTER(*this) - { - if(b_.size() == 0) - goto do_read; - for(;;) - { - // parse - { - auto const used = p_.put(b_.data(), ec); - bytes_transferred_ += used; - b_.consume(used); - } - if(ec != http::error::need_more) - break; - - do_read: - BOOST_ASIO_CORO_YIELD - { - // VFALCO This was read_size_or_throw - auto const size = read_size(b_, 65536); - if(size == 0) - { - ec = error::buffer_overflow; - goto upcall; - } - auto const mb = - beast::detail::dynamic_buffer_prepare( - b_, size, ec, error::buffer_overflow); - if(ec) - goto upcall; - s_.async_read_some(*mb, std::move(*this)); - } - if(ec == boost::asio::error::eof) - { - BOOST_ASSERT(bytes_transferred == 0); - if(p_.got_some()) - { - // caller sees EOF on next read - ec.assign(0, ec.category()); - p_.put_eof(ec); - if(ec) - goto upcall; - BOOST_ASSERT(p_.is_done()); - goto upcall; - } - ec = error::end_of_stream; - break; - } - if(ec) - break; - b_.commit(bytes_transferred); - } - - upcall: - if(! cont_) - { - BOOST_ASIO_CORO_YIELD - boost::asio::post( - s_.get_executor(), - bind_handler(std::move(*this), - ec, bytes_transferred_)); - } - h_(ec, bytes_transferred_); - } -} - -//------------------------------------------------------------------------------ - -struct parser_is_done -{ - template - bool - operator()(basic_parser< - isRequest, Derived> const& p) const - { - return p.is_done(); - } -}; - -struct parser_is_header_done -{ - template - bool - operator()(basic_parser< - isRequest, Derived> const& p) const - { - return p.is_header_done(); - } -}; - -template -class read_op - : public boost::asio::coroutine -{ - Stream& s_; - boost::asio::executor_work_guard().get_executor())> wg_; - DynamicBuffer& b_; - basic_parser& p_; - std::size_t bytes_transferred_ = 0; - Handler h_; - bool cont_ = false; - -public: - read_op(read_op&&) = default; - read_op(read_op const&) = delete; - - template - read_op(DeducedHandler&& h, Stream& s, - DynamicBuffer& b, basic_parser& p) - : s_(s) - , wg_(s_.get_executor()) - , b_(b) - , p_(p) - , h_(std::forward(h)) - { - } - - using allocator_type = - boost::asio::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (boost::asio::get_associated_allocator)(h_); - } - - using executor_type = boost::asio::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (boost::asio::get_associated_executor)( - h_, s_.get_executor()); - } - - void - operator()( - error_code ec, - std::size_t bytes_transferred = 0, - bool cont = true); - - friend - bool asio_handler_is_continuation(read_op* op) - { - using boost::asio::asio_handler_is_continuation; - return op->cont_ ? true : - asio_handler_is_continuation( - std::addressof(op->h_)); - } - - template - friend - void asio_handler_invoke(Function&& f, read_op* op) - { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } -}; - -template -void -read_op:: -operator()( - error_code ec, - std::size_t bytes_transferred, - bool cont) -{ - cont_ = cont; - BOOST_ASIO_CORO_REENTER(*this) - { - if(Condition{}(p_)) - { - BOOST_ASIO_CORO_YIELD - boost::asio::post(s_.get_executor(), - bind_handler(std::move(*this), ec)); - goto upcall; - } - for(;;) - { - BOOST_ASIO_CORO_YIELD - async_read_some( - s_, b_, p_, std::move(*this)); - if(ec) - goto upcall; - bytes_transferred_ += bytes_transferred; - if(Condition{}(p_)) - goto upcall; - } - upcall: - h_(ec, bytes_transferred_); - } -} - -//------------------------------------------------------------------------------ - -template -class read_msg_op - : public boost::asio::coroutine -{ - using parser_type = - parser; - - using message_type = - typename parser_type::value_type; - - struct data - { - Stream& s; - boost::asio::executor_work_guard().get_executor())> wg; - DynamicBuffer& b; - message_type& m; - parser_type p; - std::size_t bytes_transferred = 0; - bool cont = false; - - data(Handler const&, Stream& s_, - DynamicBuffer& b_, message_type& m_) - : s(s_) - , wg(s.get_executor()) - , b(b_) - , m(m_) - , p(std::move(m)) - { - p.eager(true); - } - }; - - handler_ptr d_; - -public: - read_msg_op(read_msg_op&&) = default; - read_msg_op(read_msg_op const&) = delete; - - template - read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args) - : d_(std::forward(h), - s, std::forward(args)...) - { - } - - using allocator_type = - boost::asio::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (boost::asio::get_associated_allocator)(d_.handler()); - } - - using executor_type = boost::asio::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (boost::asio::get_associated_executor)( - d_.handler(), d_->s.get_executor()); - } - - void - operator()( - error_code ec, - std::size_t bytes_transferred = 0, - bool cont = true); - - friend - bool asio_handler_is_continuation(read_msg_op* op) - { - using boost::asio::asio_handler_is_continuation; - return op->d_->cont ? true : - asio_handler_is_continuation( - std::addressof(op->d_.handler())); - } - - template - friend - void asio_handler_invoke(Function&& f, read_msg_op* op) - { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->d_.handler())); - } -}; - -template -void -read_msg_op:: -operator()( - error_code ec, - std::size_t bytes_transferred, - bool cont) -{ - auto& d = *d_; - d.cont = cont; - BOOST_ASIO_CORO_REENTER(*this) - { - for(;;) - { - BOOST_ASIO_CORO_YIELD - async_read_some( - d.s, d.b, d.p, std::move(*this)); - if(ec) - goto upcall; - d.bytes_transferred += - bytes_transferred; - if(d.p.is_done()) - { - d.m = d.p.release(); - goto upcall; - } - } - upcall: - bytes_transferred = d.bytes_transferred; - { - auto wg = std::move(d.wg); - d_.invoke(ec, bytes_transferred); - } - } -} - -} // detail - -//------------------------------------------------------------------------------ - -template< - class SyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived> -std::size_t -read_some( - SyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - BOOST_ASSERT(! parser.is_done()); - error_code ec; - auto const bytes_transferred = - read_some(stream, buffer, parser, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived> -std::size_t -read_some( - SyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser, - error_code& ec) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - BOOST_ASSERT(! parser.is_done()); - std::size_t bytes_transferred = 0; - if(buffer.size() == 0) - goto do_read; - for(;;) - { - // invoke parser - { - auto const n = parser.put(buffer.data(), ec); - bytes_transferred += n; - buffer.consume(n); - if(! ec) - break; - if(ec != http::error::need_more) - break; - } - do_read: - auto const size = read_size(buffer, 65536); - if(size == 0) - { - ec = error::buffer_overflow; - break; - } - auto const mb = - beast::detail::dynamic_buffer_prepare( - buffer, size, ec, error::buffer_overflow); - if(ec) - break; - auto const n = stream.read_some(*mb, ec); - if(ec == boost::asio::error::eof) - { - BOOST_ASSERT(n == 0); - if(parser.got_some()) - { - // caller sees EOF on next read - parser.put_eof(ec); - if(ec) - break; - BOOST_ASSERT(parser.is_done()); - break; - } - ec = error::end_of_stream; - break; - } - if(ec) - break; - buffer.commit(n); - } - return bytes_transferred; -} - -template< - class AsyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived, - class ReadHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) -async_read_some( - AsyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser, - ReadHandler&& handler) -{ - static_assert(is_async_read_stream::value, - "AsyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - BOOST_ASSERT(! parser.is_done()); - BOOST_BEAST_HANDLER_INIT( - ReadHandler, void(error_code, std::size_t)); - detail::read_some_op{ - std::move(init.completion_handler), stream, buffer, parser}( - {}, 0, false); - return init.result.get(); -} - -//------------------------------------------------------------------------------ - -template< - class SyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived> -std::size_t -read_header( - SyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - error_code ec; - auto const bytes_transferred = - read_header(stream, buffer, parser, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived> -std::size_t -read_header( - SyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser, - error_code& ec) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - parser.eager(false); - if(parser.is_header_done()) - { - ec.assign(0, ec.category()); - return 0; - } - std::size_t bytes_transferred = 0; - do - { - bytes_transferred += read_some( - stream, buffer, parser, ec); - if(ec) - return bytes_transferred; - } - while(! parser.is_header_done()); - return bytes_transferred; -} - -template< - class AsyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived, - class ReadHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) -async_read_header( - AsyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser, - ReadHandler&& handler) -{ - static_assert(is_async_read_stream::value, - "AsyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - parser.eager(false); - BOOST_BEAST_HANDLER_INIT( - ReadHandler, void(error_code, std::size_t)); - detail::read_op{ - std::move(init.completion_handler), stream, - buffer, parser}({}, 0, false); - return init.result.get(); -} - -//------------------------------------------------------------------------------ - -template< - class SyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived> -std::size_t -read( - SyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - error_code ec; - auto const bytes_transferred = - read(stream, buffer, parser, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived> -std::size_t -read( - SyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser, - error_code& ec) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - parser.eager(true); - if(parser.is_done()) - { - ec.assign(0, ec.category()); - return 0; - } - std::size_t bytes_transferred = 0; - do - { - bytes_transferred += read_some( - stream, buffer, parser, ec); - if(ec) - return bytes_transferred; - } - while(! parser.is_done()); - return bytes_transferred; -} - -template< - class AsyncReadStream, - class DynamicBuffer, - bool isRequest, class Derived, - class ReadHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) -async_read( - AsyncReadStream& stream, - DynamicBuffer& buffer, - basic_parser& parser, - ReadHandler&& handler) -{ - static_assert(is_async_read_stream::value, - "AsyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - parser.eager(true); - BOOST_BEAST_HANDLER_INIT( - ReadHandler, void(error_code, std::size_t)); - detail::read_op{ - std::move(init.completion_handler), stream, buffer, parser}( - {}, 0, false); - return init.result.get(); -} - -//------------------------------------------------------------------------------ - -template< - class SyncReadStream, - class DynamicBuffer, - bool isRequest, class Body, class Allocator> -std::size_t -read( - SyncReadStream& stream, - DynamicBuffer& buffer, - message>& msg) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_reader::value, - "BodyReader requirements not met"); - error_code ec; - auto const bytes_transferred = - read(stream, buffer, msg, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncReadStream, - class DynamicBuffer, - bool isRequest, class Body, class Allocator> -std::size_t -read( - SyncReadStream& stream, - DynamicBuffer& buffer, - message>& msg, - error_code& ec) -{ - static_assert(is_sync_read_stream::value, - "SyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_reader::value, - "BodyReader requirements not met"); - parser p{std::move(msg)}; - p.eager(true); - auto const bytes_transferred = - read(stream, buffer, p.base(), ec); - if(ec) - return bytes_transferred; - msg = p.release(); - return bytes_transferred; -} - -template< - class AsyncReadStream, - class DynamicBuffer, - bool isRequest, class Body, class Allocator, - class ReadHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) -async_read( - AsyncReadStream& stream, - DynamicBuffer& buffer, - message>& msg, - ReadHandler&& handler) -{ - static_assert(is_async_read_stream::value, - "AsyncReadStream requirements not met"); - static_assert( - boost::asio::is_dynamic_buffer::value, - "DynamicBuffer requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_reader::value, - "BodyReader requirements not met"); - BOOST_BEAST_HANDLER_INIT( - ReadHandler, void(error_code, std::size_t)); - detail::read_msg_op< - AsyncReadStream, - DynamicBuffer, - isRequest, Body, Allocator, - BOOST_ASIO_HANDLER_TYPE( - ReadHandler, void(error_code, std::size_t))>{ - std::move(init.completion_handler), stream, buffer, msg}( - {}, 0, false); - return init.result.get(); -} - -} // http -} // beast -} // boost - -#endif diff --git a/boost/beast/http/impl/rfc7230.hpp b/boost/beast/http/impl/rfc7230.hpp new file mode 100644 index 0000000000..11e424b4d1 --- /dev/null +++ b/boost/beast/http/impl/rfc7230.hpp @@ -0,0 +1,423 @@ +// +// 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_RFC7230_HPP +#define BOOST_BEAST_HTTP_IMPL_RFC7230_HPP + +#include +#include + +namespace boost { +namespace beast { +namespace http { + +class param_list::const_iterator +{ + using iter_type = string_view::const_iterator; + + std::string s_; + detail::param_iter pi_; + +public: + using value_type = param_list::value_type; + using pointer = value_type const*; + using reference = value_type const&; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + const_iterator() = default; + + bool + operator==(const_iterator const& other) const + { + return + other.pi_.it == pi_.it && + other.pi_.last == pi_.last && + other.pi_.first == pi_.first; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + return pi_.v; + } + + pointer + operator->() const + { + return &*(*this); + } + + const_iterator& + operator++() + { + increment(); + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + +private: + friend class param_list; + + const_iterator(iter_type first, iter_type last) + { + pi_.it = first; + pi_.first = first; + pi_.last = last; + increment(); + } + + BOOST_BEAST_DECL + static + std::string + unquote(string_view sr); + + BOOST_BEAST_DECL + void + increment(); +}; + +inline +auto +param_list:: +begin() const -> + const_iterator +{ + return const_iterator{s_.begin(), s_.end()}; +} + +inline +auto +param_list:: +end() const -> + const_iterator +{ + return const_iterator{s_.end(), s_.end()}; +} + +inline +auto +param_list:: +cbegin() const -> + const_iterator +{ + return const_iterator{s_.begin(), s_.end()}; +} + +inline +auto +param_list:: +cend() const -> + const_iterator +{ + return const_iterator{s_.end(), s_.end()}; +} + +//------------------------------------------------------------------------------ + +class ext_list::const_iterator +{ + ext_list::value_type v_; + iter_type it_; + iter_type first_; + iter_type last_; + +public: + using value_type = ext_list::value_type; + using pointer = value_type const*; + using reference = value_type const&; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + const_iterator() = default; + + bool + operator==(const_iterator const& other) const + { + return + other.it_ == it_ && + other.first_ == first_ && + other.last_ == last_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + return v_; + } + + pointer + operator->() const + { + return &*(*this); + } + + const_iterator& + operator++() + { + increment(); + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + +private: + friend class ext_list; + + const_iterator(iter_type begin, iter_type end) + { + it_ = begin; + first_ = begin; + last_ = end; + increment(); + } + + BOOST_BEAST_DECL + void + increment(); +}; + +inline +auto +ext_list:: +begin() const -> + const_iterator +{ + return const_iterator{s_.begin(), s_.end()}; +} + +inline +auto +ext_list:: +end() const -> + const_iterator +{ + return const_iterator{s_.end(), s_.end()}; +} + +inline +auto +ext_list:: +cbegin() const -> + const_iterator +{ + return const_iterator{s_.begin(), s_.end()}; +} + +inline +auto +ext_list:: +cend() const -> + const_iterator +{ + return const_iterator{s_.end(), s_.end()}; +} + +template +auto +ext_list:: +find(T const& s) -> + const_iterator +{ + return std::find_if(begin(), end(), + [&s](value_type const& v) + { + return iequals(s, v.first); + }); +} + +template +bool +ext_list:: +exists(T const& s) +{ + return find(s) != end(); +} + + +//------------------------------------------------------------------------------ + +class token_list::const_iterator +{ + token_list::value_type v_; + iter_type it_; + iter_type first_; + iter_type last_; + +public: + using value_type = token_list::value_type; + using pointer = value_type const*; + using reference = value_type const&; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + const_iterator() = default; + + bool + operator==(const_iterator const& other) const + { + return + other.it_ == it_ && + other.first_ == first_ && + other.last_ == last_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + return v_; + } + + pointer + operator->() const + { + return &*(*this); + } + + const_iterator& + operator++() + { + increment(); + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + +private: + friend class token_list; + + const_iterator(iter_type begin, iter_type end) + { + it_ = begin; + first_ = begin; + last_ = end; + increment(); + } + + BOOST_BEAST_DECL + void + increment(); +}; + +inline +auto +token_list:: +begin() const -> + const_iterator +{ + return const_iterator{s_.begin(), s_.end()}; +} + +inline +auto +token_list:: +end() const -> + const_iterator +{ + return const_iterator{s_.end(), s_.end()}; +} + +inline +auto +token_list:: +cbegin() const -> + const_iterator +{ + return const_iterator{s_.begin(), s_.end()}; +} + +inline +auto +token_list:: +cend() const -> + const_iterator +{ + return const_iterator{s_.end(), s_.end()}; +} + +template +bool +token_list:: +exists(T const& s) +{ + return std::find_if(begin(), end(), + [&s](value_type const& v) + { + return iequals(s, v); + } + ) != end(); +} + +template +bool +validate_list(detail::basic_parsed_list< + Policy> const& list) +{ + auto const last = list.end(); + auto it = list.begin(); + if(it.error()) + return false; + while(it != last) + { + ++it; + if(it.error()) + return false; + if(it == last) + break; + } + return true; +} + +} // http +} // beast +} // boost + +#ifdef BOOST_BEAST_HEADER_ONLY +#include +#endif + +#endif + diff --git a/boost/beast/http/impl/rfc7230.ipp b/boost/beast/http/impl/rfc7230.ipp index 96ec902ceb..cf60cefa36 100644 --- a/boost/beast/http/impl/rfc7230.ipp +++ b/boost/beast/http/impl/rfc7230.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -10,129 +10,13 @@ #ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_IPP #define BOOST_BEAST_HTTP_IMPL_RFC7230_IPP -#include -#include +#include namespace boost { namespace beast { namespace http { -class param_list::const_iterator -{ - using iter_type = string_view::const_iterator; - - std::string s_; - detail::param_iter pi_; - -public: - using value_type = param_list::value_type; - using pointer = value_type const*; - using reference = value_type const&; - using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; - - const_iterator() = default; - - bool - operator==(const_iterator const& other) const - { - return - other.pi_.it == pi_.it && - other.pi_.last == pi_.last && - other.pi_.first == pi_.first; - } - - bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const - { - return pi_.v; - } - - pointer - operator->() const - { - return &*(*this); - } - - const_iterator& - operator++() - { - increment(); - return *this; - } - - const_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - -private: - friend class param_list; - - const_iterator(iter_type first, iter_type last) - { - pi_.it = first; - pi_.first = first; - pi_.last = last; - increment(); - } - - template - static - std::string - unquote(string_view sr); - - template - void - increment(); -}; - -inline -auto -param_list:: -begin() const -> - const_iterator -{ - return const_iterator{s_.begin(), s_.end()}; -} - -inline -auto -param_list:: -end() const -> - const_iterator -{ - return const_iterator{s_.end(), s_.end()}; -} - -inline -auto -param_list:: -cbegin() const -> - const_iterator -{ - return const_iterator{s_.begin(), s_.end()}; -} - -inline -auto -param_list:: -cend() const -> - const_iterator -{ - return const_iterator{s_.end(), s_.end()}; -} -template std::string param_list::const_iterator:: unquote(string_view sr) @@ -151,7 +35,6 @@ unquote(string_view sr) return s; } -template void param_list::const_iterator:: increment() @@ -172,140 +55,6 @@ increment() } } -//------------------------------------------------------------------------------ - -class ext_list::const_iterator -{ - ext_list::value_type v_; - iter_type it_; - iter_type first_; - iter_type last_; - -public: - using value_type = ext_list::value_type; - using pointer = value_type const*; - using reference = value_type const&; - using difference_type = std::ptrdiff_t; - using iterator_category = std::forward_iterator_tag; - - const_iterator() = default; - - bool - operator==(const_iterator const& other) const - { - return - other.it_ == it_ && - other.first_ == first_ && - other.last_ == last_; - } - - bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const - { - return v_; - } - - pointer - operator->() const - { - return &*(*this); - } - - const_iterator& - operator++() - { - increment(); - return *this; - } - - const_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - -private: - friend class ext_list; - - const_iterator(iter_type begin, iter_type end) - { - it_ = begin; - first_ = begin; - last_ = end; - increment(); - } - - template - void - increment(); -}; - -inline -auto -ext_list:: -begin() const -> - const_iterator -{ - return const_iterator{s_.begin(), s_.end()}; -} - -inline -auto -ext_list:: -end() const -> - const_iterator -{ - return const_iterator{s_.end(), s_.end()}; -} - -inline -auto -ext_list:: -cbegin() const -> - const_iterator -{ - return const_iterator{s_.begin(), s_.end()}; -} - -inline -auto -ext_list:: -cend() const -> - const_iterator -{ - return const_iterator{s_.end(), s_.end()}; -} - -template -auto -ext_list:: -find(T const& s) -> - const_iterator -{ - return std::find_if(begin(), end(), - [&s](value_type const& v) - { - return iequals(s, v.first); - }); -} - -template -bool -ext_list:: -exists(T const& s) -{ - return find(s) != end(); -} - -template void ext_list::const_iterator:: increment() @@ -316,7 +65,7 @@ increment() param-list = *( OWS ";" OWS param ) param = token OWS "=" OWS ( token / quoted-string ) - chunked;a=b;i=j,gzip;windowBits=12 + chunked;a=b;i=j;gzip;windowBits=12 x,y ,,,,,chameleon */ @@ -350,6 +99,8 @@ increment() } v_.first = string_view{&*p0, static_cast(it_ - p0)}; + if (it_ == last_) + return; detail::param_iter pi; pi.it = it_; pi.first = it_; @@ -372,119 +123,6 @@ increment() } } -//------------------------------------------------------------------------------ - -class token_list::const_iterator -{ - token_list::value_type v_; - iter_type it_; - iter_type first_; - iter_type last_; - -public: - using value_type = token_list::value_type; - using pointer = value_type const*; - using reference = value_type const&; - using difference_type = std::ptrdiff_t; - using iterator_category = std::forward_iterator_tag; - - const_iterator() = default; - - bool - operator==(const_iterator const& other) const - { - return - other.it_ == it_ && - other.first_ == first_ && - other.last_ == last_; - } - - bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const - { - return v_; - } - - pointer - operator->() const - { - return &*(*this); - } - - const_iterator& - operator++() - { - increment(); - return *this; - } - - const_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - -private: - friend class token_list; - - const_iterator(iter_type begin, iter_type end) - { - it_ = begin; - first_ = begin; - last_ = end; - increment(); - } - - template - void - increment(); -}; - -inline -auto -token_list:: -begin() const -> - const_iterator -{ - return const_iterator{s_.begin(), s_.end()}; -} - -inline -auto -token_list:: -end() const -> - const_iterator -{ - return const_iterator{s_.end(), s_.end()}; -} - -inline -auto -token_list:: -cbegin() const -> - const_iterator -{ - return const_iterator{s_.begin(), s_.end()}; -} - -inline -auto -token_list:: -cend() const -> - const_iterator -{ - return const_iterator{s_.end(), s_.end()}; -} - -template void token_list::const_iterator:: increment() @@ -531,42 +169,8 @@ increment() } } -template -bool -token_list:: -exists(T const& s) -{ - return std::find_if(begin(), end(), - [&s](value_type const& v) - { - return iequals(s, v); - } - ) != end(); -} - -template -bool -validate_list(detail::basic_parsed_list< - Policy> const& list) -{ - auto const last = list.end(); - auto it = list.begin(); - if(it.error()) - return false; - while(it != last) - { - ++it; - if(it.error()) - return false; - if(it == last) - break; - } - return true; -} - } // http } // beast } // boost -#endif - +#endif // BOOST_BEAST_HTTP_IMPL_RFC7230_IPP diff --git a/boost/beast/http/impl/serializer.hpp b/boost/beast/http/impl/serializer.hpp new file mode 100644 index 0000000000..39efb482d0 --- /dev/null +++ b/boost/beast/http/impl/serializer.hpp @@ -0,0 +1,431 @@ +// +// 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_SERIALIZER_HPP +#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { + +template< + bool isRequest, class Body, class Fields> +void +serializer:: +fwrinit(std::true_type) +{ + fwr_.emplace(m_, m_.version(), m_.method()); +} + +template< + bool isRequest, class Body, class Fields> +void +serializer:: +fwrinit(std::false_type) +{ + fwr_.emplace(m_, m_.version(), m_.result_int()); +} + +template< + bool isRequest, class Body, class Fields> +template +inline +void +serializer:: +do_visit(error_code& ec, Visit& visit) +{ + pv_.template emplace(limit_, v_.template get()); + visit(ec, beast::detail::make_buffers_ref( + pv_.template get())); +} + +//------------------------------------------------------------------------------ + +template< + bool isRequest, class Body, class Fields> +serializer:: +serializer(value_type& m) + : m_(m) + , wr_(m_.base(), m_.body()) +{ +} + +template< + bool isRequest, class Body, class Fields> +template +void +serializer:: +next(error_code& ec, Visit&& visit) +{ + switch(s_) + { + case do_construct: + { + fwrinit(std::integral_constant{}); + if(m_.chunked()) + goto go_init_c; + s_ = do_init; + BOOST_FALLTHROUGH; + } + + case do_init: + { + wr_.init(ec); + if(ec) + return; + if(split_) + goto go_header_only; + auto result = wr_.get(ec); + if(ec == error::need_more) + goto go_header_only; + if(ec) + return; + if(! result) + goto go_header_only; + more_ = result->second; + v_.template emplace<2>( + boost::in_place_init, + fwr_->get(), + result->first); + s_ = do_header; + BOOST_FALLTHROUGH; + } + + case do_header: + do_visit<2>(ec, visit); + break; + + go_header_only: + v_.template emplace<1>(fwr_->get()); + s_ = do_header_only; + BOOST_FALLTHROUGH; + case do_header_only: + do_visit<1>(ec, visit); + break; + + case do_body: + s_ = do_body + 1; + BOOST_FALLTHROUGH; + + case do_body + 1: + { + auto result = wr_.get(ec); + if(ec) + return; + if(! result) + goto go_complete; + more_ = result->second; + v_.template emplace<3>(result->first); + s_ = do_body + 2; + BOOST_FALLTHROUGH; + } + + case do_body + 2: + do_visit<3>(ec, visit); + break; + + //---------------------------------------------------------------------- + + go_init_c: + s_ = do_init_c; + BOOST_FALLTHROUGH; + case do_init_c: + { + wr_.init(ec); + if(ec) + return; + if(split_) + goto go_header_only_c; + auto result = wr_.get(ec); + if(ec == error::need_more) + goto go_header_only_c; + if(ec) + return; + if(! result) + goto go_header_only_c; + more_ = result->second; + if(! more_) + { + // do it all in one buffer + v_.template emplace<7>( + boost::in_place_init, + fwr_->get(), + buffer_bytes(result->first), + net::const_buffer{nullptr, 0}, + chunk_crlf{}, + result->first, + chunk_crlf{}, + detail::chunk_last(), + net::const_buffer{nullptr, 0}, + chunk_crlf{}); + goto go_all_c; + } + v_.template emplace<4>( + boost::in_place_init, + fwr_->get(), + buffer_bytes(result->first), + net::const_buffer{nullptr, 0}, + chunk_crlf{}, + result->first, + chunk_crlf{}); + s_ = do_header_c; + BOOST_FALLTHROUGH; + } + + case do_header_c: + do_visit<4>(ec, visit); + break; + + go_header_only_c: + v_.template emplace<1>(fwr_->get()); + s_ = do_header_only_c; + BOOST_FALLTHROUGH; + + case do_header_only_c: + do_visit<1>(ec, visit); + break; + + case do_body_c: + s_ = do_body_c + 1; + BOOST_FALLTHROUGH; + + case do_body_c + 1: + { + auto result = wr_.get(ec); + if(ec) + return; + if(! result) + goto go_final_c; + more_ = result->second; + if(! more_) + { + // do it all in one buffer + v_.template emplace<6>( + boost::in_place_init, + buffer_bytes(result->first), + net::const_buffer{nullptr, 0}, + chunk_crlf{}, + result->first, + chunk_crlf{}, + detail::chunk_last(), + net::const_buffer{nullptr, 0}, + chunk_crlf{}); + goto go_body_final_c; + } + v_.template emplace<5>( + boost::in_place_init, + buffer_bytes(result->first), + net::const_buffer{nullptr, 0}, + chunk_crlf{}, + result->first, + chunk_crlf{}); + s_ = do_body_c + 2; + BOOST_FALLTHROUGH; + } + + case do_body_c + 2: + do_visit<5>(ec, visit); + break; + + go_body_final_c: + s_ = do_body_final_c; + BOOST_FALLTHROUGH; + case do_body_final_c: + do_visit<6>(ec, visit); + break; + + go_all_c: + s_ = do_all_c; + BOOST_FALLTHROUGH; + case do_all_c: + do_visit<7>(ec, visit); + break; + + go_final_c: + case do_final_c: + v_.template emplace<8>( + boost::in_place_init, + detail::chunk_last(), + net::const_buffer{nullptr, 0}, + chunk_crlf{}); + s_ = do_final_c + 1; + BOOST_FALLTHROUGH; + + case do_final_c + 1: + do_visit<8>(ec, visit); + break; + + //---------------------------------------------------------------------- + + default: + case do_complete: + BOOST_ASSERT(false); + break; + + go_complete: + s_ = do_complete; + break; + } +} + +template< + bool isRequest, class Body, class Fields> +void +serializer:: +consume(std::size_t n) +{ + switch(s_) + { + case do_header: + BOOST_ASSERT( + n <= buffer_bytes(v_.template get<2>())); + v_.template get<2>().consume(n); + if(buffer_bytes(v_.template get<2>()) > 0) + break; + header_done_ = true; + v_.reset(); + if(! more_) + goto go_complete; + s_ = do_body + 1; + break; + + case do_header_only: + BOOST_ASSERT( + n <= buffer_bytes(v_.template get<1>())); + v_.template get<1>().consume(n); + if(buffer_bytes(v_.template get<1>()) > 0) + break; + fwr_ = boost::none; + header_done_ = true; + if(! split_) + goto go_complete; + s_ = do_body; + break; + + case do_body + 2: + { + BOOST_ASSERT( + n <= buffer_bytes(v_.template get<3>())); + v_.template get<3>().consume(n); + if(buffer_bytes(v_.template get<3>()) > 0) + break; + v_.reset(); + if(! more_) + goto go_complete; + s_ = do_body + 1; + break; + } + + //---------------------------------------------------------------------- + + case do_header_c: + BOOST_ASSERT( + n <= buffer_bytes(v_.template get<4>())); + v_.template get<4>().consume(n); + if(buffer_bytes(v_.template get<4>()) > 0) + break; + header_done_ = true; + v_.reset(); + if(more_) + s_ = do_body_c + 1; + else + s_ = do_final_c; + break; + + case do_header_only_c: + { + BOOST_ASSERT( + n <= buffer_bytes(v_.template get<1>())); + v_.template get<1>().consume(n); + if(buffer_bytes(v_.template get<1>()) > 0) + break; + fwr_ = boost::none; + header_done_ = true; + if(! split_) + { + s_ = do_final_c; + break; + } + s_ = do_body_c; + break; + } + + case do_body_c + 2: + BOOST_ASSERT( + n <= buffer_bytes(v_.template get<5>())); + v_.template get<5>().consume(n); + if(buffer_bytes(v_.template get<5>()) > 0) + break; + v_.reset(); + if(more_) + s_ = do_body_c + 1; + else + s_ = do_final_c; + break; + + case do_body_final_c: + { + BOOST_ASSERT( + n <= buffer_bytes(v_.template get<6>())); + v_.template get<6>().consume(n); + if(buffer_bytes(v_.template get<6>()) > 0) + break; + v_.reset(); + s_ = do_complete; + break; + } + + case do_all_c: + { + BOOST_ASSERT( + n <= buffer_bytes(v_.template get<7>())); + v_.template get<7>().consume(n); + if(buffer_bytes(v_.template get<7>()) > 0) + break; + header_done_ = true; + v_.reset(); + s_ = do_complete; + break; + } + + case do_final_c + 1: + BOOST_ASSERT(buffer_bytes(v_.template get<8>())); + v_.template get<8>().consume(n); + if(buffer_bytes(v_.template get<8>()) > 0) + break; + v_.reset(); + goto go_complete; + + //---------------------------------------------------------------------- + + default: + BOOST_ASSERT(false); + case do_complete: + break; + + go_complete: + s_ = do_complete; + break; + } +} + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/serializer.ipp b/boost/beast/http/impl/serializer.ipp deleted file mode 100644 index b9e7d26bdb..0000000000 --- a/boost/beast/http/impl/serializer.ipp +++ /dev/null @@ -1,432 +0,0 @@ -// -// Copyright (c) 2016-2017 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_SERIALIZER_IPP -#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_IPP - -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast { -namespace http { - -template< - bool isRequest, class Body, class Fields> -void -serializer:: -fwrinit(std::true_type) -{ - fwr_.emplace(m_, m_.version(), m_.method()); -} - -template< - bool isRequest, class Body, class Fields> -void -serializer:: -fwrinit(std::false_type) -{ - fwr_.emplace(m_, m_.version(), m_.result_int()); -} - -template< - bool isRequest, class Body, class Fields> -template -inline -void -serializer:: -do_visit(error_code& ec, Visit& visit) -{ - pv_.template emplace(limit_, v_.template get()); - visit(ec, beast::detail::make_buffers_ref( - pv_.template get())); -} - -//------------------------------------------------------------------------------ - -template< - bool isRequest, class Body, class Fields> -serializer:: -serializer(value_type& m) - : m_(m) - , wr_(m_.base(), m_.body()) -{ -} - -template< - bool isRequest, class Body, class Fields> -template -void -serializer:: -next(error_code& ec, Visit&& visit) -{ - using boost::asio::buffer_size; - switch(s_) - { - case do_construct: - { - fwrinit(std::integral_constant{}); - if(m_.chunked()) - goto go_init_c; - s_ = do_init; - BOOST_FALLTHROUGH; - } - - case do_init: - { - wr_.init(ec); - if(ec) - return; - if(split_) - goto go_header_only; - auto result = wr_.get(ec); - if(ec == error::need_more) - goto go_header_only; - if(ec) - return; - if(! result) - goto go_header_only; - more_ = result->second; - v_.template emplace<2>( - boost::in_place_init, - fwr_->get(), - result->first); - s_ = do_header; - BOOST_FALLTHROUGH; - } - - case do_header: - do_visit<2>(ec, visit); - break; - - go_header_only: - v_.template emplace<1>(fwr_->get()); - s_ = do_header_only; - BOOST_FALLTHROUGH; - case do_header_only: - do_visit<1>(ec, visit); - break; - - case do_body: - s_ = do_body + 1; - BOOST_FALLTHROUGH; - - case do_body + 1: - { - auto result = wr_.get(ec); - if(ec) - return; - if(! result) - goto go_complete; - more_ = result->second; - v_.template emplace<3>(result->first); - s_ = do_body + 2; - BOOST_FALLTHROUGH; - } - - case do_body + 2: - do_visit<3>(ec, visit); - break; - - //---------------------------------------------------------------------- - - go_init_c: - s_ = do_init_c; - BOOST_FALLTHROUGH; - case do_init_c: - { - wr_.init(ec); - if(ec) - return; - if(split_) - goto go_header_only_c; - auto result = wr_.get(ec); - if(ec == error::need_more) - goto go_header_only_c; - if(ec) - return; - if(! result) - goto go_header_only_c; - more_ = result->second; - if(! more_) - { - // do it all in one buffer - v_.template emplace<7>( - boost::in_place_init, - fwr_->get(), - buffer_size(result->first), - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}, - result->first, - chunk_crlf{}, - detail::chunk_last(), - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}); - goto go_all_c; - } - v_.template emplace<4>( - boost::in_place_init, - fwr_->get(), - buffer_size(result->first), - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}, - result->first, - chunk_crlf{}); - s_ = do_header_c; - BOOST_FALLTHROUGH; - } - - case do_header_c: - do_visit<4>(ec, visit); - break; - - go_header_only_c: - v_.template emplace<1>(fwr_->get()); - s_ = do_header_only_c; - BOOST_FALLTHROUGH; - - case do_header_only_c: - do_visit<1>(ec, visit); - break; - - case do_body_c: - s_ = do_body_c + 1; - BOOST_FALLTHROUGH; - - case do_body_c + 1: - { - auto result = wr_.get(ec); - if(ec) - return; - if(! result) - goto go_final_c; - more_ = result->second; - if(! more_) - { - // do it all in one buffer - v_.template emplace<6>( - boost::in_place_init, - buffer_size(result->first), - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}, - result->first, - chunk_crlf{}, - detail::chunk_last(), - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}); - goto go_body_final_c; - } - v_.template emplace<5>( - boost::in_place_init, - buffer_size(result->first), - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}, - result->first, - chunk_crlf{}); - s_ = do_body_c + 2; - BOOST_FALLTHROUGH; - } - - case do_body_c + 2: - do_visit<5>(ec, visit); - break; - - go_body_final_c: - s_ = do_body_final_c; - BOOST_FALLTHROUGH; - case do_body_final_c: - do_visit<6>(ec, visit); - break; - - go_all_c: - s_ = do_all_c; - BOOST_FALLTHROUGH; - case do_all_c: - do_visit<7>(ec, visit); - break; - - go_final_c: - case do_final_c: - v_.template emplace<8>( - boost::in_place_init, - detail::chunk_last(), - boost::asio::const_buffer{nullptr, 0}, - chunk_crlf{}); - s_ = do_final_c + 1; - BOOST_FALLTHROUGH; - - case do_final_c + 1: - do_visit<8>(ec, visit); - break; - - //---------------------------------------------------------------------- - - default: - case do_complete: - BOOST_ASSERT(false); - break; - - go_complete: - s_ = do_complete; - break; - } -} - -template< - bool isRequest, class Body, class Fields> -void -serializer:: -consume(std::size_t n) -{ - using boost::asio::buffer_size; - switch(s_) - { - case do_header: - BOOST_ASSERT( - n <= buffer_size(v_.template get<2>())); - v_.template get<2>().consume(n); - if(buffer_size(v_.template get<2>()) > 0) - break; - header_done_ = true; - v_.reset(); - if(! more_) - goto go_complete; - s_ = do_body + 1; - break; - - case do_header_only: - BOOST_ASSERT( - n <= buffer_size(v_.template get<1>())); - v_.template get<1>().consume(n); - if(buffer_size(v_.template get<1>()) > 0) - break; - fwr_ = boost::none; - header_done_ = true; - if(! split_) - goto go_complete; - s_ = do_body; - break; - - case do_body + 2: - { - BOOST_ASSERT( - n <= buffer_size(v_.template get<3>())); - v_.template get<3>().consume(n); - if(buffer_size(v_.template get<3>()) > 0) - break; - v_.reset(); - if(! more_) - goto go_complete; - s_ = do_body + 1; - break; - } - - //---------------------------------------------------------------------- - - case do_header_c: - BOOST_ASSERT( - n <= buffer_size(v_.template get<4>())); - v_.template get<4>().consume(n); - if(buffer_size(v_.template get<4>()) > 0) - break; - header_done_ = true; - v_.reset(); - if(more_) - s_ = do_body_c + 1; - else - s_ = do_final_c; - break; - - case do_header_only_c: - { - BOOST_ASSERT( - n <= buffer_size(v_.template get<1>())); - v_.template get<1>().consume(n); - if(buffer_size(v_.template get<1>()) > 0) - break; - fwr_ = boost::none; - header_done_ = true; - if(! split_) - { - s_ = do_final_c; - break; - } - s_ = do_body_c; - break; - } - - case do_body_c + 2: - BOOST_ASSERT( - n <= buffer_size(v_.template get<5>())); - v_.template get<5>().consume(n); - if(buffer_size(v_.template get<5>()) > 0) - break; - v_.reset(); - if(more_) - s_ = do_body_c + 1; - else - s_ = do_final_c; - break; - - case do_body_final_c: - { - BOOST_ASSERT( - n <= buffer_size(v_.template get<6>())); - v_.template get<6>().consume(n); - if(buffer_size(v_.template get<6>()) > 0) - break; - v_.reset(); - s_ = do_complete; - break; - } - - case do_all_c: - { - BOOST_ASSERT( - n <= buffer_size(v_.template get<7>())); - v_.template get<7>().consume(n); - if(buffer_size(v_.template get<7>()) > 0) - break; - header_done_ = true; - v_.reset(); - s_ = do_complete; - break; - } - - case do_final_c + 1: - BOOST_ASSERT(buffer_size(v_.template get<8>())); - v_.template get<8>().consume(n); - if(buffer_size(v_.template get<8>()) > 0) - break; - v_.reset(); - goto go_complete; - - //---------------------------------------------------------------------- - - default: - BOOST_ASSERT(false); - case do_complete: - break; - - go_complete: - s_ = do_complete; - break; - } -} - -} // http -} // beast -} // boost - -#endif diff --git a/boost/beast/http/impl/status.ipp b/boost/beast/http/impl/status.ipp index 7d37919423..2a61acd5c9 100644 --- a/boost/beast/http/impl/status.ipp +++ b/boost/beast/http/impl/status.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -10,15 +10,13 @@ #ifndef BOOST_BEAST_HTTP_IMPL_STATUS_IPP #define BOOST_BEAST_HTTP_IMPL_STATUS_IPP -#include +#include #include namespace boost { namespace beast { namespace http { -namespace detail { -template status int_to_status(unsigned v) { @@ -107,9 +105,30 @@ int_to_status(unsigned v) return status::unknown; } -template +status_class +to_status_class(unsigned v) +{ + switch(v / 100) + { + case 1: return status_class::informational; + case 2: return status_class::successful; + case 3: return status_class::redirection; + case 4: return status_class::client_error; + case 5: return status_class::server_error; + default: + break; + } + return status_class::unknown; +} + +status_class +to_status_class(status v) +{ + return to_status_class(static_cast(v)); +} + string_view -status_to_string(unsigned v) +obsolete_reason(status v) { switch(static_cast(v)) { @@ -190,55 +209,6 @@ status_to_string(unsigned v) return ""; } -template -status_class -to_status_class(unsigned v) -{ - switch(v / 100) - { - case 1: return status_class::informational; - case 2: return status_class::successful; - case 3: return status_class::redirection; - case 4: return status_class::client_error; - case 5: return status_class::server_error; - default: - break; - } - return status_class::unknown; -} - -} // detail - -inline -status -int_to_status(unsigned v) -{ - return detail::int_to_status(v); -} - -inline -status_class -to_status_class(unsigned v) -{ - return detail::to_status_class(v); -} - -inline -status_class -to_status_class(status v) -{ - return to_status_class(static_cast(v)); -} - -inline -string_view -obsolete_reason(status v) -{ - return detail::status_to_string( - static_cast(v)); -} - -inline std::ostream& operator<<(std::ostream& os, status v) { diff --git a/boost/beast/http/impl/verb.ipp b/boost/beast/http/impl/verb.ipp index 36a1734c33..20ef47c551 100644 --- a/boost/beast/http/impl/verb.ipp +++ b/boost/beast/http/impl/verb.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -10,7 +10,7 @@ #ifndef BOOST_BEAST_HTTP_IMPL_VERB_IPP #define BOOST_BEAST_HTTP_IMPL_VERB_IPP -#include +#include #include #include @@ -18,12 +18,8 @@ namespace boost { namespace beast { namespace http { -namespace detail { - -template -inline string_view -verb_to_string(verb v) +to_string(verb v) { switch(v) { @@ -74,7 +70,6 @@ verb_to_string(verb v) BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"}); } -template verb string_to_verb(string_view v) { @@ -314,22 +309,6 @@ string_to_verb(string_view v) return verb::unknown; } -} // detail - -inline -string_view -to_string(verb v) -{ - return detail::verb_to_string(v); -} - -inline -verb -string_to_verb(string_view s) -{ - return detail::string_to_verb(s); -} - } // http } // beast } // boost diff --git a/boost/beast/http/impl/write.hpp b/boost/beast/http/impl/write.hpp new file mode 100644 index 0000000000..5636ce92f8 --- /dev/null +++ b/boost/beast/http/impl/write.hpp @@ -0,0 +1,950 @@ +// +// 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_WRITE_HPP +#define BOOST_BEAST_HTTP_IMPL_WRITE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace beast { +namespace http { +namespace detail { + +template< + class Handler, + class Stream, + bool isRequest, class Body, class Fields> +class write_some_op + : public beast::async_base< + Handler, beast::executor_type> +{ + Stream& s_; + serializer& sr_; + + class lambda + { + write_some_op& op_; + + public: + bool invoked = false; + + explicit + lambda(write_some_op& op) + : op_(op) + { + } + + template + void + operator()( + error_code& ec, + ConstBufferSequence const& buffers) + { + invoked = true; + ec = {}; + op_.s_.async_write_some( + buffers, std::move(op_)); + } + }; + +public: + template + write_some_op( + Handler_&& h, + Stream& s, + serializer& sr) + : async_base< + Handler, beast::executor_type>( + std::forward(h), s.get_executor()) + , s_(s) + , sr_(sr) + { + (*this)(); + } + + void + operator()() + { + error_code ec; + if(! sr_.is_done()) + { + lambda f{*this}; + sr_.next(ec, f); + if(ec) + { + BOOST_ASSERT(! f.invoked); + return net::post( + s_.get_executor(), + beast::bind_front_handler( + std::move(*this), ec, 0)); + } + if(f.invoked) + { + // *this is now moved-from, + return; + } + // What else could it be? + BOOST_ASSERT(sr_.is_done()); + } + + return net::post( + s_.get_executor(), + beast::bind_front_handler( + std::move(*this), ec, 0)); + } + + void + operator()( + error_code ec, + std::size_t bytes_transferred) + { + if(! ec) + sr_.consume(bytes_transferred); + this->complete_now(ec, bytes_transferred); + } +}; + +//------------------------------------------------------------------------------ + +struct serializer_is_header_done +{ + template< + bool isRequest, class Body, class Fields> + bool + operator()( + serializer& sr) const + { + return sr.is_header_done(); + } +}; + +struct serializer_is_done +{ + template< + bool isRequest, class Body, class Fields> + bool + operator()( + serializer& sr) const + { + return sr.is_done(); + } +}; + +//------------------------------------------------------------------------------ + +template< + class Handler, + class Stream, + class Predicate, + bool isRequest, class Body, class Fields> +class write_op + : public beast::async_base< + Handler, beast::executor_type> + , public net::coroutine +{ + Stream& s_; + serializer& sr_; + std::size_t bytes_transferred_ = 0; + +public: + template + write_op( + Handler_&& h, + Stream& s, + serializer& sr) + : async_base< + Handler, beast::executor_type>( + std::forward(h), s.get_executor()) + , s_(s) + , sr_(sr) + { + (*this)(); + } + + void + operator()( + error_code ec = {}, + std::size_t bytes_transferred = 0) + { + BOOST_ASIO_CORO_REENTER(*this) + { + if(Predicate{}(sr_)) + { + BOOST_ASIO_CORO_YIELD + net::post( + s_.get_executor(), + std::move(*this)); + goto upcall; + } + for(;;) + { + BOOST_ASIO_CORO_YIELD + beast::http::async_write_some( + s_, sr_, std::move(*this)); + bytes_transferred_ += bytes_transferred; + if(ec) + goto upcall; + if(Predicate{}(sr_)) + break; + } + upcall: + this->complete_now(ec, bytes_transferred_); + } + } +}; + +//------------------------------------------------------------------------------ + +template< + class Handler, + class Stream, + bool isRequest, class Body, class Fields> +class write_msg_op + : public beast::stable_async_base< + Handler, beast::executor_type> +{ + Stream& s_; + serializer& sr_; + +public: + template< + class Handler_, + class... Args> + write_msg_op( + Handler_&& h, + Stream& s, + Args&&... args) + : stable_async_base< + Handler, beast::executor_type>( + std::forward(h), s.get_executor()) + , s_(s) + , sr_(beast::allocate_stable< + serializer>( + *this, std::forward(args)...)) + { + (*this)(); + } + + void + operator()() + { + async_write(s_, sr_, std::move(*this)); + } + + void + operator()( + error_code ec, std::size_t bytes_transferred) + { + this->complete_now(ec, bytes_transferred); + } +}; + +struct run_write_some_op +{ + template< + class WriteHandler, + class Stream, + bool isRequest, class Body, class Fields> + void + operator()( + WriteHandler&& h, + Stream* s, + serializer* sr) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable::value, + "WriteHandler type requirements not met"); + + write_some_op< + typename std::decay::type, + Stream, + isRequest, Body, Fields>( + std::forward(h), *s, *sr); + } +}; + +struct run_write_op +{ + template< + class WriteHandler, + class Stream, + class Predicate, + bool isRequest, class Body, class Fields> + void + operator()( + WriteHandler&& h, + Stream* s, + Predicate const&, + serializer* sr) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable::value, + "WriteHandler type requirements not met"); + + write_op< + typename std::decay::type, + Stream, + Predicate, + isRequest, Body, Fields>( + std::forward(h), *s, *sr); + } +}; + +struct run_write_msg_op +{ + template< + class WriteHandler, + class Stream, + bool isRequest, class Body, class Fields, + class... Args> + void + operator()( + WriteHandler&& h, + Stream* s, + message* m, + std::false_type, + Args&&... args) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable::value, + "WriteHandler type requirements not met"); + + write_msg_op< + typename std::decay::type, + Stream, + isRequest, Body, Fields>( + std::forward(h), *s, *m, + std::forward(args)...); + } + + template< + class WriteHandler, + class Stream, + bool isRequest, class Body, class Fields, + class... Args> + void + operator()( + WriteHandler&& h, + Stream* s, + message const* m, + std::true_type, + Args&&... args) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable::value, + "WriteHandler type requirements not met"); + + write_msg_op< + typename std::decay::type, + Stream, + isRequest, Body, Fields>( + std::forward(h), *s, *m, + std::forward(args)...); + } +}; + +//------------------------------------------------------------------------------ + +template +class write_some_lambda +{ + Stream& stream_; + +public: + bool invoked = false; + std::size_t bytes_transferred = 0; + + explicit + write_some_lambda(Stream& stream) + : stream_(stream) + { + } + + template + void + operator()(error_code& ec, + ConstBufferSequence const& buffers) + { + invoked = true; + bytes_transferred = + stream_.write_some(buffers, ec); + } +}; + +template +class write_lambda +{ + Stream& stream_; + +public: + bool invoked = false; + std::size_t bytes_transferred = 0; + + explicit + write_lambda(Stream& stream) + : stream_(stream) + { + } + + template + void + operator()(error_code& ec, + ConstBufferSequence const& buffers) + { + invoked = true; + bytes_transferred = net::write( + stream_, buffers, ec); + } +}; + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +std::size_t +write_some_impl( + SyncWriteStream& stream, + serializer& sr, + error_code& ec) +{ + if(! sr.is_done()) + { + write_some_lambda f{stream}; + sr.next(ec, f); + if(ec) + return f.bytes_transferred; + if(f.invoked) + sr.consume(f.bytes_transferred); + return f.bytes_transferred; + } + ec = {}; + return 0; +} + +template< + class AsyncWriteStream, + bool isRequest, class Body, class Fields, + class WriteHandler> +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) +async_write_some_impl( + AsyncWriteStream& stream, + serializer& sr, + WriteHandler&& handler) +{ + return net::async_initiate< + WriteHandler, + void(error_code, std::size_t)>( + run_write_some_op{}, + handler, + &stream, + &sr); +} + +} // detail + +//------------------------------------------------------------------------------ + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +std::size_t +write_some( + SyncWriteStream& stream, + serializer& sr) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + error_code ec; + auto const bytes_transferred = + write_some(stream, sr, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +std::size_t +write_some( + SyncWriteStream& stream, + serializer& sr, + error_code& ec) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + return detail::write_some_impl(stream, sr, ec); +} + +template< + class AsyncWriteStream, + bool isRequest, class Body, class Fields, + class WriteHandler> +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) +async_write_some( + AsyncWriteStream& stream, + serializer& sr, + WriteHandler&& handler) +{ + static_assert(is_async_write_stream< + AsyncWriteStream>::value, + "AsyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + return detail::async_write_some_impl(stream, sr, + std::forward(handler)); +} + +//------------------------------------------------------------------------------ + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +std::size_t +write_header(SyncWriteStream& stream, + serializer& sr) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + error_code ec; + auto const bytes_transferred = + write_header(stream, sr, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +std::size_t +write_header( + SyncWriteStream& stream, + serializer& sr, + error_code& ec) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + sr.split(true); + std::size_t bytes_transferred = 0; + if(! sr.is_header_done()) + { + detail::write_lambda f{stream}; + do + { + sr.next(ec, f); + bytes_transferred += f.bytes_transferred; + if(ec) + return bytes_transferred; + BOOST_ASSERT(f.invoked); + sr.consume(f.bytes_transferred); + } + while(! sr.is_header_done()); + } + else + { + ec = {}; + } + return bytes_transferred; +} + +template< + class AsyncWriteStream, + bool isRequest, class Body, class Fields, + class WriteHandler> +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) +async_write_header( + AsyncWriteStream& stream, + serializer& sr, + WriteHandler&& handler) +{ + static_assert(is_async_write_stream< + AsyncWriteStream>::value, + "AsyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + sr.split(true); + return net::async_initiate< + WriteHandler, + void(error_code, std::size_t)>( + detail::run_write_op{}, + handler, + &stream, + detail::serializer_is_header_done{}, + &sr); +} + +//------------------------------------------------------------------------------ + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +std::size_t +write( + SyncWriteStream& stream, + serializer& sr) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + error_code ec; + auto const bytes_transferred = + write(stream, sr, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +std::size_t +write( + SyncWriteStream& stream, + serializer& sr, + error_code& ec) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + std::size_t bytes_transferred = 0; + sr.split(false); + for(;;) + { + bytes_transferred += + write_some(stream, sr, ec); + if(ec) + return bytes_transferred; + if(sr.is_done()) + break; + } + return bytes_transferred; +} + +template< + class AsyncWriteStream, + bool isRequest, class Body, class Fields, + class WriteHandler> +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) +async_write( + AsyncWriteStream& stream, + serializer& sr, + WriteHandler&& handler) +{ + static_assert(is_async_write_stream< + AsyncWriteStream>::value, + "AsyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + sr.split(false); + return net::async_initiate< + WriteHandler, + void(error_code, std::size_t)>( + detail::run_write_op{}, + handler, + &stream, + detail::serializer_is_done{}, + &sr); +} + +//------------------------------------------------------------------------------ + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +typename std::enable_if< + is_mutable_body_writer::value, + std::size_t>::type +write( + SyncWriteStream& stream, + message& msg) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + error_code ec; + auto const bytes_transferred = + write(stream, msg, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +typename std::enable_if< + ! is_mutable_body_writer::value, + std::size_t>::type +write( + SyncWriteStream& stream, + message const& msg) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + error_code ec; + auto const bytes_transferred = + write(stream, msg, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +typename std::enable_if< + is_mutable_body_writer::value, + std::size_t>::type +write( + SyncWriteStream& stream, + message& msg, + error_code& ec) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + serializer sr{msg}; + return write(stream, sr, ec); +} + +template< + class SyncWriteStream, + bool isRequest, class Body, class Fields> +typename std::enable_if< + ! is_mutable_body_writer::value, + std::size_t>::type +write( + SyncWriteStream& stream, + message const& msg, + error_code& ec) +{ + static_assert(is_sync_write_stream::value, + "SyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + serializer sr{msg}; + return write(stream, sr, ec); +} + +template< + class AsyncWriteStream, + bool isRequest, class Body, class Fields, + class WriteHandler> +typename std::enable_if< + is_mutable_body_writer::value, + BOOST_BEAST_ASYNC_RESULT2(WriteHandler)>::type +async_write( + AsyncWriteStream& stream, + message& msg, + WriteHandler&& handler) +{ + static_assert( + is_async_write_stream::value, + "AsyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + return net::async_initiate< + WriteHandler, + void(error_code, std::size_t)>( + detail::run_write_msg_op{}, + handler, + &stream, + &msg, + std::false_type{}); +} + +template< + class AsyncWriteStream, + bool isRequest, class Body, class Fields, + class WriteHandler> +typename std::enable_if< + ! is_mutable_body_writer::value, + BOOST_BEAST_ASYNC_RESULT2(WriteHandler)>::type +async_write( + AsyncWriteStream& stream, + message const& msg, + WriteHandler&& handler) +{ + static_assert( + is_async_write_stream::value, + "AsyncWriteStream type requirements not met"); + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + return net::async_initiate< + WriteHandler, + void(error_code, std::size_t)>( + detail::run_write_msg_op{}, + handler, + &stream, + &msg, + std::true_type{}); +} + +//------------------------------------------------------------------------------ + +namespace detail { + +template +class write_ostream_lambda +{ + std::ostream& os_; + Serializer& sr_; + +public: + write_ostream_lambda(std::ostream& os, + Serializer& sr) + : os_(os) + , sr_(sr) + { + } + + template + void + operator()(error_code& ec, + ConstBufferSequence const& buffers) const + { + ec = {}; + if(os_.fail()) + return; + std::size_t bytes_transferred = 0; + for(auto b : beast::buffers_range_ref(buffers)) + { + os_.write(static_cast( + b.data()), b.size()); + if(os_.fail()) + return; + bytes_transferred += b.size(); + } + sr_.consume(bytes_transferred); + } +}; + +} // detail + +template +std::ostream& +operator<<(std::ostream& os, + header const& h) +{ + typename Fields::writer fr{ + h, h.version(), h.method()}; + return os << buffers(fr.get()); +} + +template +std::ostream& +operator<<(std::ostream& os, + header const& h) +{ + typename Fields::writer fr{ + h, h.version(), h.result_int()}; + return os << buffers(fr.get()); +} + +template +std::ostream& +operator<<(std::ostream& os, + message const& msg) +{ + static_assert(is_body::value, + "Body type requirements not met"); + static_assert(is_body_writer::value, + "BodyWriter type requirements not met"); + serializer sr{msg}; + error_code ec; + detail::write_ostream_lambda f{os, sr}; + do + { + sr.next(ec, f); + if(os.fail()) + break; + if(ec) + { + os.setstate(std::ios::failbit); + break; + } + } + while(! sr.is_done()); + return os; +} + +} // http +} // beast +} // boost + +#endif diff --git a/boost/beast/http/impl/write.ipp b/boost/beast/http/impl/write.ipp deleted file mode 100644 index bbada07024..0000000000 --- a/boost/beast/http/impl/write.ipp +++ /dev/null @@ -1,1003 +0,0 @@ -// -// Copyright (c) 2016-2017 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_WRITE_IPP -#define BOOST_BEAST_HTTP_IMPL_WRITE_IPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace beast { -namespace http { -namespace detail { - -template< - class Stream, class Handler, - bool isRequest, class Body, class Fields> -class write_some_op -{ - Stream& s_; - boost::asio::executor_work_guard().get_executor())> wg_; - serializer& sr_; - Handler h_; - - class lambda - { - write_some_op& op_; - - public: - bool invoked = false; - - explicit - lambda(write_some_op& op) - : op_(op) - { - } - - template - void - operator()(error_code& ec, - ConstBufferSequence const& buffers) - { - invoked = true; - ec.assign(0, ec.category()); - return op_.s_.async_write_some( - buffers, std::move(op_)); - } - }; - -public: - write_some_op(write_some_op&&) = default; - write_some_op(write_some_op const&) = delete; - - template - write_some_op(DeducedHandler&& h, Stream& s, - serializer& sr) - : s_(s) - , wg_(s_.get_executor()) - , sr_(sr) - , h_(std::forward(h)) - { - } - - using allocator_type = - boost::asio::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (boost::asio::get_associated_allocator)(h_); - } - - using executor_type = boost::asio::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (boost::asio::get_associated_executor)( - h_, s_.get_executor()); - } - - void - operator()(); - - void - operator()( - error_code ec, - std::size_t bytes_transferred); - - friend - bool asio_handler_is_continuation(write_some_op* op) - { - using boost::asio::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - template - friend - void asio_handler_invoke(Function&& f, write_some_op* op) - { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } -}; - -template< - class Stream, class Handler, - bool isRequest, class Body, class Fields> -void -write_some_op< - Stream, Handler, isRequest, Body, Fields>:: -operator()() -{ - error_code ec; - if(! sr_.is_done()) - { - lambda f{*this}; - sr_.next(ec, f); - if(ec) - { - BOOST_ASSERT(! f.invoked); - return boost::asio::post( - s_.get_executor(), - bind_handler(std::move(*this), ec, 0)); - } - if(f.invoked) - { - // *this has been moved from, - // cannot access members here. - return; - } - // What else could it be? - BOOST_ASSERT(sr_.is_done()); - } - return boost::asio::post( - s_.get_executor(), - bind_handler(std::move(*this), ec, 0)); -} - -template< - class Stream, class Handler, - bool isRequest, class Body, class Fields> -void -write_some_op< - Stream, Handler, isRequest, Body, Fields>:: -operator()( - error_code ec, std::size_t bytes_transferred) -{ - if(! ec) - sr_.consume(bytes_transferred); - h_(ec, bytes_transferred); -} - -//------------------------------------------------------------------------------ - -struct serializer_is_header_done -{ - template< - bool isRequest, class Body, class Fields> - bool - operator()( - serializer& sr) const - { - return sr.is_header_done(); - } -}; - -struct serializer_is_done -{ - template< - bool isRequest, class Body, class Fields> - bool - operator()( - serializer& sr) const - { - return sr.is_done(); - } -}; - -//------------------------------------------------------------------------------ - -template< - class Stream, class Handler, class Predicate, - bool isRequest, class Body, class Fields> -class write_op : public boost::asio::coroutine -{ - Stream& s_; - boost::asio::executor_work_guard().get_executor())> wg_; - serializer& sr_; - std::size_t bytes_transferred_ = 0; - Handler h_; - bool cont_; - -public: - write_op(write_op&&) = default; - write_op(write_op const&) = delete; - - template - write_op(DeducedHandler&& h, Stream& s, - serializer& sr) - : s_(s) - , wg_(s_.get_executor()) - , sr_(sr) - , h_(std::forward(h)) - , cont_([&] - { - using boost::asio::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(h_)); - }()) - { - } - - using allocator_type = - boost::asio::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (boost::asio::get_associated_allocator)(h_); - } - - using executor_type = boost::asio::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (boost::asio::get_associated_executor)( - h_, s_.get_executor()); - } - - void - operator()( - error_code ec = {}, - std::size_t bytes_transferred = 0); - - friend - bool asio_handler_is_continuation(write_op* op) - { - return op->cont_; - } - - template - friend - void asio_handler_invoke(Function&& f, write_op* op) - { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } -}; - -template< - class Stream, class Handler, class Predicate, - bool isRequest, class Body, class Fields> -void -write_op:: -operator()( - error_code ec, - std::size_t bytes_transferred) -{ - BOOST_ASIO_CORO_REENTER(*this) - { - if(Predicate{}(sr_)) - { - BOOST_ASIO_CORO_YIELD - boost::asio::post( - s_.get_executor(), - bind_handler(std::move(*this))); - goto upcall; - } - for(;;) - { - BOOST_ASIO_CORO_YIELD - beast::http::async_write_some( - s_, sr_, std::move(*this)); - bytes_transferred_ += bytes_transferred; - if(ec) - goto upcall; - if(Predicate{}(sr_)) - break; - cont_ = true; - } - upcall: - h_(ec, bytes_transferred_); - } -} - -//------------------------------------------------------------------------------ - -template -class write_msg_op -{ - struct data - { - Stream& s; - boost::asio::executor_work_guard().get_executor())> wg; - serializer sr; - - data(Handler const&, Stream& s_, message< - isRequest, Body, Fields>& m_) - : s(s_) - , wg(s.get_executor()) - , sr(m_) - { - } - - data(Handler const&, Stream& s_, message< - isRequest, Body, Fields> const& m_) - : s(s_) - , wg(s.get_executor()) - , sr(m_) - { - } - }; - - handler_ptr d_; - -public: - write_msg_op(write_msg_op&&) = default; - write_msg_op(write_msg_op const&) = delete; - - template - write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args) - : d_(std::forward(h), - s, std::forward(args)...) - { - } - - using allocator_type = - boost::asio::associated_allocator_t; - - allocator_type - get_allocator() const noexcept - { - return (boost::asio::get_associated_allocator)(d_.handler()); - } - - using executor_type = boost::asio::associated_executor_t< - Handler, decltype(std::declval().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (boost::asio::get_associated_executor)( - d_.handler(), d_->s.get_executor()); - } - - void - operator()(); - - void - operator()( - error_code ec, std::size_t bytes_transferred); - - friend - bool asio_handler_is_continuation(write_msg_op* op) - { - using boost::asio::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->d_.handler())); - } - - template - friend - void asio_handler_invoke(Function&& f, write_msg_op* op) - { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->d_.handler())); - } -}; - -template -void -write_msg_op< - Stream, Handler, isRequest, Body, Fields>:: -operator()() -{ - auto& d = *d_; - return async_write(d.s, d.sr, std::move(*this)); -} - -template -void -write_msg_op< - Stream, Handler, isRequest, Body, Fields>:: -operator()(error_code ec, std::size_t bytes_transferred) -{ - auto wg = std::move(d_->wg); - d_.invoke(ec, bytes_transferred); -} - -//------------------------------------------------------------------------------ - -template -class write_some_lambda -{ - Stream& stream_; - -public: - bool invoked = false; - std::size_t bytes_transferred = 0; - - explicit - write_some_lambda(Stream& stream) - : stream_(stream) - { - } - - template - void - operator()(error_code& ec, - ConstBufferSequence const& buffers) - { - invoked = true; - bytes_transferred = - stream_.write_some(buffers, ec); - } -}; - -template -class write_lambda -{ - Stream& stream_; - -public: - bool invoked = false; - std::size_t bytes_transferred = 0; - - explicit - write_lambda(Stream& stream) - : stream_(stream) - { - } - - template - void - operator()(error_code& ec, - ConstBufferSequence const& buffers) - { - invoked = true; - bytes_transferred = boost::asio::write( - stream_, buffers, ec); - } -}; - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -std::size_t -write_some_impl( - SyncWriteStream& stream, - serializer& sr, - error_code& ec) -{ - if(! sr.is_done()) - { - write_some_lambda f{stream}; - sr.next(ec, f); - if(ec) - return f.bytes_transferred; - if(f.invoked) - sr.consume(f.bytes_transferred); - return f.bytes_transferred; - } - ec.assign(0, ec.category()); - return 0; -} - -template< - class AsyncWriteStream, - bool isRequest, class Body, class Fields, - class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) -async_write_some_impl( - AsyncWriteStream& stream, - serializer& sr, - WriteHandler&& handler) -{ - BOOST_BEAST_HANDLER_INIT( - WriteHandler, void(error_code, std::size_t)); - detail::write_some_op< - AsyncWriteStream, - BOOST_ASIO_HANDLER_TYPE(WriteHandler, - void(error_code, std::size_t)), - isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, sr}(); - return init.result.get(); -} - -} // detail - -//------------------------------------------------------------------------------ - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -std::size_t -write_some( - SyncWriteStream& stream, - serializer& sr) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - error_code ec; - auto const bytes_transferred = - write_some(stream, sr, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -std::size_t -write_some( - SyncWriteStream& stream, - serializer& sr, - error_code& ec) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - return detail::write_some_impl(stream, sr, ec); -} - -template< - class AsyncWriteStream, - bool isRequest, class Body, class Fields, - class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) -async_write_some( - AsyncWriteStream& stream, - serializer& sr, - WriteHandler&& handler) -{ - static_assert(is_async_write_stream< - AsyncWriteStream>::value, - "AsyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - return detail::async_write_some_impl(stream, sr, - std::forward(handler)); -} - -//------------------------------------------------------------------------------ - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -std::size_t -write_header(SyncWriteStream& stream, - serializer& sr) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - error_code ec; - auto const bytes_transferred = - write_header(stream, sr, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -std::size_t -write_header( - SyncWriteStream& stream, - serializer& sr, - error_code& ec) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - sr.split(true); - std::size_t bytes_transferred = 0; - if(! sr.is_header_done()) - { - detail::write_lambda f{stream}; - do - { - sr.next(ec, f); - bytes_transferred += f.bytes_transferred; - if(ec) - return bytes_transferred; - BOOST_ASSERT(f.invoked); - sr.consume(f.bytes_transferred); - } - while(! sr.is_header_done()); - } - else - { - ec.assign(0, ec.category()); - } - return bytes_transferred; -} - -template< - class AsyncWriteStream, - bool isRequest, class Body, class Fields, - class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) -async_write_header( - AsyncWriteStream& stream, - serializer& sr, - WriteHandler&& handler) -{ - static_assert(is_async_write_stream< - AsyncWriteStream>::value, - "AsyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - sr.split(true); - BOOST_BEAST_HANDLER_INIT( - WriteHandler, void(error_code, std::size_t)); - detail::write_op< - AsyncWriteStream, - BOOST_ASIO_HANDLER_TYPE(WriteHandler, - void(error_code, std::size_t)), - detail::serializer_is_header_done, - isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, sr}(); - return init.result.get(); -} - -//------------------------------------------------------------------------------ - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -std::size_t -write( - SyncWriteStream& stream, - serializer& sr) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - error_code ec; - auto const bytes_transferred = - write(stream, sr, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -std::size_t -write( - SyncWriteStream& stream, - serializer& sr, - error_code& ec) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - std::size_t bytes_transferred = 0; - sr.split(false); - for(;;) - { - bytes_transferred += - write_some(stream, sr, ec); - if(ec) - return bytes_transferred; - if(sr.is_done()) - break; - } - return bytes_transferred; -} - -template< - class AsyncWriteStream, - bool isRequest, class Body, class Fields, - class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) -async_write( - AsyncWriteStream& stream, - serializer& sr, - WriteHandler&& handler) -{ - static_assert(is_async_write_stream< - AsyncWriteStream>::value, - "AsyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - sr.split(false); - BOOST_BEAST_HANDLER_INIT( - WriteHandler, void(error_code, std::size_t)); - detail::write_op< - AsyncWriteStream, - BOOST_ASIO_HANDLER_TYPE(WriteHandler, - void(error_code, std::size_t)), - detail::serializer_is_done, - isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, sr}(); - return init.result.get(); -} - -//------------------------------------------------------------------------------ - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -typename std::enable_if< - is_mutable_body_writer::value, - std::size_t>::type -write( - SyncWriteStream& stream, - message& msg) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - error_code ec; - auto const bytes_transferred = - write(stream, msg, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -typename std::enable_if< - ! is_mutable_body_writer::value, - std::size_t>::type -write( - SyncWriteStream& stream, - message const& msg) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - error_code ec; - auto const bytes_transferred = - write(stream, msg, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return bytes_transferred; -} - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -typename std::enable_if< - is_mutable_body_writer::value, - std::size_t>::type -write( - SyncWriteStream& stream, - message& msg, - error_code& ec) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - serializer sr{msg}; - return write(stream, sr, ec); -} - -template< - class SyncWriteStream, - bool isRequest, class Body, class Fields> -typename std::enable_if< - ! is_mutable_body_writer::value, - std::size_t>::type -write( - SyncWriteStream& stream, - message const& msg, - error_code& ec) -{ - static_assert(is_sync_write_stream::value, - "SyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - serializer sr{msg}; - return write(stream, sr, ec); -} - -template< - class AsyncWriteStream, - bool isRequest, class Body, class Fields, - class WriteHandler> -typename std::enable_if< - is_mutable_body_writer::value, - BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t))>::type -async_write( - AsyncWriteStream& stream, - message& msg, - WriteHandler&& handler) -{ - static_assert( - is_async_write_stream::value, - "AsyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - BOOST_BEAST_HANDLER_INIT( - WriteHandler, void(error_code, std::size_t)); - detail::write_msg_op< - AsyncWriteStream, - BOOST_ASIO_HANDLER_TYPE(WriteHandler, - void(error_code, std::size_t)), - isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, msg}(); - return init.result.get(); -} - -template< - class AsyncWriteStream, - bool isRequest, class Body, class Fields, - class WriteHandler> -typename std::enable_if< - ! is_mutable_body_writer::value, - BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t))>::type -async_write( - AsyncWriteStream& stream, - message const& msg, - WriteHandler&& handler) -{ - static_assert( - is_async_write_stream::value, - "AsyncWriteStream requirements not met"); - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - BOOST_BEAST_HANDLER_INIT( - WriteHandler, void(error_code, std::size_t)); - detail::write_msg_op< - AsyncWriteStream, - BOOST_ASIO_HANDLER_TYPE(WriteHandler, - void(error_code, std::size_t)), - isRequest, Body, Fields>{ - std::move(init.completion_handler), stream, msg}(); - return init.result.get(); -} - -//------------------------------------------------------------------------------ - -namespace detail { - -template -class write_ostream_lambda -{ - std::ostream& os_; - Serializer& sr_; - -public: - write_ostream_lambda(std::ostream& os, - Serializer& sr) - : os_(os) - , sr_(sr) - { - } - - template - void - operator()(error_code& ec, - ConstBufferSequence const& buffers) const - { - ec.assign(0, ec.category()); - if(os_.fail()) - return; - std::size_t bytes_transferred = 0; - for(auto b : buffers_range(buffers)) - { - os_.write(static_cast( - b.data()), b.size()); - if(os_.fail()) - return; - bytes_transferred += b.size(); - } - sr_.consume(bytes_transferred); - } -}; - -} // detail - -template -std::ostream& -operator<<(std::ostream& os, - header const& h) -{ - typename Fields::writer fr{ - h, h.version(), h.method()}; - return os << buffers(fr.get()); -} - -template -std::ostream& -operator<<(std::ostream& os, - header const& h) -{ - typename Fields::writer fr{ - h, h.version(), h.result_int()}; - return os << buffers(fr.get()); -} - -template -std::ostream& -operator<<(std::ostream& os, - message const& msg) -{ - static_assert(is_body::value, - "Body requirements not met"); - static_assert(is_body_writer::value, - "BodyWriter requirements not met"); - serializer sr{msg}; - error_code ec; - detail::write_ostream_lambda f{os, sr}; - do - { - sr.next(ec, f); - if(os.fail()) - break; - if(ec) - { - os.setstate(std::ios::failbit); - break; - } - } - while(! sr.is_done()); - return os; -} - -} // http -} // beast -} // boost - -#endif diff --git a/boost/beast/http/message.hpp b/boost/beast/http/message.hpp index bd85d160f1..c21e3685f0 100644 --- a/boost/beast/http/message.hpp +++ b/boost/beast/http/message.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -16,8 +16,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -47,18 +47,19 @@ namespace http { */ #if BOOST_BEAST_DOXYGEN template -struct header : Fields +class header : public Fields #else template -struct header; +class header; template -struct header : Fields +class header : public Fields #endif { +public: static_assert(is_fields::value, - "Fields requirements not met"); + "Fields type requirements not met"); /// Indicates if the header is a request or response. #if BOOST_BEAST_DOXYGEN @@ -128,7 +129,7 @@ struct header : Fields @note This function is only available when `isRequest == true`. - @see @ref method_string + @see method_string */ verb method() const; @@ -151,7 +152,7 @@ struct header : Fields @note This function is only available when `isRequest == true`. - @see @ref method + @see method */ string_view method_string() const; @@ -222,14 +223,14 @@ struct header : Fields ! std::is_convertible::type, verb>::value && ! std::is_convertible::type, header>::value + std::decay::type, status>::value >::type> explicit header(Arg1&& arg1, ArgN&&... argn); private: template - friend struct message; + friend class message; template friend @@ -258,10 +259,11 @@ private: A `header` includes the start-line and header-fields. */ template -struct header : Fields +class header : public Fields { +public: static_assert(is_fields::value, - "Fields requirements not met"); + "Fields type requirements not met"); /// Indicates if the header is a request or response. using is_request = std::false_type; @@ -297,9 +299,11 @@ struct header : Fields template::type, status>::value && + std::decay::type, header>::value && + ! std::is_convertible::type, verb>::value && ! std::is_convertible::type, header>::value + std::decay::type, status>::value >::type> explicit header(Arg1&& arg1, ArgN&&... argn); @@ -418,7 +422,7 @@ struct header : Fields private: #if ! BOOST_BEAST_DOXYGEN template - friend struct message; + friend class message; template friend @@ -487,13 +491,14 @@ using value_type_t = typename T::value_type; field value pairs. */ template -struct message - : header +class message + : public header #if ! BOOST_BEAST_DOXYGEN , boost::empty_value< typename Body::value_type> #endif { +public: /// The base class used to hold the header portion of the message. using header_type = header; @@ -542,11 +547,11 @@ struct message /** Constructor - @param method The request-method to use + @param method The request-method to use. @param target The request-target. - @param version The HTTP-version + @param version The HTTP-version. @note This function is only available when `isRequest == true`. */ @@ -561,11 +566,11 @@ struct message /** Constructor - @param method The request-method to use + @param method The request-method to use. @param target The request-target. - @param version The HTTP-version + @param version The HTTP-version. @param body_arg An argument forwarded to the `body` constructor. @@ -585,11 +590,11 @@ struct message /** Constructor - @param method The request-method to use + @param method The request-method to use. @param target The request-target. - @param version The HTTP-version + @param version The HTTP-version. @param body_arg An argument forwarded to the `body` constructor. @@ -611,9 +616,9 @@ struct message /** Constructor - @param result The status-code for the response + @param result The status-code for the response. - @param version The HTTP-version + @param version The HTTP-version. @note This member is only available when `isRequest == false`. */ @@ -628,9 +633,9 @@ struct message /** Constructor - @param result The status-code for the response + @param result The status-code for the response. - @param version The HTTP-version + @param version The HTTP-version. @param body_arg An argument forwarded to the `body` constructor. @@ -648,9 +653,9 @@ struct message /** Constructor - @param result The status-code for the response + @param result The status-code for the response. - @param version The HTTP-version + @param version The HTTP-version. @param body_arg An argument forwarded to the `body` constructor. @@ -722,7 +727,7 @@ struct message /** Set or clear the chunked Transfer-Encoding - This function will set or removed the "chunked" transfer + This function will set or remove the "chunked" transfer encoding as the last item in the list of encodings in the field. @@ -804,7 +809,7 @@ struct message @li @ref chunked would return `true` @li @ref result returns @ref status::no_content - + @li @ref result returns @ref status::not_modified @li @ref result returns any informational status class (100 to 199) @@ -821,7 +826,7 @@ struct message /** Returns the payload size of the body in octets if possible. - This function invokes the @b Body algorithm to measure + This function invokes the Body algorithm to measure the number of octets in the serialized body container. If there is no body, this will return zero. Otherwise, if the body exists but is not known ahead of time, `boost::none` @@ -893,7 +898,7 @@ struct message private: static_assert(is_body::value, - "Body requirements not met"); + "Body type requirements not met"); template< class... BodyArgs, @@ -901,7 +906,7 @@ private: message( std::piecewise_construct_t, std::tuple& body_args, - beast::detail::index_sequence) + mp11::index_sequence) : boost::empty_value< typename Body::value_type>(boost::empty_init_t(), std::forward( @@ -919,8 +924,8 @@ private: std::piecewise_construct_t, std::tuple& body_args, std::tuple& fields_args, - beast::detail::index_sequence, - beast::detail::index_sequence) + mp11::index_sequence, + mp11::index_sequence) : header_type(std::forward( std::get(fields_args))...) , boost::empty_value< @@ -998,6 +1003,6 @@ swap( } // beast } // boost -#include +#include #endif diff --git a/boost/beast/http/parser.hpp b/boost/beast/http/parser.hpp index fb7b74f240..9b0523f7e3 100644 --- a/boost/beast/http/parser.hpp +++ b/boost/beast/http/parser.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -35,7 +35,7 @@ namespace http { will be parsed. @tparam Body The type used to represent the body. This must - meet the requirements of @b Body. + meet the requirements of Body. @tparam Allocator The type of allocator used with the @ref basic_fields container. @@ -47,24 +47,21 @@ template< class Body, class Allocator = std::allocator> class parser - : public basic_parser> + : public basic_parser { static_assert(is_body::value, - "Body requirements not met"); + "Body type requirements not met"); static_assert(is_body_reader::value, - "BodyReader requirements not met"); + "BodyReader type requirements not met"); template friend class parser; - using base_type = basic_parser>; - message> m_; typename Body::reader rd_; bool rd_inited_ = false; + bool used_ = false; std::functionBodyReader, otherwise an exception is generated. @par Example @code @@ -297,8 +294,6 @@ public: } private: - friend class basic_parser; - parser(std::true_type); parser(std::false_type); @@ -338,42 +333,95 @@ private: string_view method_str, string_view target, int version, - error_code& ec) + error_code& ec, + std::true_type) { - try - { - m_.target(target); - if(method != verb::unknown) - m_.method(method); - else - m_.method_string(method_str); - ec.assign(0, ec.category()); - } - catch(std::bad_alloc const&) + // If this assert goes off, it means you tried to re-use a + // parser after it was done reading a message. This is not + // allowed, you need to create a new parser for each message. + // The easiest way to do that is to store the parser in + // an optional object. + + BOOST_ASSERT(! used_); + if(used_) { - ec = error::bad_alloc; + ec = error::stale_parser; + return; } + used_ = true; + + m_.target(target); + if(method != verb::unknown) + m_.method(method); + else + m_.method_string(method_str); m_.version(version); } + void + on_request_impl( + verb, string_view, string_view, + int, error_code&, std::false_type) + { + } + + void + on_request_impl( + verb method, + string_view method_str, + string_view target, + int version, + error_code& ec) override + { + this->on_request_impl( + method, method_str, target, version, ec, + std::integral_constant{}); + } + void on_response_impl( int code, string_view reason, int version, - error_code& ec) + error_code& ec, + std::true_type) { - m_.result(code); - m_.version(version); - try + // If this assert goes off, it means you tried to re-use a + // parser after it was done reading a message. This is not + // allowed, you need to create a new parser for each message. + // The easiest way to do that is to store the parser in + // an optional object. + + BOOST_ASSERT(! used_); + if(used_) { - m_.reason(reason); - ec.assign(0, ec.category()); - } - catch(std::bad_alloc const&) - { - ec = error::bad_alloc; + ec = error::stale_parser; + return; } + used_ = true; + + m_.result(code); + m_.version(version); + m_.reason(reason); + } + + void + on_response_impl( + int, string_view, int, + error_code&, std::false_type) + { + } + + void + on_response_impl( + int code, + string_view reason, + int version, + error_code& ec) override + { + this->on_response_impl( + code, reason, version, ec, + std::integral_constant{}); } void @@ -381,29 +429,21 @@ private: field name, string_view name_string, string_view value, - error_code& ec) + error_code&) override { - try - { - m_.insert(name, name_string, value); - ec.assign(0, ec.category()); - } - catch(std::bad_alloc const&) - { - ec = error::bad_alloc; - } + m_.insert(name, name_string, value); } void - on_header_impl(error_code& ec) + on_header_impl(error_code& ec) override { - ec.assign(0, ec.category()); + ec = {}; } void on_body_init_impl( boost::optional const& content_length, - error_code& ec) + error_code& ec) override { rd_.init(content_length, ec); rd_inited_ = true; @@ -412,9 +452,9 @@ private: std::size_t on_body_impl( string_view body, - error_code& ec) + error_code& ec) override { - return rd_.put(boost::asio::buffer( + return rd_.put(net::buffer( body.data(), body.size()), ec); } @@ -422,27 +462,27 @@ 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.assign(0, ec.category()); } 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); - return rd_.put(boost::asio::buffer( + return rd_.put(net::buffer( body.data(), body.size()), ec); } void - on_finish_impl(error_code& ec) + on_finish_impl( + error_code& ec) override { rd_.finish(ec); } @@ -460,6 +500,6 @@ using response_parser = parser; } // beast } // boost -#include +#include #endif diff --git a/boost/beast/http/read.hpp b/boost/beast/http/read.hpp index a4f0efe400..229444be8f 100644 --- a/boost/beast/http/read.hpp +++ b/boost/beast/http/read.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -20,604 +20,635 @@ namespace boost { namespace beast { namespace http { +//------------------------------------------------------------------------------ + /** Read part of a message from a stream using a parser. - This function is used to read part of a message from a stream into a - subclass of @ref basic_parser. - The call will block until one of the following conditions is true: + This function is used to read part of a message from a stream into an + instance of @ref basic_parser. The call will block until one of the + following conditions is true: @li A call to @ref basic_parser::put with a non-empty buffer sequence - is successful. + is successful. @li An error occurs. - This operation is implemented in terms of one or - more calls to the stream's `read_some` function. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of one or more calls to the stream's + `read_some` function. The implementation may read additional bytes from + the stream that lie past the end of the message being read. These additional + bytes are stored in the dynamic buffer, which must be preserved for + subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b SyncReadStream concept. + @param stream The stream from which the data is to be read. The type must + meet the SyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. @param parser The parser to use. - @return The number of bytes transferred to the parser. + @return The number of bytes transferred from the stream. @throws system_error Thrown on failure. + + @note The function returns the total number of bytes transferred from the + stream. This may be zero for the case where there is sufficient pre-existing + message data in the dynamic buffer. */ template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_some( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser); + basic_parser& parser); /** Read part of a message from a stream using a parser. - This function is used to read part of a message from a stream into a - subclass of @ref basic_parser. - The call will block until one of the following conditions is true: + This function is used to read part of a message from a stream into an + instance of @ref basic_parser. The call will block until one of the + following conditions is true: @li A call to @ref basic_parser::put with a non-empty buffer sequence - is successful. + is successful. @li An error occurs. - This operation is implemented in terms of one or - more calls to the stream's `read_some` function. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. - - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + This operation is implemented in terms of one or more calls to the stream's + `read_some` function. The implementation may read additional bytes from + the stream that lie past the end of the message being read. These additional + bytes are stored in the dynamic buffer, which must be preserved for + subsequent reads. - @li @ref error::end_of_stream if no octets were parsed, or + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::end_of_stream if no bytes were parsed, or - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - The function returns the number of bytes processed from the dynamic - buffer. The caller should remove these bytes by calling `consume` on - the dynamic buffer, regardless of any error. + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b SyncReadStream concept. + @param stream The stream from which the data is to be read. The type must + support the SyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. @param parser The parser to use. @param ec Set to the error, if any occurred. - @return The number of bytes transferred to the parser. + @return The number of bytes transferred from the stream. + + @note The function returns the total number of bytes transferred from the + stream. This may be zero for the case where there is sufficient pre-existing + message data in the dynamic buffer. */ template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_some( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec); /** Read part of a message asynchronously from a stream using a parser. This function is used to asynchronously read part of a message from - a stream into a subclass of @ref basic_parser. - The function call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: + a stream into an instance of @ref basic_parser. The function call + always returns immediately. The asynchronous operation will continue + until one of the following conditions is true: @li A call to @ref basic_parser::put with a non-empty buffer sequence - is successful. + is successful. @li An error occurs. - This operation is implemented in terms of zero or more calls to - the next layer's `async_read_some` function, and is known as a - composed operation. The program must ensure that the - stream performs no other reads until this operation completes. - The implementation may read additional octets that lie past the - end of the object being parsed. This additional data is stored - in the stream buffer, which may be used in subsequent calls. + This operation is implemented in terms of zero or more calls to the + next layer's `async_read_some` function, and is known as a composed + operation. The program must ensure that the stream performs no other + reads until this operation completes. The implementation may read additional + bytes from the stream that lie past the end of the message being read. + These additional bytes are stored in the dynamic buffer, which must be + preserved for subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b AsyncReadStream concept. + @param stream The stream from which the data is to be read. The type + must meet the AsyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. The object must remain valid at least until the handler + is called; ownership is not transferred. - @param parser The parser to use. - The object must remain valid at least until the - handler is called; ownership is not transferred. + @param parser The parser to use. The object must remain valid at least until + the handler is called; ownership is not transferred. - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( error_code const& error, // result of operation - std::size_t bytes_transferred // the number of bytes transferred to the parser - ); @endcode + std::size_t bytes_transferred // the total number of bytes transferred from the stream + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. - The completion handler will receive as a parameter the number - of octets processed from the dynamic buffer. The octets should - be removed by calling `consume` on the dynamic buffer after - the read completes, regardless of any error. + @note The completion handler will receive as a parameter the total number + of bytes transferred from the stream. This may be zero for the case where + there is sufficient pre-existing message data in the dynamic buffer. */ 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)) +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) async_read_some( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler); //------------------------------------------------------------------------------ -/** Read a header from a stream using a parser. +/** Read a complete message header from a stream using a parser. - This function is used to read a header from a stream into a subclass - of @ref basic_parser. - The call will block until one of the following conditions is true: + This function is used to read a complete message header from a stream + into an instance of @ref basic_parser. The call will block until one of the + following conditions is true: @li @ref basic_parser::is_header_done returns `true` @li An error occurs. - This operation is implemented in terms of one or - more calls to the stream's `read_some` function. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of one or more calls to the stream's + `read_some` function. The implementation may read additional bytes from + the stream that lie past the end of the message being read. These additional + bytes are stored in the dynamic buffer, which must be preserved for + subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b SyncReadStream concept. + @param stream The stream from which the data is to be read. The type must + meet the SyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. @param parser The parser to use. - @return The number of bytes transferred to the parser. + @return The number of bytes transferred from the stream. @throws system_error Thrown on failure. - @note The implementation will call @ref basic_parser::eager - with the value `false` on the parser passed in. + @note The function returns the total number of bytes transferred from the + stream. This may be zero for the case where there is sufficient pre-existing + message data in the dynamic buffer. The implementation will call + @ref basic_parser::eager with the value `false` on the parser passed in. */ template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_header( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser); + basic_parser& parser); -/** Read a header from a stream using a parser. +/** Read a complete message header from a stream using a parser. - This function is used to read a header from a stream into a subclass - of @ref basic_parser. - The call will block until one of the following conditions is true: + This function is used to read a complete message header from a stream + into an instance of @ref basic_parser. The call will block until one of the + following conditions is true: @li @ref basic_parser::is_header_done returns `true` @li An error occurs. - This operation is implemented in terms of one or - more calls to the stream's `read_some` function. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of one or more calls to the stream's + `read_some` function. The implementation may read additional bytes from + the stream that lie past the end of the message being read. These additional + bytes are stored in the dynamic buffer, which must be preserved for + subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b SyncReadStream concept. + @param stream The stream from which the data is to be read. The type must + meet the SyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. @param parser The parser to use. @param ec Set to the error, if any occurred. - @return The number of bytes transferred to the parser. + @return The number of bytes transferred from the stream. - @note The implementation will call @ref basic_parser::eager - with the value `false` on the parser passed in. + @note The function returns the total number of bytes transferred from the + stream. This may be zero for the case where there is sufficient pre-existing + message data in the dynamic buffer. The implementation will call + @ref basic_parser::eager with the value `false` on the parser passed in. */ template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read_header( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec); -/** Read a header from a stream asynchronously using a parser. +/** Read a complete message header asynchronously from a stream using a parser. - This function is used to asynchronously read a header from a stream - into a subclass of @ref basic_parser. - The function call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: + This function is used to asynchronously read a complete message header from + a stream into an instance of @ref basic_parser. The function call always + returns immediately. The asynchronous operation will continue until one of + the following conditions is true: @li @ref basic_parser::is_header_done returns `true` @li An error occurs. - This operation is implemented in terms of one or more calls to - the stream's `async_read_some` function, and is known as a - composed operation. The program must ensure that the - stream performs no other reads until this operation completes. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of zero or more calls to the + next layer's `async_read_some` function, and is known as a composed + operation. The program must ensure that the stream performs no other + reads until this operation completes. The implementation may read additional + bytes from the stream that lie past the end of the message being read. + These additional bytes are stored in the dynamic buffer, which must be + preserved for subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b AsyncReadStream concept. + @param stream The stream from which the data is to be read. The type + must meet the AsyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. The object must remain valid at least until the handler + is called; ownership is not transferred. - @param parser The parser to use. - The object must remain valid at least until the - handler is called; ownership is not transferred. - - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( - error_code const& error, // result of operation, - std::size_t bytes_transferred // the number of bytes transferred to the parser - ); @endcode + @param parser The parser to use. The object must remain valid at least until + the handler is called; ownership is not transferred. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + error_code const& error, // result of operation + std::size_t bytes_transferred // the total number of bytes transferred from the stream + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. - @note The implementation will call @ref basic_parser::eager - with the value `false` on the parser passed in. + @note The completion handler will receive as a parameter the total number + of bytes transferred from the stream. This may be zero for the case where + there is sufficient pre-existing message data in the dynamic buffer. The + implementation will call @ref basic_parser::eager with the value `false` + on the parser passed in. */ 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)) +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) async_read_header( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler); //------------------------------------------------------------------------------ /** Read a complete message from a stream using a parser. - This function is used to read a complete message from a stream into a - subclass of @ref basic_parser. - The call will block until one of the following conditions is true: + This function is used to read a complete message from a stream into an + instance of @ref basic_parser. The call will block until one of the + following conditions is true: @li @ref basic_parser::is_done returns `true` @li An error occurs. - This operation is implemented in terms of one or - more calls to the stream's `read_some` function. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of one or more calls to the stream's + `read_some` function. The implementation may read additional bytes from + the stream that lie past the end of the message being read. These additional + bytes are stored in the dynamic buffer, which must be preserved for + subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b SyncReadStream concept. + @param stream The stream from which the data is to be read. The type must + meet the SyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. @param parser The parser to use. - @return The number of bytes transferred to the parser. + @return The number of bytes transferred from the stream. @throws system_error Thrown on failure. - @note The implementation will call @ref basic_parser::eager - with the value `true` on the parser passed in. + @note The function returns the total number of bytes transferred from the + stream. This may be zero for the case where there is sufficient pre-existing + message data in the dynamic buffer. The implementation will call + @ref basic_parser::eager with the value `true` on the parser passed in. */ template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser); + basic_parser& parser); /** Read a complete message from a stream using a parser. - This function is used to read a complete message from a stream into a - subclass of @ref basic_parser. - The call will block until one of the following conditions is true: + This function is used to read a complete message from a stream into an + instance of @ref basic_parser. The call will block until one of the + following conditions is true: @li @ref basic_parser::is_done returns `true` @li An error occurs. - This operation is implemented in terms of one or - more calls to the stream's `read_some` function. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of one or more calls to the stream's + `read_some` function. The implementation may read additional bytes from + the stream that lie past the end of the message being read. These additional + bytes are stored in the dynamic buffer, which must be preserved for + subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b SyncReadStream concept. + @param stream The stream from which the data is to be read. The type must + meet the SyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. @param parser The parser to use. @param ec Set to the error, if any occurred. - @return The number of bytes transferred to the parser. + @return The number of bytes transferred from the stream. - @note The implementation will call @ref basic_parser::eager - with the value `true` on the parser passed in. + @note The function returns the total number of bytes transferred from the + stream. This may be zero for the case where there is sufficient pre-existing + message data in the dynamic buffer. The implementation will call + @ref basic_parser::eager with the value `true` on the parser passed in. */ template< class SyncReadStream, class DynamicBuffer, - bool isRequest, class Derived> + bool isRequest> std::size_t read( SyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, error_code& ec); -/** Read a complete message from a stream asynchronously using a parser. +/** Read a complete message asynchronously from a stream using a parser. This function is used to asynchronously read a complete message from a - stream into a subclass of @ref basic_parser. - The function call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: + stream into an instance of @ref basic_parser. The function call always + returns immediately. The asynchronous operation will continue until one + of the following conditions is true: @li @ref basic_parser::is_done returns `true` @li An error occurs. - This operation is implemented in terms of one or more calls to - the stream's `async_read_some` function, and is known as a - composed operation. The program must ensure that the - stream performs no other reads until this operation completes. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of zero or more calls to the + next layer's `async_read_some` function, and is known as a composed + operation. The program must ensure that the stream performs no other + reads until this operation completes. The implementation may read additional + bytes from the stream that lie past the end of the message being read. + These additional bytes are stored in the dynamic buffer, which must be + preserved for subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b AsyncReadStream concept. + @param stream The stream from which the data is to be read. The type + must meet the AsyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. The object must remain valid at least until the handler + is called; ownership is not transferred. - @param parser The parser to use. - The object must remain valid at least until the - handler is called; ownership is not transferred. - - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( - error_code const& error, // result of operation, - std::size_t bytes_transferred // the number of bytes transferred to the parser - ); @endcode + @param parser The parser to use. The object must remain valid at least until + the handler is called; ownership is not transferred. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + error_code const& error, // result of operation + std::size_t bytes_transferred // the total number of bytes transferred from the stream + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. - @note The implementation will call @ref basic_parser::eager - with the value `true` on the parser passed in. + @note The completion handler will receive as a parameter the total number + of bytes transferred from the stream. This may be zero for the case where + there is sufficient pre-existing message data in the dynamic buffer. The + implementation will call @ref basic_parser::eager with the value `true` + on the parser passed in. */ 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)) +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) async_read( AsyncReadStream& stream, DynamicBuffer& buffer, - basic_parser& parser, + basic_parser& parser, ReadHandler&& handler); //------------------------------------------------------------------------------ /** Read a complete message from a stream. - This function is used to read a complete message from a stream using HTTP/1. - The call will block until one of the following conditions is true: + This function is used to read a complete message from a stream into an + instance of @ref message. The call will block until one of the following + conditions is true: - @li The entire message is read. + @li The entire message is read in. @li An error occurs. - This operation is implemented in terms of one or - more calls to the stream's `read_some` function. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of one or more calls to the stream's + `read_some` function. The implementation may read additional bytes from + the stream that lie past the end of the message being read. These additional + bytes are stored in the dynamic buffer, which must be preserved for + subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b SyncReadStream concept. + @param stream The stream from which the data is to be read. The type must + meet the SyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. - @param msg An object in which to store the message contents. - This object should not have previous contents, otherwise - the behavior is undefined. - The type must be @b MoveAssignable and @b MoveConstructible. + @param msg The container in which to store the message contents. This + message container should not have previous contents, otherwise the behavior + is undefined. The type must be meet the MoveAssignable and + MoveConstructible requirements. - @return The number of bytes transferred to the parser. + @return The number of bytes transferred from the stream. @throws system_error Thrown on failure. + + @note The function returns the total number of bytes transferred from the + stream. This may be zero for the case where there is sufficient pre-existing + message data in the dynamic buffer. The implementation will call + @ref basic_parser::eager with the value `true` on the parser passed in. */ template< class SyncReadStream, @@ -631,47 +662,53 @@ read( /** Read a complete message from a stream. - This function is used to read a complete message from a stream using HTTP/1. - The call will block until one of the following conditions is true: + This function is used to read a complete message from a stream into an + instance of @ref message. The call will block until one of the following + conditions is true: - @li The entire message is read. + @li The entire message is read in. @li An error occurs. - This operation is implemented in terms of one or - more calls to the stream's `read_some` function. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. + This operation is implemented in terms of one or more calls to the stream's + `read_some` function. The implementation may read additional bytes from + the stream that lie past the end of the message being read. These additional + bytes are stored in the dynamic buffer, which must be preserved for + subsequent reads. - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: + If the end of file error is received while reading from the stream, then + the error returned from this function will be: - @li @ref error::end_of_stream if no octets were parsed, or + @li @ref error::end_of_stream if no bytes were parsed, or - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream + @li A successful result. The next attempt to read will return + @ref error::end_of_stream - @param stream The stream from which the data is to be read. - The type must support the @b SyncReadStream concept. + @param stream The stream from which the data is to be read. The type must + meet the SyncReadStream requirements. - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. - @param msg An object in which to store the message contents. - This object should not have previous contents, otherwise - the behavior is undefined. - The type must be @b MoveAssignable and @b MoveConstructible. + @param msg The container in which to store the message contents. This + message container should not have previous contents, otherwise the behavior + is undefined. The type must be meet the MoveAssignable and + MoveConstructible requirements. @param ec Set to the error, if any occurred. - @return The number of bytes transferred to the parser. + @return The number of bytes transferred from the stream. + + @note The function returns the total number of bytes transferred from the + stream. This may be zero for the case where there is sufficient pre-existing + message data in the dynamic buffer. The implementation will call + @ref basic_parser::eager with the value `true` on the parser passed in. */ template< class SyncReadStream, @@ -684,72 +721,79 @@ read( message>& msg, error_code& ec); -/** Read a complete message from a stream asynchronously. +/** Read a complete message asynchronously from a stream. This function is used to asynchronously read a complete message from a - stream using HTTP/1. - The function call always returns immediately. The asynchronous operation - will continue until one of the following conditions is true: + stream into an instance of @ref message. The function call always returns + immediately. The asynchronous operation will continue until one of the + following conditions is true: - @li The entire message is read. + @li The entire message is read in. @li An error occurs. - This operation is implemented in terms of one or more calls to - the stream's `async_read_some` function, and is known as a - composed operation. The program must ensure that the - stream performs no other reads until this operation completes. - The implementation may read additional octets that lie past the - end of the message being read. This additional data is stored - in the dynamic buffer, which must be retained for subsequent reads. - - If the stream returns the error `boost::asio::error::eof` indicating the - end of file during a read, the error returned from this function will be: - - @li @ref error::end_of_stream if no octets were parsed, or - - @li @ref error::partial_message if any octets were parsed but the - message was incomplete, otherwise: - - @li A successful result. A subsequent attempt to read will - return @ref error::end_of_stream - - @param stream The stream from which the data is to be read. - The type must support the @b AsyncReadStream concept. - - @param buffer A @b DynamicBuffer holding additional bytes - read by the implementation from the stream. This is both - an input and an output parameter; on entry, any data in the - dynamic buffer's input sequence will be given to the parser - first. - - @param msg An object in which to store the message contents. - This object should not have previous contents, otherwise - the behavior is undefined. - The type must be @b MoveAssignable and @b MoveConstructible. - - The object must remain valid at least until the - handler is called; ownership is not transferred. - - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( - error_code const& error, // result of operation, - std::size_t bytes_transferred // the number of bytes transferred to the parser - ); @endcode + This operation is implemented in terms of zero or more calls to the + next layer's `async_read_some` function, and is known as a composed + operation. The program must ensure that the stream performs no other + reads until this operation completes. The implementation may read additional + bytes from the stream that lie past the end of the message being read. + These additional bytes are stored in the dynamic buffer, which must be + preserved for subsequent reads. + + If the end of file error is received while reading from the stream, then + the error returned from this function will be: + + @li @ref error::end_of_stream if no bytes were parsed, or + + @li @ref error::partial_message if any bytes were parsed but the + message was incomplete, otherwise: + + @li A successful result. The next attempt to read will return + @ref error::end_of_stream + + @param stream The stream from which the data is to be read. The type + must meet the AsyncReadStream requirements. + + @param buffer Storage for additional bytes read by the implementation from + the stream. This is both an input and an output parameter; on entry, the + parser will be presented with any remaining data in the dynamic buffer's + readable bytes sequence first. The type must meet the DynamicBuffer + requirements. The object must remain valid at least until the handler + is called; ownership is not transferred. + + @param msg The container in which to store the message contents. This + message container should not have previous contents, otherwise the behavior + is undefined. The type must be meet the MoveAssignable and + MoveConstructible requirements. The object must remain valid + at least until the handler is called; ownership is not transferred. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + error_code const& error, // result of operation + std::size_t bytes_transferred // the total number of bytes transferred from the stream + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. + + @note The completion handler will receive as a parameter the total number + of bytes transferred from the stream. This may be zero for the case where + there is sufficient pre-existing message data in the dynamic buffer. The + implementation will call @ref basic_parser::eager with the value `true` + on the parser passed in. */ template< class AsyncReadStream, class DynamicBuffer, bool isRequest, class Body, class Allocator, class ReadHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) async_read( AsyncReadStream& stream, DynamicBuffer& buffer, @@ -760,6 +804,6 @@ async_read( } // beast } // boost -#include +#include #endif diff --git a/boost/beast/http/rfc7230.hpp b/boost/beast/http/rfc7230.hpp index 952cb929d6..bc2498db52 100644 --- a/boost/beast/http/rfc7230.hpp +++ b/boost/beast/http/rfc7230.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -66,7 +66,7 @@ public: /// A constant iterator to the list #if BOOST_BEAST_DOXYGEN - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else class const_iterator; #endif @@ -155,7 +155,7 @@ public: /// A constant iterator to the list #if BOOST_BEAST_DOXYGEN - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else class const_iterator; #endif @@ -243,7 +243,7 @@ public: /// A constant iterator to the list #if BOOST_BEAST_DOXYGEN - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else class const_iterator; #endif @@ -324,6 +324,6 @@ validate_list(detail::basic_parsed_list< } // beast } // boost -#include +#include #endif diff --git a/boost/beast/http/serializer.hpp b/boost/beast/http/serializer.hpp index 352e735fe5..f2922608c7 100644 --- a/boost/beast/http/serializer.hpp +++ b/boost/beast/http/serializer.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -58,18 +57,18 @@ class serializer { public: static_assert(is_body::value, - "Body requirements not met"); + "Body type requirements not met"); static_assert(is_body_writer::value, - "BodyWriter requirements not met"); + "BodyWriter type requirements not met"); /** The type of message this serializer uses This may be const or non-const depending on the - implementation of the corresponding @b BodyWriter. + implementation of the corresponding BodyWriter. */ #if BOOST_BEAST_DOXYGEN - using value_type = implementation_defined; + using value_type = __implementation_defined__; #else using value_type = typename std::conditional< std::is_constructible>; // crlf @@ -138,7 +137,7 @@ private: using cb5_t = buffers_suffix>; // crlf @@ -146,30 +145,30 @@ private: using cb6_t = buffers_suffix>; // crlf using pcb6_t = buffers_prefix_view; using cb7_t = buffers_suffix>; // crlf using pcb7_t = buffers_prefix_view; using cb8_t = buffers_suffix>; // crlf using pcb8_t = buffers_prefix_view; @@ -187,7 +186,7 @@ private: int s_ = do_construct; bool split_ = false; bool header_done_ = false; - bool more_; + bool more_ = false; public: /// Constructor @@ -295,7 +294,7 @@ public: /** Returns the next set of buffers in the serialization. This function will attempt to call the `visit` function - object with a @b ConstBufferSequence of unspecified type + object with a ConstBufferSequence of unspecified type representing the next set of buffers in the serialization of the message represented by this object. @@ -337,7 +336,7 @@ public: void consume(std::size_t n); - /** Provides low-level access to the associated @b BodyWriter + /** Provides low-level access to the associated BodyWriter This function provides access to the instance of the writer associated with the body and created by the serializer @@ -366,6 +365,6 @@ using response_serializer = serializer; } // beast } // boost -#include +#include #endif diff --git a/boost/beast/http/span_body.hpp b/boost/beast/http/span_body.hpp index f20481164e..a0d32f8470 100644 --- a/boost/beast/http/span_body.hpp +++ b/boost/beast/http/span_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -11,6 +11,7 @@ #define BOOST_BEAST_HTTP_SPAN_BODY_HPP #include +#include #include #include #include @@ -20,7 +21,7 @@ namespace boost { namespace beast { namespace http { -/** A @b Body using @ref span +/** A Body using @ref span This body uses @ref span as a memory-based container for holding message payloads. The container represents a @@ -61,10 +62,10 @@ public: /** The algorithm for parsing the body - Meets the requirements of @b BodyReader. + Meets the requirements of BodyReader. */ #if BOOST_BEAST_DOXYGEN - using reader = implementation_defined; + using reader = __implementation_defined__; #else class reader { @@ -87,7 +88,7 @@ public: ec = error::buffer_overflow; return; } - ec.assign(0, ec.category()); + ec = {}; } template @@ -95,17 +96,15 @@ public: put(ConstBufferSequence const& buffers, error_code& ec) { - using boost::asio::buffer_size; - using boost::asio::buffer_copy; - auto const n = buffer_size(buffers); + auto const n = buffer_bytes(buffers); auto const len = body_.size(); if(n > len) { ec = error::buffer_overflow; return 0; } - ec.assign(0, ec.category()); - buffer_copy(boost::asio::buffer( + ec = {}; + net::buffer_copy(net::buffer( body_.data(), n), buffers); body_ = value_type{ body_.data() + n, body_.size() - n}; @@ -115,17 +114,17 @@ public: void finish(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } }; #endif /** The algorithm for serializing the body - Meets the requirements of @b BodyWriter. + Meets the requirements of BodyWriter. */ #if BOOST_BEAST_DOXYGEN - using writer = implementation_defined; + using writer = __implementation_defined__; #else class writer { @@ -133,7 +132,7 @@ public: public: using const_buffers_type = - boost::asio::const_buffer; + net::const_buffer; template explicit @@ -145,13 +144,13 @@ public: void init(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } boost::optional> get(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; return {{ { body_.data(), body_.size() * sizeof(typename diff --git a/boost/beast/http/status.hpp b/boost/beast/http/status.hpp index 8d7938ca0c..2e9b7da762 100644 --- a/boost/beast/http/status.hpp +++ b/boost/beast/http/status.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -30,7 +30,16 @@ enum class status : unsigned unknown = 0, continue_ = 100, + + /** Switching Protocols + + This status indicates that a request to switch to a new + protocol was accepted and applied by the server. A successful + response to a WebSocket Upgrade HTTP request will have this + code. + */ switching_protocols = 101, + processing = 102, ok = 200, @@ -125,6 +134,7 @@ enum class status_class : unsigned If the integer does not match a known status code, @ref status::unknown is returned. */ +BOOST_BEAST_DECL status int_to_status(unsigned v); @@ -135,6 +145,7 @@ int_to_status(unsigned v); @return The status class. If the integer does not match a known status class, @ref status_class::unknown is returned. */ +BOOST_BEAST_DECL status_class to_status_class(unsigned v); @@ -144,6 +155,7 @@ to_status_class(unsigned v); @return The status class. */ +BOOST_BEAST_DECL status_class to_status_class(status v); @@ -151,10 +163,12 @@ to_status_class(status v); @param v The status code to use. */ +BOOST_BEAST_DECL string_view obsolete_reason(status v); /// Outputs the standard reason phrase of a status code to a stream. +BOOST_BEAST_DECL std::ostream& operator<<(std::ostream&, status); @@ -162,6 +176,8 @@ operator<<(std::ostream&, status); } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/boost/beast/http/string_body.hpp b/boost/beast/http/string_body.hpp index 1e37aa0dc5..73a01a41fd 100644 --- a/boost/beast/http/string_body.hpp +++ b/boost/beast/http/string_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -11,8 +11,11 @@ #define BOOST_BEAST_HTTP_STRING_BODY_HPP #include +#include #include #include +#include +#include #include #include #include @@ -27,7 +30,7 @@ namespace boost { namespace beast { namespace http { -/** A @b Body using `std::basic_string` +/** A Body using `std::basic_string` This body uses `std::basic_string` as a memory-based container for holding message payloads. Messages using this body type @@ -69,10 +72,10 @@ public: /** The algorithm for parsing the body - Meets the requirements of @b BodyReader. + Meets the requirements of BodyReader. */ #if BOOST_BEAST_DOXYGEN - using reader = implementation_defined; + using reader = __implementation_defined__; #else class reader { @@ -92,23 +95,14 @@ public: { if(length) { - if(static_cast(*length) != *length) - { - ec = error::buffer_overflow; - return; - } - try - { - body_.reserve( - static_cast(*length)); - } - catch(std::exception const&) + if(*length > body_.max_size()) { ec = error::buffer_overflow; return; } + body_.reserve(beast::detail::clamp(*length)); } - ec.assign(0, ec.category()); + ec = {}; } template @@ -116,22 +110,18 @@ public: put(ConstBufferSequence const& buffers, error_code& ec) { - using boost::asio::buffer_size; - using boost::asio::buffer_copy; - auto const extra = buffer_size(buffers); + auto const extra = buffer_bytes(buffers); auto const size = body_.size(); - try - { - body_.resize(size + extra); - } - catch(std::exception const&) + if (extra > body_.max_size() - size) { ec = error::buffer_overflow; return 0; } - ec.assign(0, ec.category()); + + body_.resize(size + extra); + ec = {}; CharT* dest = &body_[size]; - for(auto b : beast::detail::buffers_range(buffers)) + for(auto b : beast::buffers_range_ref(buffers)) { Traits::copy(dest, static_cast< CharT const*>(b.data()), b.size()); @@ -143,17 +133,17 @@ public: void finish(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } }; #endif /** The algorithm for serializing the body - Meets the requirements of @b BodyWriter. + Meets the requirements of BodyWriter. */ #if BOOST_BEAST_DOXYGEN - using writer = implementation_defined; + using writer = __implementation_defined__; #else class writer { @@ -161,7 +151,7 @@ public: public: using const_buffers_type = - boost::asio::const_buffer; + net::const_buffer; template explicit @@ -173,13 +163,13 @@ public: void init(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } boost::optional> get(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; return {{const_buffers_type{ body_.data(), body_.size()}, false}}; } @@ -187,7 +177,7 @@ public: #endif }; -/// A @b Body using `std::string` +/// A Body using `std::string` using string_body = basic_string_body; } // http diff --git a/boost/beast/http/type_traits.hpp b/boost/beast/http/type_traits.hpp index d3b897f894..b8bfdbaaa1 100644 --- a/boost/beast/http/type_traits.hpp +++ b/boost/beast/http/type_traits.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -25,14 +24,14 @@ namespace beast { namespace http { template -struct message; +class message; -/** Determine if `T` meets the requirements of @b Body. +/** Determine if a type meets the Body named requirements. - This metafunction is equivalent to `std::true_type` - if `T` has a nested type named `value_type`. + This alias template is `std::true_type` if `T` meets + the requirements, otherwise it is `std::false_type`. - @tparam T The body type to test. + @tparam T The type to test. @par Example @code @@ -40,24 +39,24 @@ struct message; void check_body(message const&) { static_assert(is_body::value, - "Body requirements not met"); + "Body type requirements not met"); } @endcode */ template #if BOOST_BEAST_DOXYGEN -struct is_body : std::integral_constant{}; +using is_body = __see_below__; #else using is_body = detail::has_value_type; #endif -/** Determine if a @b Body type has a reader. +/** Determine if a type has a nested BodyWriter. - This metafunction is equivalent to `std::true_type` if: + This alias template is `std::true_type` when: - @li `T` has a nested type named `reader` + @li `T` has a nested type named `writer` - @li The nested type meets the requirements of @b BodyWriter. + @li `writer` meets the requirements of BodyWriter. @tparam T The body type to test. @@ -73,7 +72,7 @@ using is_body = detail::has_value_type; */ #if BOOST_BEAST_DOXYGEN template -struct is_body_writer : std::integral_constant {}; +using is_body_writer = __see_below__; #else template struct is_body_writer : std::false_type {}; @@ -88,7 +87,7 @@ struct is_body_writer>&>() = std::declval().get(std::declval()) )>> : std::integral_constant::value && ( (std::is_constructible&, @@ -100,11 +99,19 @@ struct is_body_writer {}; #endif -/** Returns true if the writer for a @b Body mutates the body container. +/** Determine if a type has a nested BodyWriter. + + This alias template is `std::true_type` when: + + @li `T` has a nested type named `writer` + + @li `writer` meets the requirements of BodyWriter. + + @tparam T The body type to test. */ #if BOOST_BEAST_DOXYGEN template -struct is_mutable_body_writer : std::integral_constant {}; +using is_mutable_body_writer = __see_below__; #else template struct is_mutable_body_writer : std::false_type {}; @@ -119,7 +126,7 @@ struct is_mutable_body_writer>&>() = std::declval().get(std::declval()) )>> : std::integral_constant::value && (( std::is_constructible&, @@ -137,13 +144,13 @@ struct is_mutable_body_writer{}; #endif -/** Determine if a @b Body type has a reader. +/** Determine if a type has a nested BodyReader. - This metafunction is equivalent to `std::true_type` if: + This alias template is `std::true_type` when: @li `T` has a nested type named `reader` - @li The nested type meets the requirements of @b BodyReader. + @li `reader` meets the requirements of BodyReader. @tparam T The body type to test. @@ -159,7 +166,7 @@ struct is_mutable_body_writer -struct is_body_reader : std::integral_constant {}; +using is_body_reader = __see_below__; #else template struct is_body_reader : std::false_type {}; @@ -171,7 +178,7 @@ struct is_body_reader()), std::declval() = std::declval().put( - std::declval(), + std::declval(), std::declval()), std::declval().finish( std::declval()) @@ -187,25 +194,25 @@ struct is_body_readerFields named requirements. - @tparam T The body type to test. + This alias template is `std::true_type` if `T` meets + the requirements, otherwise it is `std::false_type`. - @par Example + @tparam T The type to test. + @par Example Use with `static_assert`: - @code template void f(message const&) { static_assert(is_fields::value, - "Fields requirements not met"); + "Fields type requirements not met"); ... @endcode Use with `std::enable_if` (SFINAE): - @code template typename std::enable_if::value>::type @@ -214,7 +221,7 @@ struct is_body_reader -struct is_fields : std::integral_constant {}; +using is_fields = __see_below__; #else template using is_fields = typename detail::is_fields_helper::type; diff --git a/boost/beast/http/vector_body.hpp b/boost/beast/http/vector_body.hpp index 40c885e43e..b44ef0f625 100644 --- a/boost/beast/http/vector_body.hpp +++ b/boost/beast/http/vector_body.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -11,8 +11,10 @@ #define BOOST_BEAST_HTTP_VECTOR_BODY_HPP #include +#include #include #include +#include #include #include #include @@ -27,7 +29,7 @@ namespace boost { namespace beast { namespace http { -/** A @b Body using `std::vector` +/** A Body using `std::vector` This body uses `std::vector` as a memory-based container for holding message payloads. Messages using this body type @@ -64,10 +66,10 @@ public: /** The algorithm for parsing the body - Meets the requirements of @b BodyReader. + Meets the requirements of BodyReader. */ #if BOOST_BEAST_DOXYGEN - using reader = implementation_defined; + using reader = __implementation_defined__; #else class reader { @@ -87,23 +89,14 @@ public: { if(length) { - if(static_cast(*length) != *length) - { - ec = error::buffer_overflow; - return; - } - try - { - body_.reserve( - static_cast(*length)); - } - catch(std::exception const&) + if(*length > body_.max_size()) { ec = error::buffer_overflow; return; } + body_.reserve(beast::detail::clamp(*length)); } - ec.assign(0, ec.category()); + ec = {}; } template @@ -111,38 +104,34 @@ public: put(ConstBufferSequence const& buffers, error_code& ec) { - using boost::asio::buffer_size; - using boost::asio::buffer_copy; - auto const n = buffer_size(buffers); + auto const n = buffer_bytes(buffers); auto const len = body_.size(); - try - { - body_.resize(len + n); - } - catch(std::exception const&) + if (n > body_.max_size() - len) { ec = error::buffer_overflow; return 0; } - ec.assign(0, ec.category()); - return buffer_copy(boost::asio::buffer( + + body_.resize(len + n); + ec = {}; + return net::buffer_copy(net::buffer( &body_[0] + len, n), buffers); } void finish(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } }; #endif /** The algorithm for serializing the body - Meets the requirements of @b BodyWriter. + Meets the requirements of BodyWriter. */ #if BOOST_BEAST_DOXYGEN - using writer = implementation_defined; + using writer = __implementation_defined__; #else class writer { @@ -150,7 +139,7 @@ public: public: using const_buffers_type = - boost::asio::const_buffer; + net::const_buffer; template explicit @@ -162,13 +151,13 @@ public: void init(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; } boost::optional> get(error_code& ec) { - ec.assign(0, ec.category()); + ec = {}; return {{const_buffers_type{ body_.data(), body_.size()}, false}}; } diff --git a/boost/beast/http/verb.hpp b/boost/beast/http/verb.hpp index 1540a243e8..5352851a48 100644 --- a/boost/beast/http/verb.hpp +++ b/boost/beast/http/verb.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -133,10 +133,12 @@ enum class verb If the string does not match a known request method, @ref verb::unknown is returned. */ +BOOST_BEAST_DECL verb string_to_verb(string_view s); /// Returns the text representation of a request method verb. +BOOST_BEAST_DECL string_view to_string(verb v); @@ -152,6 +154,8 @@ operator<<(std::ostream& os, verb v) } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include +#endif #endif diff --git a/boost/beast/http/write.hpp b/boost/beast/http/write.hpp index 422f93404f..b4d741013c 100644 --- a/boost/beast/http/write.hpp +++ b/boost/beast/http/write.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// 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) @@ -55,7 +55,7 @@ namespace http { stream. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param sr The serializer to use. @@ -97,7 +97,7 @@ write_some( stream. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param sr The serializer to use. @@ -105,7 +105,7 @@ write_some( @return The number of bytes written to the stream. - @see @ref async_write_some, @ref serializer + @see async_write_some, serializer */ template< class SyncWriteStream, @@ -143,32 +143,34 @@ write_some( stream. @param stream The stream to which the data is to be written. - The type must support the @b AsyncWriteStream concept. + The type must support the AsyncWriteStream concept. @param sr The serializer to use. The object must remain valid at least until the handler is called; ownership is not transferred. - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( error_code const& error, // result of operation std::size_t bytes_transferred // the number of bytes written to the stream - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. - @see @ref serializer + @see serializer */ template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) async_write_some( AsyncWriteStream& stream, serializer& sr, @@ -190,7 +192,7 @@ async_write_some( to the stream's `write_some` function. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param sr The serializer to use. @@ -201,7 +203,7 @@ async_write_some( @note The implementation will call @ref serializer::split with the value `true` on the serializer passed in. - @see @ref serializer + @see serializer */ template< class SyncWriteStream, @@ -225,7 +227,7 @@ write_header( to the stream's `write_some` function. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param sr The serializer to use. @@ -236,7 +238,7 @@ write_header( @note The implementation will call @ref serializer::split with the value `true` on the serializer passed in. - @see @ref serializer + @see serializer */ template< class SyncWriteStream, @@ -264,35 +266,37 @@ write_header( until this operation completes. @param stream The stream to which the data is to be written. - The type must support the @b AsyncWriteStream concept. + The type must support the AsyncWriteStream concept. @param sr The serializer to use. The object must remain valid at least until the handler is called; ownership is not transferred. - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( error_code const& error, // result of operation std::size_t bytes_transferred // the number of bytes written to the stream - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. @note The implementation will call @ref serializer::split with the value `true` on the serializer passed in. - @see @ref serializer + @see serializer */ template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) async_write_header( AsyncWriteStream& stream, serializer& sr, @@ -314,7 +318,7 @@ async_write_header( to the stream's `write_some` function. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param sr The serializer to use. @@ -322,7 +326,7 @@ async_write_header( @throws system_error Thrown on failure. - @see @ref serializer + @see serializer */ template< class SyncWriteStream, @@ -346,7 +350,7 @@ write( to the stream's `write_some` function. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param sr The serializer to use. @@ -354,7 +358,7 @@ write( @return The number of bytes written to the stream. - @see @ref serializer + @see serializer */ template< class SyncWriteStream, @@ -382,32 +386,34 @@ write( until this operation completes. @param stream The stream to which the data is to be written. - The type must support the @b AsyncWriteStream concept. + The type must support the AsyncWriteStream concept. @param sr The serializer to use. The object must remain valid at least until the handler is called; ownership is not transferred. - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( error_code const& error, // result of operation std::size_t bytes_transferred // the number of bytes written to the stream - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. - @see @ref serializer + @see serializer */ template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) async_write( AsyncWriteStream& stream, serializer& sr, @@ -429,10 +435,10 @@ async_write( with an empty chunk decorator to produce buffers. @note This function only participates in overload resolution - if @ref is_mutable_body_writer for @b Body returns `true`. + if @ref is_mutable_body_writer for Body returns `true`. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param msg The message to write. @@ -440,7 +446,7 @@ async_write( @throws system_error Thrown on failure. - @see @ref message + @see message */ template< class SyncWriteStream, @@ -470,10 +476,10 @@ write( with an empty chunk decorator to produce buffers. @note This function only participates in overload resolution - if @ref is_mutable_body_writer for @b Body returns `false`. + if @ref is_mutable_body_writer for Body returns `false`. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param msg The message to write. @@ -481,7 +487,7 @@ write( @throws system_error Thrown on failure. - @see @ref message + @see message */ template< class SyncWriteStream, @@ -511,10 +517,10 @@ write( with an empty chunk decorator to produce buffers. @note This function only participates in overload resolution - if @ref is_mutable_body_writer for @b Body returns `true`. + if @ref is_mutable_body_writer for Body returns `true`. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param msg The message to write. @@ -522,7 +528,7 @@ write( @return The number of bytes written to the stream. - @see @ref message + @see message */ template< class SyncWriteStream, @@ -553,10 +559,10 @@ write( with an empty chunk decorator to produce buffers. @note This function only participates in overload resolution - if @ref is_mutable_body_writer for @b Body returns `false`. + if @ref is_mutable_body_writer for Body returns `false`. @param stream The stream to which the data is to be written. - The type must support the @b SyncWriteStream concept. + The type must support the SyncWriteStream concept. @param msg The message to write. @@ -564,7 +570,7 @@ write( @return The number of bytes written to the stream. - @see @ref message + @see message */ template< class SyncWriteStream, @@ -598,41 +604,42 @@ write( @ref serializer with an empty chunk decorator to produce buffers. @note This function only participates in overload resolution - if @ref is_mutable_body_writer for @b Body returns `true`. + if @ref is_mutable_body_writer for Body returns `true`. @param stream The stream to which the data is to be written. - The type must support the @b AsyncWriteStream concept. + The type must support the AsyncWriteStream concept. @param msg The message to write. The object must remain valid at least until the handler is called; ownership is not transferred. - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( error_code const& error, // result of operation std::size_t bytes_transferred // the number of bytes written to the stream - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. - @see @ref message + @see message */ template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> #if BOOST_BEAST_DOXYGEN -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) #else typename std::enable_if< is_mutable_body_writer::value, - BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t))>::type + BOOST_BEAST_ASYNC_RESULT2(WriteHandler)>::type #endif async_write( AsyncWriteStream& stream, @@ -656,41 +663,42 @@ async_write( @ref serializer with an empty chunk decorator to produce buffers. @note This function only participates in overload resolution - if @ref is_mutable_body_writer for @b Body returns `false`. + if @ref is_mutable_body_writer for Body returns `false`. @param stream The stream to which the data is to be written. - The type must support the @b AsyncWriteStream concept. + The type must support the AsyncWriteStream concept. @param msg The message to write. The object must remain valid at least until the handler is called; ownership is not transferred. - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( error_code const& error, // result of operation std::size_t bytes_transferred // the number of bytes written to the stream - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. - @see @ref message + @see message */ template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> #if BOOST_BEAST_DOXYGEN -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) #else typename std::enable_if< ! is_mutable_body_writer::value, - BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t))>::type + BOOST_BEAST_ASYNC_RESULT2(WriteHandler)>::type #endif async_write( AsyncWriteStream& stream, @@ -734,6 +742,6 @@ operator<<(std::ostream& os, } // beast } // boost -#include +#include #endif -- cgit v1.2.3