diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a498eb4..d6798571 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ 1.0.0-b19 +HTTP + WebSocket * Optimize utf8 validation * Optimize mask operations +API Changes: + +* Refactor message and message_headers declarations + -------------------------------------------------------------------------------- 1.0.0-b18 diff --git a/doc/reference.xsl b/doc/reference.xsl index d0f2d990..b163e93f 100644 --- a/doc/reference.xsl +++ b/doc/reference.xsl @@ -1052,10 +1052,26 @@ ] - + + [heading Static Data Members] + [table [[Name][Description]] + + + [ + [[link beast.ref. + . + [* + + ]]] [ + + ] ] + + ] + + [heading Data Members] [table [[Name][Description]] - + [ [[link beast.ref. diff --git a/include/beast/http/impl/message.ipp b/include/beast/http/impl/message.ipp index 6d853587..7c29bf48 100644 --- a/include/beast/http/impl/message.ipp +++ b/include/beast/http/impl/message.ipp @@ -20,6 +20,43 @@ namespace beast { namespace http { +template +void +swap( + message_headers& m1, + message_headers& m2) +{ + using std::swap; + swap(m1.version, m2.version); + swap(m1.method, m2.method); + swap(m1.url, m2.url); + swap(m1.headers, m2.headers); +} + +template +void +swap( + message_headers& a, + message_headers& b) +{ + using std::swap; + swap(a.version, b.version); + swap(a.status, b.status); + swap(a.reason, b.reason); + swap(a.headers, b.headers); +} + +template +void +swap( + message& m1, + message& m2) +{ + using std::swap; + swap(m1.base(), m2.base()); + swap(m1.body, m2.body); +} + template bool is_keep_alive(message const& msg) diff --git a/include/beast/http/message.hpp b/include/beast/http/message.hpp index 406e8c1b..8a136abd 100644 --- a/include/beast/http/message.hpp +++ b/include/beast/http/message.hpp @@ -18,7 +18,8 @@ namespace beast { namespace http { -/** A container for HTTP request headers. +#if GENERATING_DOCS +/** A container for HTTP request or response headers. The container includes the headers, as well as the request method and URL. Objects of this type may be @@ -27,12 +28,24 @@ namespace http { example, when receiving a request with the header value "Expect: 100-continue". */ +template +struct message_headers + +#else +template +struct message_headers; + template -struct request_headers +struct message_headers +#endif { - /// Indicates if the message is a request. - using is_request = - std::integral_constant; + /// Indicates if the message headers are a request or response. +#if GENERATING_DOCS + static bool constexpr is_request = isRequest; + +#else + static bool constexpr is_request = true; +#endif /// The type representing the headers. using headers_type = Headers; @@ -48,57 +61,65 @@ struct request_headers */ int version; - /// The Request Method. + /** The Request Method + + @note This field is present only if `isRequest == true`. + */ std::string method; - /// The Request URI. + /** The Request URI + + @note This field is present only if `isRequest == true`. + */ std::string url; - /// The HTTP headers. + /// The HTTP field values. Headers headers; - /** Construct HTTP request headers. + /// Default constructor + message_headers() = default; - Arguments, if any, are forwarded to the constructor - of the headers member. + /// Move constructor + message_headers(message_headers&&) = default; + + /// Copy constructor + message_headers(message_headers const&) = default; + + /// Move assignment + message_headers& operator=(message_headers&&) = default; + + /// Copy assignment + message_headers& operator=(message_headers const&) = default; + + /** Construct message headers. + + All arguments are forwarded to the constructor + of the `headers` member. + + @note This constructor participates in overload resolution + if and only if the first parameter is not convertible to + `message_headers`. */ - /** @{ */ - request_headers() = default; +#if GENERATING_DOCS + template + explicit + message_headers(Args&&... args); +#else template 0) || ! std::is_convertible< typename std::decay::type, - request_headers>::value>::type> + message_headers>::value>::type> explicit - request_headers(Arg1&& arg1, ArgN&&... argn) + message_headers(Arg1&& arg1, ArgN&&... argn) : headers(std::forward(arg1), std::forward(argn)...) { } - /** @} */ }; -/** Swap two HTTP request headers. - - Requirements: - - Headers is Swappable. -*/ -template -void -swap( - request_headers& a, - request_headers& b) -{ - using std::swap; - swap(a.version, b.version); - swap(a.method, b.method); - swap(a.url, b.url); - swap(a.headers, b.headers); -} - -/** A container for HTTP response headers. +/** A container for HTTP request or response headers. The container includes the headers, as well as the response status and reasons. Objects of this type may @@ -107,11 +128,10 @@ swap( example, when responding to a HEAD request. */ template -struct response_headers +struct message_headers { - /// Indicates if the message is a response. - using is_request = - std::integral_constant; + /// Indicates if the message headers are a request or response. + static bool constexpr is_request = false; /// The type representing the headers. using headers_type = Headers; @@ -127,94 +147,45 @@ struct response_headers */ int version; - /// The Response Status-Code. - int status; - - /** The Response Reason-Phrase. - - The Reason-Phrase is obsolete as of rfc7230. - */ - std::string reason; - - /// The HTTP headers. + /// The HTTP field values. Headers headers; - /** Construct HTTP request headers. + /// Default constructor + message_headers() = default; - Arguments, if any, are forwarded to the constructor - of the headers member. + /// Move constructor + message_headers(message_headers&&) = default; + + /// Copy constructor + message_headers(message_headers const&) = default; + + /// Move assignment + message_headers& operator=(message_headers&&) = default; + + /// Copy assignment + message_headers& operator=(message_headers const&) = default; + + /** Construct message headers. + + All arguments are forwarded to the constructor + of the `headers` member. + + @note This constructor participates in overload resolution + if and only if the first parameter is not convertible to + `message_headers`. */ - /** @{ */ - response_headers() = default; - template 0) || ! std::is_convertible< typename std::decay::type, - response_headers>::value>::type> + message_headers>::value>::type> explicit - response_headers(Arg1&& arg1, ArgN&&... argn) + message_headers(Arg1&& arg1, ArgN&&... argn) : headers(std::forward(arg1), std::forward(argn)...) { } - /** @} */ -}; - -/** Swap two HTTP response headers. - - Requirements: - - Headers is Swappable. -*/ -template -void -swap( - response_headers& a, - response_headers& b) -{ - using std::swap; - swap(a.version, b.version); - swap(a.status, b.status); - swap(a.reason, b.reason); - swap(a.headers, b.headers); -} - -/** A container for HTTP request or response headers. -*/ -#if GENERATING_DOCS -template -struct message_headers -{ - /// Indicates if the message is a request. - using is_request = - std::integral_constant; - - /// The type representing the headers. - using headers_type = Headers; - - /** The HTTP version. - - This holds both the major and minor version numbers, - using these formulas: - @code - major = version / 10; - minor = version % 10; - @endcode - */ - int version; - - /** The Request Method. - - @note This field is present only if `isRequest == true`. - */ - std::string method; - - /** The Request-URI. - - @note This field is present only if `isRequest == true`. - */ - std::string url; +#endif /** The Response Status-Code. @@ -229,29 +200,9 @@ struct message_headers @note This field is present only if `isRequest == false`. */ std::string reason; - - /// The HTTP headers. - Headers headers; - - /** Construct message headers. - - Any provided arguments are forwarded to the - constructor of the headers member. - */ - template - message_headers(Args&&... args); }; -#else -template -using message_headers = - typename std::conditional, - response_headers>::type; - -#endif - -/** A complete HTTP message. +/** A container for a complete HTTP message. A message can be a request or response, depending on the `isRequest` template argument value. Requests and responses have different types, @@ -260,93 +211,35 @@ using message_headers = The `Body` template argument type determines the model used to read or write the content body of the message. - @tparam isRequest `true` if this is a request. + @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 Headers A type meeting the requirements of Headers. + @tparam Headers The type of container used to hold the + field value pairs. */ template struct message : -#if GENERATING_DOCS - implementation_defined -#else message_headers -#endif { -#if GENERATING_DOCS - /// Indicates if the message is a request. - using is_request = - std::integral_constant; - - /// The type representing the headers. - using headers_type = Headers; - - /** The type controlling the body traits. - - The body member will be of type `body_type::value_type`. - */ - using body_type = Body; - - /** The HTTP version. - - This holds both the major and minor version numbers, - using these formulas: - @code - major = version / 10; - minor = version % 10; - @endcode - */ - int version; - - /** The Request Method. - - @note This field is present only if `isRequest == true`. - */ - std::string method; - - /** The Request-URI. - - @note This field is present only if `isRequest == true`. - */ - std::string url; - - /** The Response Status-Code. - - @note This field is present only if `isRequest == false`. - */ - int status; - - /** The Response Reason-Phrase. - - The Reason-Phrase is obsolete as of rfc7230. - - @note This field is present only if `isRequest == false`. - */ - std::string reason; - - /// The HTTP headers. - Headers headers; - -#else - /// The container used to hold the request or response headers + /// The base class used to hold the request or response headers using base_type = message_headers; - /** The type controlling the body traits. + /** The type providing the body traits. - The body member will be of type `body_type::value_type`. + The `body` member will be of type `body_type::value_type`. */ using body_type = Body; -#endif - - /// A container representing the body. + /// A value representing the body. typename Body::value_type body; /// Default constructor message() = default; - /** Construct a message from headers. + /** Construct a message from message headers. Additional arguments, if any, are forwarded to the constructor of the body member. @@ -359,7 +252,7 @@ struct message : { } - /** Construct a message from headers. + /** Construct a message from message headers. Additional arguments, if any, are forwarded to the constructor of the body member. @@ -394,6 +287,7 @@ struct message : /** Construct a message. @param u An argument forwarded to the body constructor. + @param v An argument forwarded to the headers constructor. @note This constructor participates in overload resolution @@ -425,6 +319,7 @@ struct message : /** Construct a message. @param un A tuple forwarded as a parameter pack to the body constructor. + @param vn A tuple forwarded as a parameter pack to the headers constructor. */ template @@ -436,6 +331,20 @@ struct message : { } + /// Returns the message headers portion of the message + base_type& + base() + { + return *this; + } + + /// Returns the message headers portion of the message + base_type const& + base() const + { + return *this; + } + private: template message(std::piecewise_construct_t, @@ -456,31 +365,52 @@ private: } }; -/// Swap one message for another message. +//------------------------------------------------------------------------------ + +#if GENERATING_DOCS +/** Swap two HTTP message headers. + + @par Requirements + `Headers` is @b Swappable. +*/ +template +void +swap( + message_headers& m1, + message_headers& m2); +#endif + +/** Swap two HTTP messages. + + @par Requirements: + `Body` and `Headers` are @b Swappable. +*/ template void swap( - message& a, - message& b) -{ - using std::swap; - using base_type = typename message< - isRequest, Body, Headers>::base_type; - swap(static_cast(a), - static_cast(b)); - swap(a.body, b.body); -} + message& m1, + message& m2); -/// A typical HTTP request +/// Message headers for a typical HTTP request +using request_headers = message_headers>>; + +/// Message headers for a typical HTTP response +using response_headers = message_headers>>; + +/// A typical HTTP request message template>> using request = message; -/// A typical HTTP response +/// A typical HTTP response message template>> using response = message; +//------------------------------------------------------------------------------ + /** Returns `true` if a HTTP/1 message indicates a keep alive. Undefined behavior if version is greater than 11. diff --git a/include/beast/http/write.hpp b/include/beast/http/write.hpp index 6bc57ad3..15a7bd12 100644 --- a/include/beast/http/write.hpp +++ b/include/beast/http/write.hpp @@ -17,6 +17,120 @@ namespace beast { namespace http { +/** Write HTTP/1 message headers on a stream. + + This function is used to write message headers to a stream. The + call will block until one of the following conditions is true: + + @li All the message headers are sent. + + @li An error occurs. + + This operation is implemented in terms of one or more calls + to the stream's `write_some` function. + + Regardless of the semantic meaning of the headers (for example, + specifying a zero-length message body and Connection: Close), + this function will not return `boost::asio::error::eof`. + + @param stream The stream to which the data is to be written. + The type must support the @b `SyncWriteStream` concept. + + @param msg The message headers to write. + + @throws system_error Thrown on failure. +*/ +template +void +write(SyncWriteStream& stream, + message_headers const& msg); + +/** Write HTTP/1 message headers on a stream. + + This function is used to write message headers to a stream. The + call will block until one of the following conditions is true: + + @li All the message headers are sent. + + @li An error occurs. + + This operation is implemented in terms of one or more calls + to the stream's `write_some` function. + + Regardless of the semantic meaning of the headers (for example, + specifying a zero-length message body and Connection: Close), + this function will not return `boost::asio::error::eof`. + + @param stream The stream to which the data is to be written. + The type must support the @b `SyncWriteStream` concept. + + @param msg The message headers to write. + + @param ec Set to the error, if any occurred. +*/ +template +void +write(SyncWriteStream& stream, + message_headers const& msg, + error_code& ec); + +/** Start an asynchronous operation to write HTTP/1 message headers to a stream. + + This function is used to asynchronously write message headers to a stream. + The function call always returns immediately. The asynchronous + operation will continue until one of the following conditions is true: + + @li The entire message headers are sent. + + @li An error occurs. + + This operation is implemented in terms of one or more calls to the + stream's `async_write_some` functions, and is known as a composed + operation. The program must ensure that the stream performs no + other write operations (such as @ref async_write, the stream's + `async_write_some` function, or any other composed operations that + perform writes) until this operation completes. + + Regardless of the semantic meaning of the headers (for example, + specifying a zero-length message body and Connection: Close), + the handler will not be called with `boost::asio::error::eof`. + + @param stream The stream to which the data is to be written. + The type must support the @b `AsyncWriteStream` concept. + + @param msg The message headers to send. + + @param handler The handler to be called when the request completes. + Copies will be made of the handler as required. The equivalent + function signature of the handler must be: + @code void handler( + error_code const& error // result of operation + ); @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_service::post`. + + @note The message object must remain valid at least until the + completion handler is called, no copies are made. +*/ +template +#if GENERATING_DOCS +void_or_deduced +#else +typename async_completion< + WriteHandler, void(error_code)>::result_type +#endif +async_write(AsyncWriteStream& stream, + message_headers const& msg, + WriteHandler&& handler); + +//------------------------------------------------------------------------------ + /** Write a HTTP/1 message on a stream. This function is used to write a message to a stream. The call @@ -135,7 +249,24 @@ async_write(AsyncWriteStream& stream, message const& msg, WriteHandler&& handler); -/** Serialize a HTTP/1 message to an ostream. +//------------------------------------------------------------------------------ + +/** Serialize HTTP/1 message headers to a `std::ostream`. + + The function converts the message headers to its HTTP/1 + serialized representation and stores the result in the output + stream. + + @param os The output stream to write to. + + @param msg The message headers to write. +*/ +template +std::ostream& +operator<<(std::ostream& os, + message_headers const& msg); + +/** Serialize a HTTP/1 message to a `std::ostream`. The function converts the message to its HTTP/1 serialized representation and stores the result in the output stream. @@ -143,7 +274,7 @@ async_write(AsyncWriteStream& stream, The implementation will automatically perform chunk encoding if the contents of the message indicate that chunk encoding is required. - @param os The ostream to write to. + @param os The output stream to write to. @param msg The message to write. */ diff --git a/test/http/message.cpp b/test/http/message.cpp index 06689155..e01ad2e5 100644 --- a/test/http/message.cpp +++ b/test/http/message.cpp @@ -168,13 +168,13 @@ public: void testHeaders() { { - using req_type = request_headers; + using req_type = request_headers; static_assert(std::is_copy_constructible::value, ""); static_assert(std::is_move_constructible::value, ""); static_assert(std::is_copy_assignable::value, ""); static_assert(std::is_move_assignable::value, ""); - using res_type = response_headers; + using res_type = response_headers; static_assert(std::is_copy_constructible::value, ""); static_assert(std::is_move_constructible::value, ""); static_assert(std::is_copy_assignable::value, ""); @@ -183,7 +183,7 @@ public: { MoveHeaders h; - request_headers r{std::move(h)}; + message_headers r{std::move(h)}; BEAST_EXPECT(h.moved_from); BEAST_EXPECT(r.headers.moved_to); request m{std::move(r)}; diff --git a/test/http/nodejs_parser.hpp b/test/http/nodejs_parser.hpp index 76110050..3dd98ad6 100644 --- a/test/http/nodejs_parser.hpp +++ b/test/http/nodejs_parser.hpp @@ -818,7 +818,8 @@ private: { return on_request(method, url, major, minor, keep_alive, upgrade, - typename message_type::is_request{}); + std::integral_constant< + bool, message_type::is_request>{}); } bool @@ -847,7 +848,7 @@ private: { return on_response( status, reason, major, minor, keep_alive, upgrade, - std::integral_constant{}); + std::integral_constant{}); } void diff --git a/test/http/parser_v1.cpp b/test/http/parser_v1.cpp index 7717d8ce..02075d86 100644 --- a/test/http/parser_v1.cpp +++ b/test/http/parser_v1.cpp @@ -64,7 +64,7 @@ public: streambuf rb; headers_parser_v1 p0; parse(ss, rb, p0); - request_headers const& reqh = p0.get(); + request_headers const& reqh = p0.get(); BEAST_EXPECT(reqh.method == "GET"); BEAST_EXPECT(reqh.url == "/"); BEAST_EXPECT(reqh.version == 11);