Refactor message and message_headers declarations:

message_headers is now a set of partial class template
specializations instead of a template class alias. This solves
a problem where template functions taking message_headers as a
parameter could not deduce argument types, since std::conditional
obscured the deduced context.

Both classes are refactored to share declarations using an #ifdef,
to eliminate an ugly set of extra declarations needed when building
the documentation.

Copy and move class special members are added.

A new function message::base() is provided which returns the
message_headers portion of a message.
This commit is contained in:
Vinnie Falco
2016-11-07 12:20:39 -05:00
parent 595c5e0b6b
commit 50bc9a58cd
8 changed files with 349 additions and 228 deletions

View File

@ -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

View File

@ -1052,10 +1052,26 @@
</xsl:for-each>
<xsl:text>]&#xd;</xsl:text>
</xsl:if>
<xsl:if test="count(sectiondef[@kind='public-attrib' or @kind='public-static-attrib']) > 0">
<xsl:if test="count(sectiondef[@kind='public-static-attrib']) &gt; 0">
<xsl:text>[heading Static Data Members]&#xd;</xsl:text>
<xsl:text>[table&#xd; [[Name][Description]]&#xd;</xsl:text>
<xsl:for-each select="sectiondef[@kind='public-static-attrib']/memberdef" mode="class-table">
<xsl:sort select="name"/>
<xsl:text> [&#xd;</xsl:text>
<xsl:text> [[link beast.ref.</xsl:text>
<xsl:value-of select="$class-id"/>.<xsl:value-of select="name"/>
<xsl:text> [*</xsl:text>
<xsl:value-of select="name"/>
<xsl:text>]]]&#xd; [&#xd; </xsl:text>
<xsl:value-of select="briefdescription"/>
<xsl:text>&#xd; ]&#xd; ]&#xd;</xsl:text>
</xsl:for-each>
<xsl:text>]&#xd;</xsl:text>
</xsl:if>
<xsl:if test="count(sectiondef[@kind='public-attrib']) &gt; 0">
<xsl:text>[heading Data Members]&#xd;</xsl:text>
<xsl:text>[table&#xd; [[Name][Description]]&#xd;</xsl:text>
<xsl:for-each select="sectiondef[@kind='public-attrib' or @kind='public-static-attrib']/memberdef" mode="class-table">
<xsl:for-each select="sectiondef[@kind='public-attrib']/memberdef" mode="class-table">
<xsl:sort select="name"/>
<xsl:text> [&#xd;</xsl:text>
<xsl:text> [[link beast.ref.</xsl:text>

View File

@ -20,6 +20,43 @@
namespace beast {
namespace http {
template<class Headers>
void
swap(
message_headers<true, Headers>& m1,
message_headers<true, 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<class Headers>
void
swap(
message_headers<false, Headers>& a,
message_headers<false, 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<bool isRequest, class Body, class Headers>
void
swap(
message<isRequest, Body, Headers>& m1,
message<isRequest, Body, Headers>& m2)
{
using std::swap;
swap(m1.base(), m2.base());
swap(m1.body, m2.body);
}
template<bool isRequest, class Body, class Headers>
bool
is_keep_alive(message<isRequest, Body, Headers> const& msg)

View File

@ -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<bool isRequest, class Headers>
struct message_headers
#else
template<bool isRequest, class Headers>
struct message_headers;
template<class Headers>
struct request_headers
struct message_headers<true, Headers>
#endif
{
/// Indicates if the message is a request.
using is_request =
std::integral_constant<bool, true>;
/// 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<class... Args>
explicit
message_headers(Args&&... args);
#else
template<class Arg1, class... ArgN,
class = typename std::enable_if<
(sizeof...(ArgN) > 0) || ! std::is_convertible<
typename std::decay<Arg1>::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>(arg1),
std::forward<ArgN>(argn)...)
{
}
/** @} */
};
/** Swap two HTTP request headers.
Requirements:
Headers is Swappable.
*/
template<class Headers>
void
swap(
request_headers<Headers>& a,
request_headers<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<class Headers>
struct response_headers
struct message_headers<false, Headers>
{
/// Indicates if the message is a response.
using is_request =
std::integral_constant<bool, false>;
/// 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<class Arg1, class... ArgN,
class = typename std::enable_if<
(sizeof...(ArgN) > 0) || ! std::is_convertible<
typename std::decay<Arg1>::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>(arg1),
std::forward<ArgN>(argn)...)
{
}
/** @} */
};
/** Swap two HTTP response headers.
Requirements:
Headers is Swappable.
*/
template<class Headers>
void
swap(
response_headers<Headers>& a,
response_headers<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<bool isRequest, class Headers>
struct message_headers
{
/// Indicates if the message is a request.
using is_request =
std::integral_constant<bool, isRequest>;
/// 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<class... Args>
message_headers(Args&&... args);
};
#else
template<bool isRequest, class Headers>
using message_headers =
typename std::conditional<isRequest,
request_headers<Headers>,
response_headers<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<bool isRequest, class Body, class Headers>
struct message :
#if GENERATING_DOCS
implementation_defined
#else
message_headers<isRequest, Headers>
#endif
{
#if GENERATING_DOCS
/// Indicates if the message is a request.
using is_request =
std::integral_constant<bool, isRequest>;
/// 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<isRequest, 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<class... Un, class... Vn>
@ -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<class... Un, size_t... IUn>
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<bool isRequest, class Headers>
void
swap(
message_headers<isRequest, Headers>& m1,
message_headers<isRequest, Headers>& m2);
#endif
/** Swap two HTTP messages.
@par Requirements:
`Body` and `Headers` are @b Swappable.
*/
template<bool isRequest, class Body, class Headers>
void
swap(
message<isRequest, Body, Headers>& a,
message<isRequest, Body, Headers>& b)
{
using std::swap;
using base_type = typename message<
isRequest, Body, Headers>::base_type;
swap(static_cast<base_type&>(a),
static_cast<base_type&>(b));
swap(a.body, b.body);
}
message<isRequest, Body, Headers>& m1,
message<isRequest, Body, Headers>& m2);
/// A typical HTTP request
/// Message headers for a typical HTTP request
using request_headers = message_headers<true,
basic_headers<std::allocator<char>>>;
/// Message headers for a typical HTTP response
using response_headers = message_headers<false,
basic_headers<std::allocator<char>>>;
/// A typical HTTP request message
template<class Body,
class Headers = basic_headers<std::allocator<char>>>
using request = message<true, Body, Headers>;
/// A typical HTTP response
/// A typical HTTP response message
template<class Body,
class Headers = basic_headers<std::allocator<char>>>
using response = message<false, Body, Headers>;
//------------------------------------------------------------------------------
/** Returns `true` if a HTTP/1 message indicates a keep alive.
Undefined behavior if version is greater than 11.

View File

@ -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<class SyncWriteStream,
bool isRequest, class Headers>
void
write(SyncWriteStream& stream,
message_headers<isRequest, 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<class SyncWriteStream,
bool isRequest, class Headers>
void
write(SyncWriteStream& stream,
message_headers<isRequest, 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 <em>composed
operation</em>. 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<class AsyncWriteStream,
bool isRequest, class Headers,
class WriteHandler>
#if GENERATING_DOCS
void_or_deduced
#else
typename async_completion<
WriteHandler, void(error_code)>::result_type
#endif
async_write(AsyncWriteStream& stream,
message_headers<isRequest, 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<isRequest, Body, Headers> 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<bool isRequest, class Headers>
std::ostream&
operator<<(std::ostream& os,
message_headers<isRequest, 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.
*/

View File

@ -168,13 +168,13 @@ public:
void testHeaders()
{
{
using req_type = request_headers<headers>;
using req_type = request_headers;
static_assert(std::is_copy_constructible<req_type>::value, "");
static_assert(std::is_move_constructible<req_type>::value, "");
static_assert(std::is_copy_assignable<req_type>::value, "");
static_assert(std::is_move_assignable<req_type>::value, "");
using res_type = response_headers<headers>;
using res_type = response_headers;
static_assert(std::is_copy_constructible<res_type>::value, "");
static_assert(std::is_move_constructible<res_type>::value, "");
static_assert(std::is_copy_assignable<res_type>::value, "");
@ -183,7 +183,7 @@ public:
{
MoveHeaders h;
request_headers<MoveHeaders> r{std::move(h)};
message_headers<true, MoveHeaders> r{std::move(h)};
BEAST_EXPECT(h.moved_from);
BEAST_EXPECT(r.headers.moved_to);
request<string_body, MoveHeaders> m{std::move(r)};

View File

@ -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<bool, ! message_type::is_request::value>{});
std::integral_constant<bool, ! message_type::is_request>{});
}
void

View File

@ -64,7 +64,7 @@ public:
streambuf rb;
headers_parser_v1<true, headers> p0;
parse(ss, rb, p0);
request_headers<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);