diff options
Diffstat (limited to 'boost/beast/http/message.hpp')
-rw-r--r-- | boost/beast/http/message.hpp | 999 |
1 files changed, 999 insertions, 0 deletions
diff --git a/boost/beast/http/message.hpp b/boost/beast/http/message.hpp new file mode 100644 index 0000000000..5cf5d94dc9 --- /dev/null +++ b/boost/beast/http/message.hpp @@ -0,0 +1,999 @@ +// +// 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_MESSAGE_HPP +#define BOOST_BEAST_HTTP_MESSAGE_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/http/fields.hpp> +#include <boost/beast/http/verb.hpp> +#include <boost/beast/http/status.hpp> +#include <boost/beast/http/type_traits.hpp> +#include <boost/beast/core/string.hpp> +#include <boost/beast/core/detail/empty_base_optimization.hpp> +#include <boost/beast/core/detail/integer_sequence.hpp> +#include <boost/assert.hpp> +#include <boost/optional.hpp> +#include <boost/throw_exception.hpp> +#include <memory> +#include <stdexcept> +#include <string> +#include <tuple> +#include <utility> + +namespace boost { +namespace beast { +namespace http { + +/** A container for an HTTP request or response header. + + This container is derived from the `Fields` template type. + To understand all of the members of this class it is necessary + to view the declaration for the `Fields` type. When using + the default fields container, those declarations are in + @ref fields. + + Newly constructed header objects have version set to + HTTP/1.1. Newly constructed response objects also have + result code set to @ref status::ok. + + A `header` includes the start-line and header-fields. +*/ +#if BOOST_BEAST_DOXYGEN +template<bool isRequest, class Fields = fields> +struct header : Fields + +#else +template<bool isRequest, class Fields = fields> +struct header; + +template<class Fields> +struct header<true, Fields> : Fields +#endif +{ + static_assert(is_fields<Fields>::value, + "Fields requirements not met"); + + /// Indicates if the header is a request or response. +#if BOOST_BEAST_DOXYGEN + using is_request = std::integral_constant<bool, isRequest>; +#else + using is_request = std::true_type; +#endif + + /// The type representing the fields. + using fields_type = Fields; + + /// Constructor + header() = default; + + /// Constructor + header(header&&) = default; + + /// Constructor + header(header const&) = default; + + /// Assignment + header& operator=(header&&) = default; + + /// Assignment + header& operator=(header const&) = default; + + /** Return the HTTP-version. + + This holds both the major and minor version numbers, + using these formulas: + @code + unsigned major = version / 10; + unsigned minor = version % 10; + @endcode + + Newly constructed headers will use HTTP/1.1 by default. + */ + unsigned version() const noexcept + { + return version_; + } + + /** Set the HTTP-version. + + This holds both the major and minor version numbers, + using these formulas: + @code + unsigned major = version / 10; + unsigned minor = version % 10; + @endcode + + Newly constructed headers will use HTTP/1.1 by default. + + @param value The version number to use + */ + void version(unsigned value) noexcept + { + BOOST_ASSERT(value > 0 && value < 100); + version_ = value; + } + + /** Return the request-method verb. + + If the request-method is not one of the recognized verbs, + @ref verb::unknown is returned. Callers may use @ref method_string + to retrieve the exact text. + + @note This function is only available when `isRequest == true`. + + @see @ref method_string + */ + verb + method() const; + + /** Set the request-method. + + This function will set the method for requests to a known verb. + + @param v The request method verb to set. + This may not be @ref verb::unknown. + + @throws std::invalid_argument when `v == verb::unknown`. + + @note This function is only available when `isRequest == true`. + */ + void + method(verb v); + + /** Return the request-method as a string. + + @note This function is only available when `isRequest == true`. + + @see @ref method + */ + string_view + method_string() const; + + /** Set the request-method. + + This function will set the request-method a known verb + if the string matches, otherwise it will store a copy of + the passed string. + + @param s A string representing the request-method. + + @note This function is only available when `isRequest == true`. + */ + void + method_string(string_view s); + + /** Returns the request-target string. + + The request target string returned is the same string which + was received from the network or stored. In particular, it will + contain url-encoded characters and should follow the syntax + rules for URIs used with HTTP. + + @note This function is only available when `isRequest == true`. + */ + string_view + target() const; + + /** Set the request-target string. + + It is the caller's responsibility to ensure that the request + target string follows the syntax rules for URIs used with + HTTP. In particular, reserved or special characters must be + url-encoded. The implementation does not perform syntax checking + on the passed string. + + @param s A string representing the request-target. + + @note This function is only available when `isRequest == true`. + */ + void + target(string_view s); + + // VFALCO Don't rearrange these declarations or + // ifdefs, or else the documentation will break. + + /** Constructor + + @param args Arguments forwarded to the `Fields` + base class constructor. + + @note This constructor participates in overload + resolution if and only if the first parameter is + not convertible to @ref header, @ref verb, or + @ref status. + */ +#if BOOST_BEAST_DOXYGEN + template<class... Args> + explicit + header(Args&&... args); + +#else + template<class Arg1, class... ArgN, + class = typename std::enable_if< + ! std::is_convertible<typename + std::decay<Arg1>::type, header>::value && + ! std::is_convertible<typename + std::decay<Arg1>::type, verb>::value && + ! std::is_convertible<typename + std::decay<Arg1>::type, header>::value + >::type> + explicit + header(Arg1&& arg1, ArgN&&... argn); + +private: + template<bool, class, class> + friend struct message; + + template<class T> + friend + void + swap(header<true, T>& m1, header<true, T>& m2); + + template<class... FieldsArgs> + header( + verb method, + string_view target_, + unsigned version_value, + FieldsArgs&&... fields_args) + : Fields(std::forward<FieldsArgs>(fields_args)...) + , method_(method) + { + version(version_value); + target(target_); + } + + unsigned version_ = 11; + verb method_ = verb::unknown; +}; + +/** A container for an HTTP request or response header. + + A `header` includes the start-line and header-fields. +*/ +template<class Fields> +struct header<false, Fields> : Fields +{ + static_assert(is_fields<Fields>::value, + "Fields requirements not met"); + + /// Indicates if the header is a request or response. + using is_request = std::false_type; + + /// The type representing the fields. + using fields_type = Fields; + + /// Constructor. + header() = default; + + /// Constructor + header(header&&) = default; + + /// Constructor + header(header const&) = default; + + /// Assignment + header& operator=(header&&) = default; + + /// Assignment + header& operator=(header const&) = default; + + /** Constructor + + @param args Arguments forwarded to the `Fields` + base class constructor. + + @note This constructor participates in overload + resolution if and only if the first parameter is + not convertible to @ref header, @ref verb, or + @ref status. + */ + template<class Arg1, class... ArgN, + class = typename std::enable_if< + ! std::is_convertible<typename + std::decay<Arg1>::type, status>::value && + ! std::is_convertible<typename + std::decay<Arg1>::type, header>::value + >::type> + explicit + header(Arg1&& arg1, ArgN&&... argn); + + /** Return the HTTP-version. + + This holds both the major and minor version numbers, + using these formulas: + @code + unsigned major = version / 10; + unsigned minor = version % 10; + @endcode + + Newly constructed headers will use HTTP/1.1 by default. + */ + unsigned version() const noexcept + { + return version_; + } + + /** Set the HTTP-version. + + This holds both the major and minor version numbers, + using these formulas: + @code + unsigned major = version / 10; + unsigned minor = version % 10; + @endcode + + Newly constructed headers will use HTTP/1.1 by default. + + @param value The version number to use + */ + void version(unsigned value) noexcept + { + BOOST_ASSERT(value > 0 && value < 100); + version_ = value; + } +#endif + + /** The response status-code result. + + If the actual status code is not a known code, this + function returns @ref status::unknown. Use @ref result_int + to return the raw status code as a number. + + @note This member is only available when `isRequest == false`. + */ + status + result() const; + + /** Set the response status-code. + + @param v The code to set. + + @note This member is only available when `isRequest == false`. + */ + void + result(status v); + + /** Set the response status-code as an integer. + + This sets the status code to the exact number passed in. + If the number does not correspond to one of the known + status codes, the function @ref result will return + @ref status::unknown. Use @ref result_int to obtain the + original raw status-code. + + @param v The status-code integer to set. + + @throws std::invalid_argument if `v > 999`. + */ + void + result(unsigned v); + + /** The response status-code expressed as an integer. + + This returns the raw status code as an integer, even + when that code is not in the list of known status codes. + + @note This member is only available when `isRequest == false`. + */ + unsigned + result_int() const; + + /** Return the response reason-phrase. + + The reason-phrase is obsolete as of rfc7230. + + @note This function is only available when `isRequest == false`. + */ + string_view + reason() const; + + /** Set the response reason-phrase (deprecated) + + This function sets a custom reason-phrase to a copy of + the string passed in. Normally it is not necessary to set + the reason phrase on an outgoing response object; the + implementation will automatically use the standard reason + text for the corresponding status code. + + To clear a previously set custom phrase, pass an empty + string. This will restore the default standard reason text + based on the status code used when serializing. + + The reason-phrase is obsolete as of rfc7230. + + @param s The string to use for the reason-phrase. + + @note This function is only available when `isRequest == false`. + */ + void + reason(string_view s); + +private: +#if ! BOOST_BEAST_DOXYGEN + template<bool, class, class> + friend struct message; + + template<class T> + friend + void + swap(header<false, T>& m1, header<false, T>& m2); + + template<class... FieldsArgs> + header( + status result, + unsigned version_value, + FieldsArgs&&... fields_args) + : Fields(std::forward<FieldsArgs>(fields_args)...) + , result_(result) + { + version(version_value); + } + + unsigned version_ = 11; + status result_ = status::ok; +#endif +}; + +/// A typical HTTP request header +template<class Fields = fields> +using request_header = header<true, Fields>; + +/// A typical HTTP response header +template<class Fields = fields> +using response_header = header<false, Fields>; + +#if defined(BOOST_MSVC) +// Workaround for MSVC bug with private base classes +namespace detail { +template<class T> +using value_type_t = typename T::value_type; +} // detail +#endif + +/** A container for a complete HTTP message. + + This container is derived from the `Fields` template type. + To understand all of the members of this class it is necessary + to view the declaration for the `Fields` type. When using + the default fields container, those declarations are in + @ref fields. + + A message can be a request or response, depending on the + `isRequest` template argument value. Requests and responses + have different types; functions may be overloaded based on + the type if desired. + + The `Body` template argument type determines the model used + to read or write the content body of the message. + + Newly constructed messages objects have version set to + HTTP/1.1. Newly constructed response objects also have + result code set to @ref status::ok. + + @tparam isRequest `true` if this represents a request, + or `false` if this represents a response. Some class data + members are conditionally present depending on this value. + + @tparam Body A type meeting the requirements of Body. + + @tparam Fields The type of container used to hold the + field value pairs. +*/ +template<bool isRequest, class Body, class Fields = fields> +struct message + : header<isRequest, Fields> +#if ! BOOST_BEAST_DOXYGEN + , beast::detail::empty_base_optimization< + typename Body::value_type> +#endif +{ + /// The base class used to hold the header portion of the message. + using header_type = header<isRequest, Fields>; + + /** The type providing the body traits. + + The @ref message::body member will be of type `body_type::value_type`. + */ + using body_type = Body; + + /// Constructor + message() = default; + + /// Constructor + message(message&&) = default; + + /// Constructor + message(message const&) = default; + + /// Assignment + message& operator=(message&&) = default; + + /// Assignment + message& operator=(message const&) = default; + + /** Constructor + + @param h The header to move construct from. + + @param body_args Optional arguments forwarded + to the `body` constructor. + */ + template<class... BodyArgs> + explicit + message(header_type&& h, BodyArgs&&... body_args); + + /** Constructor. + + @param h The header to copy construct from. + + @param body_args Optional arguments forwarded + to the `body` constructor. + */ + template<class... BodyArgs> + explicit + message(header_type const& h, BodyArgs&&... body_args); + + /** Constructor + + @param method The request-method to use + + @param target The request-target. + + @param version The HTTP-version + + @note This function is only available when `isRequest == true`. + */ +#if BOOST_BEAST_DOXYGEN + message(verb method, string_view target, unsigned version); +#else + template<class Version, + class = typename std::enable_if<isRequest && + std::is_convertible<Version, unsigned>::value>::type> + message(verb method, string_view target, Version version); +#endif + + /** Constructor + + @param method The request-method to use + + @param target The request-target. + + @param version The HTTP-version + + @param body_arg An argument forwarded to the `body` constructor. + + @note This function is only available when `isRequest == true`. + */ +#if BOOST_BEAST_DOXYGEN + template<class BodyArg> + message(verb method, string_view target, + unsigned version, BodyArg&& body_arg); +#else + template<class Version, class BodyArg, + class = typename std::enable_if<isRequest && + std::is_convertible<Version, unsigned>::value>::type> + message(verb method, string_view target, + Version version, BodyArg&& body_arg); +#endif + + /** Constructor + + @param method The request-method to use + + @param target The request-target. + + @param version The HTTP-version + + @param body_arg An argument forwarded to the `body` constructor. + + @param fields_arg An argument forwarded to the `Fields` constructor. + + @note This function is only available when `isRequest == true`. + */ +#if BOOST_BEAST_DOXYGEN + template<class BodyArg, class FieldsArg> + message(verb method, string_view target, unsigned version, + BodyArg&& body_arg, FieldsArg&& fields_arg); +#else + template<class Version, class BodyArg, class FieldsArg, + class = typename std::enable_if<isRequest && + std::is_convertible<Version, unsigned>::value>::type> + message(verb method, string_view target, Version version, + BodyArg&& body_arg, FieldsArg&& fields_arg); +#endif + + /** Constructor + + @param result The status-code for the response + + @param version The HTTP-version + + @note This member is only available when `isRequest == false`. + */ +#if BOOST_BEAST_DOXYGEN + message(status result, unsigned version); +#else + template<class Version, + class = typename std::enable_if<! isRequest && + std::is_convertible<Version, unsigned>::value>::type> + message(status result, Version version); +#endif + + /** Constructor + + @param result The status-code for the response + + @param version The HTTP-version + + @param body_arg An argument forwarded to the `body` constructor. + + @note This member is only available when `isRequest == false`. + */ +#if BOOST_BEAST_DOXYGEN + template<class BodyArg> + message(status result, unsigned version, BodyArg&& body_arg); +#else + template<class Version, class BodyArg, + class = typename std::enable_if<! isRequest && + std::is_convertible<Version, unsigned>::value>::type> + message(status result, Version version, BodyArg&& body_arg); +#endif + + /** Constructor + + @param result The status-code for the response + + @param version The HTTP-version + + @param body_arg An argument forwarded to the `body` constructor. + + @param fields_arg An argument forwarded to the `Fields` base class constructor. + + @note This member is only available when `isRequest == false`. + */ +#if BOOST_BEAST_DOXYGEN + template<class BodyArg, class FieldsArg> + message(status result, unsigned version, + BodyArg&& body_arg, FieldsArg&& fields_arg); +#else + template<class Version, class BodyArg, class FieldsArg, + class = typename std::enable_if<! isRequest && + std::is_convertible<Version, unsigned>::value>::type> + message(status result, Version version, + BodyArg&& body_arg, FieldsArg&& fields_arg); +#endif + + /** Constructor + + The header and body are default-constructed. + */ + explicit + message(std::piecewise_construct_t); + + /** Construct a message. + + @param body_args A tuple forwarded as a parameter + pack to the body constructor. + */ + template<class... BodyArgs> + message(std::piecewise_construct_t, + std::tuple<BodyArgs...> body_args); + + /** Construct a message. + + @param body_args A tuple forwarded as a parameter + pack to the body constructor. + + @param fields_args A tuple forwarded as a parameter + pack to the `Fields` constructor. + */ + template<class... BodyArgs, class... FieldsArgs> + message(std::piecewise_construct_t, + std::tuple<BodyArgs...> body_args, + std::tuple<FieldsArgs...> fields_args); + + /// Returns the header portion of the message + header_type const& + base() const + { + return *this; + } + + /// Returns the header portion of the message + header_type& + base() + { + return *this; + } + + /// Returns `true` if the chunked Transfer-Encoding is specified + bool + chunked() const + { + return this->get_chunked_impl(); + } + + /** Set or clear the chunked Transfer-Encoding + + This function will set or removed the "chunked" transfer + encoding as the last item in the list of encodings in the + field. + + If the result of removing the chunked token results in an + empty string, the field is erased. + + The Content-Length field is erased unconditionally. + */ + void + chunked(bool value); + + /** Returns `true` if the Content-Length field is present. + + This function inspects the fields and returns `true` if + the Content-Length field is present. The properties of the + body are not checked, this only looks for the field. + */ + bool + has_content_length() const + { + return this->has_content_length_impl(); + } + + /** Set or clear the Content-Length field + + This function adjusts the Content-Length field as follows: + + @li If `value` specifies a value, the Content-Length field + is set to the value. Otherwise + + @li The Content-Length field is erased. + + If "chunked" token appears as the last item in the + Transfer-Encoding field it is unconditionally removed. + + @param value The value to set for Content-Length. + */ + void + content_length(boost::optional<std::uint64_t> const& value); + + /** Returns `true` if the message semantics indicate keep-alive + + The value depends on the version in the message, which must + be set to the final value before this function is called or + else the return value is unreliable. + */ + bool + keep_alive() const + { + return this->get_keep_alive_impl(this->version()); + } + + /** Set the keep-alive message semantic option + + This function adjusts the Connection field to indicate + whether or not the connection should be kept open after + the corresponding response. The result depends on the + version set on the message, which must be set to the + final value before making this call. + + @param value `true` if the connection should persist. + */ + void + keep_alive(bool value) + { + this->set_keep_alive_impl(this->version(), value); + } + + /** Returns `true` if the message semantics require an end of file. + + For HTTP requests, this function returns the logical + NOT of a call to @ref keep_alive. + + For HTTP responses, this function returns the logical NOT + of a call to @ref keep_alive if any of the following are true: + + @li @ref has_content_length would return `true` + + @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) + + Otherwise, the function returns `true`. + + @see https://tools.ietf.org/html/rfc7230#section-3.3 + */ + bool + need_eof() const + { + return need_eof(typename header_type::is_request{}); + } + + /** Returns the payload size of the body in octets if possible. + + This function invokes the @b 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` + is returned (usually indicating that a chunked Transfer-Encoding + will be used). + + @note The value of the Content-Length field in the message + is not inspected. + */ + boost::optional<std::uint64_t> + payload_size() const; + + /** Prepare the message payload fields for the body. + + This function will adjust the Content-Length and + Transfer-Encoding field values based on the properties + of the body. + + @par Example + @code + request<string_body> req{verb::post, "/"}; + req.set(field::user_agent, "Beast"); + req.body() = "Hello, world!"; + req.prepare_payload(); + @endcode + */ + void + prepare_payload() + { + prepare_payload(typename header_type::is_request{}); + } + + /// Returns the body +#if BOOST_BEAST_DOXYGEN || ! defined(BOOST_MSVC) + typename body_type::value_type& +#else + detail::value_type_t<Body>& +#endif + body()& noexcept + { + return this->member(); + } + + /// Returns the body +#if BOOST_BEAST_DOXYGEN || ! defined(BOOST_MSVC) + typename body_type::value_type&& +#else + detail::value_type_t<Body>&& +#endif + body()&& noexcept + { + return std::move(this->member()); + } + + /// Returns the body +#if BOOST_BEAST_DOXYGEN || ! defined(BOOST_MSVC) + typename body_type::value_type const& +#else + detail::value_type_t<Body> const& +#endif + body() const& noexcept + { + return this->member(); + } + +private: + static_assert(is_body<Body>::value, + "Body requirements not met"); + + template< + class... BodyArgs, + std::size_t... IBodyArgs> + message( + std::piecewise_construct_t, + std::tuple<BodyArgs...>& body_args, + beast::detail::index_sequence<IBodyArgs...>) + : beast::detail::empty_base_optimization< + typename Body::value_type>( + std::forward<BodyArgs>( + std::get<IBodyArgs>(body_args))...) + { + boost::ignore_unused(body_args); + } + + template< + class... BodyArgs, + class... FieldsArgs, + std::size_t... IBodyArgs, + std::size_t... IFieldsArgs> + message( + std::piecewise_construct_t, + std::tuple<BodyArgs...>& body_args, + std::tuple<FieldsArgs...>& fields_args, + beast::detail::index_sequence<IBodyArgs...>, + beast::detail::index_sequence<IFieldsArgs...>) + : header_type(std::forward<FieldsArgs>( + std::get<IFieldsArgs>(fields_args))...) + , beast::detail::empty_base_optimization< + typename Body::value_type>( + std::forward<BodyArgs>( + std::get<IBodyArgs>(body_args))...) + { + boost::ignore_unused(body_args); + boost::ignore_unused(fields_args); + } + + bool + need_eof(std::true_type) const + { + return ! keep_alive(); + } + + bool + need_eof(std::false_type) const; + + boost::optional<std::uint64_t> + payload_size(std::true_type) const + { + return Body::size(this->body()); + } + + boost::optional<std::uint64_t> + payload_size(std::false_type) const + { + return boost::none; + } + + void + prepare_payload(std::true_type); + + void + prepare_payload(std::false_type); +}; + +/// A typical HTTP request +template<class Body, class Fields = fields> +using request = message<true, Body, Fields>; + +/// A typical HTTP response +template<class Body, class Fields = fields> +using response = message<false, Body, Fields>; + +//------------------------------------------------------------------------------ + +#if BOOST_BEAST_DOXYGEN +/** Swap two header objects. + + @par Requirements + `Fields` is @b Swappable. +*/ +template<bool isRequest, class Fields> +void +swap( + header<isRequest, Fields>& m1, + header<isRequest, Fields>& m2); +#endif + +/** Swap two message objects. + + @par Requirements: + `Body::value_type` and `Fields` are @b Swappable. +*/ +template<bool isRequest, class Body, class Fields> +void +swap( + message<isRequest, Body, Fields>& m1, + message<isRequest, Body, Fields>& m2); + +} // http +} // beast +} // boost + +#include <boost/beast/http/impl/message.ipp> + +#endif |