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 1.0.0-b19
HTTP
WebSocket WebSocket
* Optimize utf8 validation * Optimize utf8 validation
* Optimize mask operations * Optimize mask operations
API Changes:
* Refactor message and message_headers declarations
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
1.0.0-b18 1.0.0-b18

View File

@ -1052,10 +1052,26 @@
</xsl:for-each> </xsl:for-each>
<xsl:text>]&#xd;</xsl:text> <xsl:text>]&#xd;</xsl:text>
</xsl:if> </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>[heading Data Members]&#xd;</xsl:text>
<xsl:text>[table&#xd; [[Name][Description]]&#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:sort select="name"/>
<xsl:text> [&#xd;</xsl:text> <xsl:text> [&#xd;</xsl:text>
<xsl:text> [[link beast.ref.</xsl:text> <xsl:text> [[link beast.ref.</xsl:text>

View File

@ -20,6 +20,43 @@
namespace beast { namespace beast {
namespace http { 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> template<bool isRequest, class Body, class Headers>
bool bool
is_keep_alive(message<isRequest, Body, Headers> const& msg) is_keep_alive(message<isRequest, Body, Headers> const& msg)

View File

@ -18,7 +18,8 @@
namespace beast { namespace beast {
namespace http { 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 The container includes the headers, as well as the
request method and URL. Objects of this type may be 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 example, when receiving a request with the header value
"Expect: 100-continue". "Expect: 100-continue".
*/ */
template<bool isRequest, class Headers>
struct message_headers
#else
template<bool isRequest, class Headers>
struct message_headers;
template<class Headers> template<class Headers>
struct request_headers struct message_headers<true, Headers>
#endif
{ {
/// Indicates if the message is a request. /// Indicates if the message headers are a request or response.
using is_request = #if GENERATING_DOCS
std::integral_constant<bool, true>; static bool constexpr is_request = isRequest;
#else
static bool constexpr is_request = true;
#endif
/// The type representing the headers. /// The type representing the headers.
using headers_type = Headers; using headers_type = Headers;
@ -48,57 +61,65 @@ struct request_headers
*/ */
int version; int version;
/// The Request Method. /** The Request Method
@note This field is present only if `isRequest == true`.
*/
std::string method; std::string method;
/// The Request URI. /** The Request URI
@note This field is present only if `isRequest == true`.
*/
std::string url; std::string url;
/// The HTTP headers. /// The HTTP field values.
Headers headers; Headers headers;
/** Construct HTTP request headers. /// Default constructor
message_headers() = default;
Arguments, if any, are forwarded to the constructor /// Move constructor
of the headers member. 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`.
*/ */
/** @{ */ #if GENERATING_DOCS
request_headers() = default; template<class... Args>
explicit
message_headers(Args&&... args);
#else
template<class Arg1, class... ArgN, template<class Arg1, class... ArgN,
class = typename std::enable_if< class = typename std::enable_if<
(sizeof...(ArgN) > 0) || ! std::is_convertible< (sizeof...(ArgN) > 0) || ! std::is_convertible<
typename std::decay<Arg1>::type, typename std::decay<Arg1>::type,
request_headers>::value>::type> message_headers>::value>::type>
explicit explicit
request_headers(Arg1&& arg1, ArgN&&... argn) message_headers(Arg1&& arg1, ArgN&&... argn)
: headers(std::forward<Arg1>(arg1), : headers(std::forward<Arg1>(arg1),
std::forward<ArgN>(argn)...) std::forward<ArgN>(argn)...)
{ {
} }
/** @} */
}; };
/** Swap two HTTP request headers. /** A container for HTTP request or response 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.
The container includes the headers, as well as the The container includes the headers, as well as the
response status and reasons. Objects of this type may response status and reasons. Objects of this type may
@ -107,11 +128,10 @@ swap(
example, when responding to a HEAD request. example, when responding to a HEAD request.
*/ */
template<class Headers> template<class Headers>
struct response_headers struct message_headers<false, Headers>
{ {
/// Indicates if the message is a response. /// Indicates if the message headers are a request or response.
using is_request = static bool constexpr is_request = false;
std::integral_constant<bool, false>;
/// The type representing the headers. /// The type representing the headers.
using headers_type = Headers; using headers_type = Headers;
@ -127,94 +147,45 @@ struct response_headers
*/ */
int version; int version;
/// The Response Status-Code. /// The HTTP field values.
int status;
/** The Response Reason-Phrase.
The Reason-Phrase is obsolete as of rfc7230.
*/
std::string reason;
/// The HTTP headers.
Headers headers; Headers headers;
/** Construct HTTP request headers. /// Default constructor
message_headers() = default;
Arguments, if any, are forwarded to the constructor /// Move constructor
of the headers member. 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, template<class Arg1, class... ArgN,
class = typename std::enable_if< class = typename std::enable_if<
(sizeof...(ArgN) > 0) || ! std::is_convertible< (sizeof...(ArgN) > 0) || ! std::is_convertible<
typename std::decay<Arg1>::type, typename std::decay<Arg1>::type,
response_headers>::value>::type> message_headers>::value>::type>
explicit explicit
response_headers(Arg1&& arg1, ArgN&&... argn) message_headers(Arg1&& arg1, ArgN&&... argn)
: headers(std::forward<Arg1>(arg1), : headers(std::forward<Arg1>(arg1),
std::forward<ArgN>(argn)...) std::forward<ArgN>(argn)...)
{ {
} }
/** @} */ #endif
};
/** 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;
/** The Response Status-Code. /** The Response Status-Code.
@ -229,29 +200,9 @@ struct message_headers
@note This field is present only if `isRequest == false`. @note This field is present only if `isRequest == false`.
*/ */
std::string reason; 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 /** A container for a complete HTTP message.
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 message can be a request or response, depending on the `isRequest` A message can be a request or response, depending on the `isRequest`
template argument value. Requests and responses have different types, 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 The `Body` template argument type determines the model used
to read or write the content body of the message. 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 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> template<bool isRequest, class Body, class Headers>
struct message : struct message :
#if GENERATING_DOCS
implementation_defined
#else
message_headers<isRequest, Headers> message_headers<isRequest, Headers>
#endif
{ {
#if GENERATING_DOCS /// The base class used to hold the request or response 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 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
using base_type = message_headers<isRequest, 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; using body_type = Body;
#endif /// A value representing the body.
/// A container representing the body.
typename Body::value_type body; typename Body::value_type body;
/// Default constructor /// Default constructor
message() = default; message() = default;
/** Construct a message from headers. /** Construct a message from message headers.
Additional arguments, if any, are forwarded to Additional arguments, if any, are forwarded to
the constructor of the body member. 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 Additional arguments, if any, are forwarded to
the constructor of the body member. the constructor of the body member.
@ -394,6 +287,7 @@ struct message :
/** Construct a message. /** Construct a message.
@param u An argument forwarded to the body constructor. @param u An argument forwarded to the body constructor.
@param v An argument forwarded to the headers constructor. @param v An argument forwarded to the headers constructor.
@note This constructor participates in overload resolution @note This constructor participates in overload resolution
@ -425,6 +319,7 @@ struct message :
/** Construct a message. /** Construct a message.
@param un A tuple forwarded as a parameter pack to the body constructor. @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. @param vn A tuple forwarded as a parameter pack to the headers constructor.
*/ */
template<class... Un, class... Vn> 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: private:
template<class... Un, size_t... IUn> template<class... Un, size_t... IUn>
message(std::piecewise_construct_t, 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> template<bool isRequest, class Body, class Headers>
void void
swap( swap(
message<isRequest, Body, Headers>& a, message<isRequest, Body, Headers>& m1,
message<isRequest, Body, Headers>& b) message<isRequest, Body, Headers>& m2);
{
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);
}
/// 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, template<class Body,
class Headers = basic_headers<std::allocator<char>>> class Headers = basic_headers<std::allocator<char>>>
using request = message<true, Body, Headers>; using request = message<true, Body, Headers>;
/// A typical HTTP response /// A typical HTTP response message
template<class Body, template<class Body,
class Headers = basic_headers<std::allocator<char>>> class Headers = basic_headers<std::allocator<char>>>
using response = message<false, Body, Headers>; using response = message<false, Body, Headers>;
//------------------------------------------------------------------------------
/** Returns `true` if a HTTP/1 message indicates a keep alive. /** Returns `true` if a HTTP/1 message indicates a keep alive.
Undefined behavior if version is greater than 11. Undefined behavior if version is greater than 11.

View File

@ -17,6 +17,120 @@
namespace beast { namespace beast {
namespace http { 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. /** Write a HTTP/1 message on a stream.
This function is used to write a message to a stream. The call 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, message<isRequest, Body, Headers> const& msg,
WriteHandler&& handler); 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 The function converts the message to its HTTP/1 serialized
representation and stores the result in the output stream. 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 implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required. 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. @param msg The message to write.
*/ */

View File

@ -168,13 +168,13 @@ public:
void testHeaders() 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_copy_constructible<req_type>::value, "");
static_assert(std::is_move_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_copy_assignable<req_type>::value, "");
static_assert(std::is_move_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_copy_constructible<res_type>::value, "");
static_assert(std::is_move_constructible<res_type>::value, ""); static_assert(std::is_move_constructible<res_type>::value, "");
static_assert(std::is_copy_assignable<res_type>::value, ""); static_assert(std::is_copy_assignable<res_type>::value, "");
@ -183,7 +183,7 @@ public:
{ {
MoveHeaders h; MoveHeaders h;
request_headers<MoveHeaders> r{std::move(h)}; message_headers<true, MoveHeaders> r{std::move(h)};
BEAST_EXPECT(h.moved_from); BEAST_EXPECT(h.moved_from);
BEAST_EXPECT(r.headers.moved_to); BEAST_EXPECT(r.headers.moved_to);
request<string_body, MoveHeaders> m{std::move(r)}; request<string_body, MoveHeaders> m{std::move(r)};

View File

@ -818,7 +818,8 @@ private:
{ {
return on_request(method, url, return on_request(method, url,
major, minor, keep_alive, upgrade, major, minor, keep_alive, upgrade,
typename message_type::is_request{}); std::integral_constant<
bool, message_type::is_request>{});
} }
bool bool
@ -847,7 +848,7 @@ private:
{ {
return on_response( return on_response(
status, reason, major, minor, keep_alive, upgrade, 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 void

View File

@ -64,7 +64,7 @@ public:
streambuf rb; streambuf rb;
headers_parser_v1<true, headers> p0; headers_parser_v1<true, headers> p0;
parse(ss, rb, 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.method == "GET");
BEAST_EXPECT(reqh.url == "/"); BEAST_EXPECT(reqh.url == "/");
BEAST_EXPECT(reqh.version == 11); BEAST_EXPECT(reqh.version == 11);