Refactor header status, reason, and target (API Change):

* header::result is a family of functions to replace header::status

* header interface now uses status enum and also ints

* reason-phrase is no longer stored unless the user explicitly
  requests it.

* When serializing, the standard reason is used for the
  corresponding status code unless the user has changed it.
This commit is contained in:
Vinnie Falco
2017-06-04 12:38:42 -07:00
parent 4f91e632bc
commit 3803c38dbd
30 changed files with 588 additions and 220 deletions

View File

@ -4,6 +4,8 @@ API Changes:
* Refactor method and verb
* Canonicalize string_view parameter types
* Tidy up empty_body writer error
* Refactor header status, reason, and target
--------------------------------------------------------------------------------

View File

@ -147,8 +147,7 @@ objects:
```
response<string_body> res;
res.version = 11; // HTTP/1.1
res.status = 200;
res.reason("OK");
res.result(status::ok);
res.body = "Hello, world!";
res.fields.insert("Server", "Beast");
res.fields.insert("Content-Length", res.body.size());

View File

@ -128,8 +128,7 @@ the number of octets received prior to the server closing the connection:
{
response<string_body> res;
res.version = 11;
res.status = 200;
res.reason("OK");
res.result(status::ok);
res.fields.insert("Server", "Beast");
res.body = "Hello, world!";

View File

@ -164,7 +164,7 @@ send_expect_100_continue(
{
response<string_body> res;
read(stream, buffer, res);
if(res.status != 100)
if(res.result() != status::continue_)
{
// The server indicated that it will not
// accept the request, so skip sending the body.
@ -229,7 +229,7 @@ send_cgi_response(
// allowing serialization to use manually provided buffers.
message<false, buffer_body, fields> res;
res.status = 200;
res.result(status::ok);
res.version = 11;
res.fields.insert("Server", "Beast");
res.fields.insert("Transfer-Encoding", "chunked");

View File

@ -197,8 +197,7 @@ receive_expect_100_continue(
// send 100 response
response<empty_body> res;
res.version = 11;
res.status = 100;
res.reason("Continue");
res.result(status::continue_);
res.fields.insert("Server", "test");
write(stream, res, ec);
if(ec)

View File

@ -56,7 +56,7 @@ class custom_parser
/// Called after receiving the start-line (isRequest == false).
void
on_response(
int status, // The status-code
int code, // The status-code
string_view reason, // The obsolete reason-phrase
int version, // The HTTP-version
error_code& ec); // The error returned to the caller, if any

View File

@ -256,13 +256,13 @@ struct header<false, Fields>
string_view
reason() const
{
return fields.reason();
return fields.reason_impl();
}
void
reason(string_view s)
{
fields.reason(s);
fields.reason_impl(s);
}
Fields fields;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -88,6 +88,7 @@
<member><link linkend="beast.ref.http__connection">connection</link></member>
<member><link linkend="beast.ref.http__error">error</link></member>
<member><link linkend="beast.ref.http__status">status</link></member>
<member><link linkend="beast.ref.http__status_class">status_class</link></member>
<member><link linkend="beast.ref.http__verb">verb</link></member>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead>

View File

@ -234,8 +234,7 @@ private:
if(! boost::filesystem::exists(path))
{
response<string_body> res;
res.status = 404;
res.reason("Not Found");
res.result(status::not_found);
res.version = req_.version;
res.fields.insert("Server", "http_async_server");
res.fields.insert("Content-Type", "text/html");
@ -249,8 +248,7 @@ private:
try
{
resp_type res;
res.status = 200;
res.reason("OK");
res.result(status::ok);
res.version = req_.version;
res.fields.insert("Server", "http_async_server");
res.fields.insert("Content-Type", mime_type(path));
@ -263,8 +261,7 @@ private:
catch(std::exception const& e)
{
response<string_body> res;
res.status = 500;
res.reason("Internal Error");
res.result(status::internal_server_error);
res.version = req_.version;
res.fields.insert("Server", "http_async_server");
res.fields.insert("Content-Type", "text/html");

View File

@ -164,8 +164,7 @@ private:
if(! boost::filesystem::exists(path))
{
response<string_body> res;
res.status = 404;
res.reason("Not Found");
res.result(status::not_found);
res.version = req.version;
res.fields.insert("Server", "http_sync_server");
res.fields.insert("Content-Type", "text/html");
@ -179,7 +178,7 @@ private:
try
{
resp_type res;
res.status = 200;
res.result(status::ok);
res.reason("OK");
res.version = req.version;
res.fields.insert("Server", "http_sync_server");
@ -193,7 +192,7 @@ private:
catch(std::exception const& e)
{
response<string_body> res;
res.status = 500;
res.result(status::internal_server_error);
res.reason("Internal Error");
res.version = req.version;
res.fields.insert("Server", "http_sync_server");

View File

@ -286,33 +286,48 @@ private:
#endif
string_view
method_string() const;
method_impl() const
{
return (*this)[":method"];
}
void
method_string(string_view s);
method_impl(string_view s)
{
if(s.empty())
this->erase(":method");
else
this->replace(":method", s);
}
string_view
target() const
target_impl() const
{
return (*this)[":target"];
}
void
target(string_view s)
target_impl(string_view s)
{
return this->replace(":target", s);
if(s.empty())
this->erase(":target");
else
return this->replace(":target", s);
}
string_view
reason() const
reason_impl() const
{
return (*this)[":reason"];
}
void
reason(string_view s)
reason_impl(string_view s)
{
return this->replace(":reason", s);
if(s.empty())
this->erase(":reason");
else
this->replace(":reason", s);
}
};

View File

@ -42,6 +42,8 @@ public:
case error::end_of_stream: return "end of stream";
case error::partial_message: return "partial message";
case error::need_more: return "need more";
case error::unexpected_body: return "unexpected body";
case error::need_buffer: return "need buffer";
case error::buffer_overflow: return "buffer overflow";
case error::bad_line_ending: return "bad line ending";
case error::bad_method: return "bad method";

View File

@ -230,8 +230,7 @@ erase(string_view name)
template<class Allocator>
void
basic_fields<Allocator>::
insert(string_view name,
string_view value)
insert(string_view name, string_view value)
{
value = detail::trim(value);
auto const p = alloc_traits::allocate(this->member(), 1);
@ -243,33 +242,13 @@ insert(string_view name,
template<class Allocator>
void
basic_fields<Allocator>::
replace(string_view name,
string_view value)
replace(string_view name, string_view value)
{
value = detail::trim(value);
erase(name);
insert(name, value);
}
template<class Allocator>
string_view
basic_fields<Allocator>::
method_string() const
{
return (*this)[":method"];
}
template<class Allocator>
void
basic_fields<Allocator>::
method_string(string_view s)
{
if(s.empty())
this->erase(":method");
else
this->replace(":method", s);
}
} // http
} // beast

View File

@ -30,7 +30,7 @@ get_method_string() const
{
if(method_ != verb::unknown)
return to_string(method_);
return fields.method_string();
return fields.method_impl();
}
template<class Fields>
@ -43,7 +43,7 @@ set_method(verb v)
BOOST_THROW_EXCEPTION(
std::invalid_argument{"unknown verb"});
method_ = v;
fields.method_string({});
fields.method_impl({});
}
template<class Fields>
@ -54,9 +54,21 @@ set_method(string_view s)
{
method_ = string_to_verb(s);
if(method_ != verb::unknown)
fields.method_string({});
fields.method_impl({});
else
fields.method_string(s);
fields.method_impl(s);
}
template<class Fields>
template<class>
string_view
header<false, Fields>::
get_reason() const
{
auto const s = fields.reason_impl();
if(! s.empty())
return s;
return obsolete_reason(result_);
}
template<class Fields>
@ -79,8 +91,8 @@ swap(
{
using std::swap;
swap(a.version, b.version);
swap(a.status, b.status);
swap(a.fields, b.fields);
swap(a.result_, b.result_);
}
template<bool isRequest, class Body, class Fields>
@ -242,9 +254,9 @@ prepare(message<isRequest, Body, Fields>& msg,
operator()(message<false, Body, Fields>& msg,
detail::prepare_info const& pi) const
{
if((msg.status / 100 ) != 1 &&
msg.status != 204 &&
msg.status != 304)
if(to_status_class(msg.result()) != status_class::informational &&
msg.result() != status::no_content &&
msg.result() != status::not_modified)
{
msg.fields.insert(
"Content-Length", *pi.content_length);

View File

@ -23,6 +23,7 @@ void
write_start_line(std::ostream& os,
header<true, Fields> const& msg)
{
// VFALCO This should all be done without dynamic allocation
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
os << msg.method() << " " << msg.target();
switch(msg.version)
@ -37,18 +38,14 @@ void
write_start_line(std::ostream& os,
header<false, Fields> const& msg)
{
// VFALCO This should all be done without dynamic allocation
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
switch(msg.version)
{
case 10: os << "HTTP/1.0 "; break;
case 11: os << "HTTP/1.1 "; break;
}
auto const reason = msg.reason();
if(reason.empty())
os << msg.status << " " << msg.reason() << "\r\n";
else
os << msg.status << " " <<
obsolete_reason(static_cast<status>(msg.status)) << "\r\n";
os << msg.result_int() << " " << msg.reason() << "\r\n";
}
template<class FieldSequence>

View File

@ -8,10 +8,101 @@
#ifndef BEAST_HTTP_IMPL_STATUS_IPP
#define BEAST_HTTP_IMPL_STATUS_IPP
#include <boost/throw_exception.hpp>
namespace beast {
namespace http {
namespace detail {
template<class = void>
status
int_to_status(int v)
{
switch(static_cast<status>(v))
{
// 1xx
case status::continue_:
case status::switching_protocols:
case status::processing:
// [[fallthrough]]
// 2xx
case status::ok:
case status::created:
case status::accepted:
case status::non_authoritative_information:
case status::no_content:
case status::reset_content:
case status::partial_content:
case status::multi_status:
case status::already_reported:
case status::im_used:
// [[fallthrough]]
// 3xx
case status::multiple_choices:
case status::moved_permanently:
case status::found:
case status::see_other:
case status::not_modified:
case status::use_proxy:
case status::temporary_redirect:
case status::permanent_redirect:
// [[fallthrough]]
// 4xx
case status::bad_request:
case status::unauthorized:
case status::payment_required:
case status::forbidden:
case status::not_found:
case status::method_not_allowed:
case status::not_acceptable:
case status::proxy_authentication_required:
case status::request_timeout:
case status::conflict:
case status::gone:
case status::length_required:
case status::precondition_failed:
case status::payload_too_large:
case status::uri_too_long:
case status::unsupported_media_type:
case status::range_not_satisfiable:
case status::expectation_failed:
case status::misdirected_request:
case status::unprocessable_entity:
case status::locked:
case status::failed_dependency:
case status::upgrade_required:
case status::precondition_required:
case status::too_many_requests:
case status::request_header_fields_too_large:
case status::connection_closed_without_response:
case status::unavailable_for_legal_reasons:
case status::client_closed_request:
// [[fallthrough]]
// 5xx
case status::internal_server_error:
case status::not_implemented:
case status::bad_gateway:
case status::service_unavailable:
case status::gateway_timeout:
case status::http_version_not_supported:
case status::variant_also_negotiates:
case status::insufficient_storage:
case status::loop_detected:
case status::not_extended:
case status::network_authentication_required:
case status::network_connect_timeout_error:
return static_cast<status>(v);
default:
break;
}
return status::unknown;
}
template<class = void>
string_view
status_to_string(int v)
@ -92,11 +183,49 @@ status_to_string(int v)
default:
break;
}
return "Unknown Status";
return "<unknown-status>";
}
template<class = void>
status_class
to_status_class(int v)
{
switch(v / 100)
{
case 1: return status_class::informational;
case 2: return status_class::successful;
case 3: return status_class::redirection;
case 4: return status_class::client_error;
case 5: return status_class::server_error;
default:
break;
}
return status_class::unknown;
}
} // detail
inline
status
int_to_status(int v)
{
return detail::int_to_status(v);
}
inline
status_class
to_status_class(int v)
{
return detail::to_status_class(v);
}
inline
status_class
to_status_class(status v)
{
return to_status_class(static_cast<int>(v));
}
inline
string_view
obsolete_reason(status v)

View File

@ -11,6 +11,7 @@
#include <beast/config.hpp>
#include <beast/http/fields.hpp>
#include <beast/http/verb.hpp>
#include <beast/http/status.hpp>
#include <beast/core/string_view.hpp>
#include <beast/core/detail/integer_sequence.hpp>
#include <boost/throw_exception.hpp>
@ -58,104 +59,6 @@ struct header<true, Fields>
/// The type representing the fields.
using fields_type = Fields;
/** 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;
/** 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 if `isRequest == true`.
@see @ref method_string
*/
verb
method() const
{
return method_;
}
/** Set the request-method verb.
This function will set the method for requests to one
of the known verbs.
@param v The request method verb to set.
This may not be @ref verb::unknown.
@throw std::invalid_argument when `v == verb::unknown`.
*/
void
method(verb v)
{
set_method(v);
}
/** Return the request-method as a string.
@note This function is only available if `isRequest == true`.
@see @ref method
*/
string_view
method_string() const
{
return get_method_string();
}
/** Set the request-method using a string.
This function will set the method for requests to a verb
if the string matches a known verb, otherwise it will
store a copy of the passed string as the method.
@param s A string representing the request method.
@note This function is only available if `isRequest == true`.
*/
void
method(string_view s)
{
set_method(s);
}
/** Return the Request Target
@note This function is only available if `isRequest == true`.
*/
auto
target() const ->
decltype(std::declval<Fields>().target()) const
{
return fields.target();
}
/** Set the Request Target
@param value A value that represents the request method.
@note This function is only available if `isRequest == true`.
*/
template<class Value>
void
target(Value&& value)
{
fields.target(std::forward<Value>(value));
}
/// The HTTP field values.
fields_type fields;
/// Default constructor
header() = default;
@ -198,13 +101,106 @@ struct header<true, Fields>
{
}
/** The HTTP-version.
This holds both the major and minor version numbers,
using these formulas:
@code
int major = version / 10;
int minor = version % 10;
@endcode
*/
int version;
/// The HTTP field values.
fields_type fields;
/** 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 if `isRequest == true`.
@see @ref method_string
*/
verb
method() const
{
return method_;
}
/** Set the request-method verb.
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.
@throw std::invalid_argument when `v == verb::unknown`.
*/
void
method(verb v)
{
set_method(v);
}
/** Return the request-method string.
@note This function is only available if `isRequest == true`.
@see @ref method
*/
string_view
method_string() const
{
return get_method_string();
}
/** Set the request-method string.
This function will set the method for requests to a verb
if the string matches a known verb, otherwise it will
store a copy of the passed string as the method.
@param s A string representing the request-method.
@note This function is only available if `isRequest == true`.
*/
void
method(string_view s)
{
set_method(s);
}
/** Returns the request-target string.
@note This function is only available if `isRequest == true`.
*/
string_view
target() const
{
return fields.target_impl();
}
/** Set the request-target string.
@param s A string representing the request-target.
@note This function is only available if `isRequest == true`.
*/
void
target(string_view s)
{
fields.target_impl(s);
}
private:
template<class Fields>
template<class T>
friend
void
swap(
header<true, Fields>& m1,
header<true, Fields>& m2);
swap(header<true, T>& m1, header<true, T>& m2);
template<class = void>
string_view
@ -256,7 +252,7 @@ struct header<false, Fields>
/// The HTTP field values.
fields_type fields;
/// Default constructor
/// Default constructor.
header() = default;
/// Move constructor
@ -293,11 +289,62 @@ struct header<false, Fields>
}
#endif
/** The Response Status-Code.
/** 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 if `isRequest == false`.
*/
int status;
status
result() const
{
return int_to_status(
static_cast<int>(result_));
}
/** Set the response status-code result.
@param v The code to set.
@note This member is only available if `isRequest == false`.
*/
void
result(status v)
{
result_ = v;
}
/** Set the raw status-code result 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.
*/
void
result(int v)
{
result_ = static_cast<status>(v);
}
/** The response status-code result 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 if `isRequest == false`.
*/
int
result_int() const
{
return static_cast<int>(result_);
}
/** Return the Reason-Phrase.
@ -305,26 +352,47 @@ struct header<false, Fields>
@note This function is only available if `isRequest == false`.
*/
auto
reason() const ->
decltype(std::declval<Fields>().reason()) const
string_view
reason() const
{
return fields.reason();
return get_reason();
}
/** Set the Reason-Phrase
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 value A value that represents the reason phrase.
@note This function is only available if `isRequest == false`.
*/
template<class Value>
void
reason(Value&& value)
reason(string_view s)
{
fields.reason(std::forward<Value>(value));
fields.reason_impl(s);
}
private:
template<class T>
friend
void
swap(header<false, T>& m1, header<false, T>& m2);
template<class = void>
string_view
get_reason() const;
status result_;
};
/** A container for a complete HTTP message.

View File

@ -153,10 +153,10 @@ private:
}
void
on_response(int status, string_view reason,
on_response(int code, string_view reason,
int version, error_code&)
{
h_.status = status;
h_.result(code);
h_.version = version;
h_.reason(reason);
}
@ -341,11 +341,11 @@ private:
}
void
on_response(int status,
on_response(int code,
string_view reason,
int version, error_code&)
{
m_.status = status;
m_.result(code);
m_.version = version;
m_.reason(reason);
}

View File

@ -15,8 +15,17 @@
namespace beast {
namespace http {
enum class status : int
enum class status : unsigned short
{
/** An unknown status-code.
This value indicates that the value for the status code
is not in the list of commonly recognized status codes.
Callers interested in the exactly value should use the
interface which provides the raw integer.
*/
unknown = 0,
continue_ = 100,
switching_protocols = 101,
processing = 102,
@ -85,13 +94,66 @@ enum class status : int
network_connect_timeout_error = 599
};
/// Returns the obsolete reason-phrase text for a status code.
/** Represents the class of a status-code.
*/
enum class status_class : int
{
/// Unknown status-class
unknown = 0,
/// The request was received, continuing processing.
informational = 1,
/// The request was successfully received, understood, and accepted.
successful = 2,
/// Further action needs to be taken in order to complete the request.
redirection = 3,
/// The request contains bad syntax or cannot be fulfilled.
client_error = 4,
/// The server failed to fulfill an apparently valid request.
server_error = 5,
};
/** Converts the integer to a known status-code.
If the integer does not match a known status code,
@ref status::unknown is returned.
*/
status
int_to_status(int v);
/** Convert an integer to a status_class.
@param v The integer representing a status code.
@return The status class. If the integer does not match
a known status class, @ref status_class::unknown is returned.
*/
status_class
to_status_class(int v);
/** Convert a status_code to a status_class.
@param v The status code to convert.
@return The status class.
*/
status_class
to_status_class(status v);
/** Returns the obsolete reason-phrase text for a status code.
@param v The status code to use.
*/
string_view
obsolete_reason(status v);
/// Outputs the reason phrase of a status code to a stream.
/// Outputs the standard reason phrase of a status code to a stream.
std::ostream&
operator<<(std::ostream& os, status v);
operator<<(std::ostream&, status);
} // http
} // beast

View File

@ -128,7 +128,7 @@ enum class verb
/** Converts a string to the request method verb.
If the string does not match a known request method,
`boost::none` is returned.
@ref verb::unknown is returned.
*/
verb
string_to_verb(string_view s);

View File

@ -148,7 +148,8 @@ operator()(error_code ec, bool again)
// sent response
case 1:
d.state = 99;
if(d.res.status != 101)
if(d.res.result() !=
http::status::switching_protocols)
ec = error::handshake_failed;
if(! ec)
{

View File

@ -112,7 +112,7 @@ do_accept(http::header<true, Fields> const& req,
http::write(stream_, res, ec);
if(ec)
return;
if(res.status != 101)
if(res.result() != http::status::switching_protocols)
{
ec = error::handshake_failed;
// VFALCO TODO Respect keep alive setting, perform
@ -216,7 +216,7 @@ build_response(http::header<true, Fields> const& req,
[&](std::string const& text)
{
response_type res;
res.status = 400;
res.result(http::status::bad_request);
res.version = req.version;
res.body = text;
prepare(res);
@ -246,7 +246,7 @@ build_response(http::header<true, Fields> const& req,
if(version != "13")
{
response_type res;
res.status = 426;
res.result(http::status::upgrade_required);
res.version = req.version;
res.fields.insert("Sec-WebSocket-Version", "13");
prepare(res);
@ -263,7 +263,7 @@ build_response(http::header<true, Fields> const& req,
pmd_negotiate(
res.fields, unused, offer, pmd_opts_);
}
res.status = 101;
res.result(http::status::switching_protocols);
res.version = req.version;
res.fields.insert("Upgrade", "websocket");
res.fields.insert("Connection", "upgrade");
@ -286,7 +286,7 @@ do_response(http::header<false> const& res,
{
if(res.version < 11)
return false;
if(res.status != 101)
if(res.result() != http::status::switching_protocols)
return false;
if(! is_upgrade(res))
return false;

View File

@ -46,7 +46,7 @@ class custom_parser
/// Called after receiving the start-line (isRequest == false).
void
on_response(
int status, // The status-code
int code, // The status-code
string_view reason, // The obsolete reason-phrase
int version, // The HTTP-version
error_code& ec); // The error returned to the caller, if any
@ -238,7 +238,7 @@ public:
read(stream, buffer, res, ec);
if(ec)
return;
if(res.status != 100)
if(res.result() != status::continue_)
{
// The server indicated that it will not
// accept the request, so skip sending the body.
@ -291,8 +291,7 @@ public:
// send 100 response
response<empty_body> res;
res.version = 11;
res.status = 100;
res.reason("Continue");
res.result(status::continue_);
res.fields.insert("Server", "test");
write(stream, res, ec);
if(ec)
@ -383,7 +382,7 @@ public:
// allowing serialization to use manually provided buffers.
message<false, buffer_body, fields> res;
res.status = 200;
res.result(status::ok);
res.version = 11;
res.fields.insert("Server", "Beast");
res.fields.insert("Transfer-Encoding", "chunked");

View File

@ -103,12 +103,12 @@ public:
testMethodString()
{
f_t f;
f.method_string("CRY");
BEAST_EXPECTS(f.method_string() == "CRY", f.method_string());
f.method_string("PUT");
BEAST_EXPECTS(f.method_string() == "PUT", f.method_string());
f.method_string({});
BEAST_EXPECTS(f.method_string().empty(), f.method_string());
f.method_impl("CRY");
BEAST_EXPECTS(f.method_impl() == "CRY", f.method_impl());
f.method_impl("PUT");
BEAST_EXPECTS(f.method_impl() == "PUT", f.method_impl());
f.method_impl({});
BEAST_EXPECTS(f.method_impl().empty(), f.method_impl());
}
void run() override

View File

@ -262,19 +262,20 @@ public:
{
message<false, string_body, fields> m1;
message<false, string_body, fields> m2;
m1.status = 200;
m1.result(status::ok);
m1.version = 10;
m1.body = "1";
m1.fields.insert("h", "v");
m2.status = 404;
m2.reason("OK");
m2.result(status::not_found);
m2.body = "2";
m2.version = 11;
swap(m1, m2);
BEAST_EXPECT(m1.status == 404);
BEAST_EXPECT(m2.status == 200);
BEAST_EXPECT(m1.reason() == "OK");
BEAST_EXPECT(m2.reason().empty());
BEAST_EXPECT(m1.result() == status::not_found);
BEAST_EXPECT(m1.result_int() == 404);
BEAST_EXPECT(m2.result() == status::ok);
BEAST_EXPECT(m2.result_int() == 200);
BEAST_EXPECT(m1.reason() == "Not Found");
BEAST_EXPECT(m2.reason() == "OK");
BEAST_EXPECT(m1.version == 11);
BEAST_EXPECT(m2.version == 10);
BEAST_EXPECT(m1.body == "2");
@ -321,6 +322,35 @@ public:
scheck("XYZ");
}
void
testStatus()
{
header<false> h;
h.result(200);
BEAST_EXPECT(h.result_int() == 200);
BEAST_EXPECT(h.result() == status::ok);
h.result(status::switching_protocols);
BEAST_EXPECT(h.result_int() == 101);
BEAST_EXPECT(h.result() == status::switching_protocols);
h.result(1);
BEAST_EXPECT(h.result_int() == 1);
BEAST_EXPECT(h.result() == status::unknown);
}
void
testReason()
{
header<false> h;
h.result(status::ok);
BEAST_EXPECT(h.reason() == "OK");
h.reason("Pepe");
BEAST_EXPECT(h.reason() == "Pepe");
h.result(status::not_found);
BEAST_EXPECT(h.reason() == "Pepe");
h.reason({});
BEAST_EXPECT(h.reason() == "Not Found");
}
void
run() override
{
@ -331,6 +361,8 @@ public:
testSwap();
testSpecialMembers();
testMethod();
testStatus();
testReason();
}
};

View File

@ -182,7 +182,7 @@ public:
BEAST_EXPECT(p.need_eof());
BEAST_EXPECT(p.content_length() == boost::none);
BEAST_EXPECT(m.version == 10);
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.result() == status::ok);
BEAST_EXPECT(m.reason() == "OK");
BEAST_EXPECT(m.fields["Server"] == "test");
BEAST_EXPECT(m.body == "Hello, world!");
@ -209,7 +209,7 @@ public:
BEAST_EXPECT(p.is_chunked());
BEAST_EXPECT(p.content_length() == boost::none);
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.result() == status::ok);
BEAST_EXPECT(m.reason() == "OK");
BEAST_EXPECT(m.fields["Server"] == "test");
BEAST_EXPECT(m.fields["Transfer-Encoding"] == "chunked");

View File

@ -20,6 +20,82 @@ public:
void
testStatus()
{
auto const check = [&](status s, int i, status_class sc)
{
BEAST_EXPECT(int_to_status(i) == s);
BEAST_EXPECT(to_status_class(i) == sc);
BEAST_EXPECT(to_status_class(int_to_status(i)) == sc);
};
check(status::continue_ ,100, status_class::informational);
check(status::switching_protocols ,101, status_class::informational);
check(status::processing ,102, status_class::informational);
check(status::ok ,200, status_class::successful);
check(status::created ,201, status_class::successful);
check(status::accepted ,202, status_class::successful);
check(status::non_authoritative_information ,203, status_class::successful);
check(status::no_content ,204, status_class::successful);
check(status::reset_content ,205, status_class::successful);
check(status::partial_content ,206, status_class::successful);
check(status::multi_status ,207, status_class::successful);
check(status::already_reported ,208, status_class::successful);
check(status::im_used ,226, status_class::successful);
check(status::multiple_choices ,300, status_class::redirection);
check(status::moved_permanently ,301, status_class::redirection);
check(status::found ,302, status_class::redirection);
check(status::see_other ,303, status_class::redirection);
check(status::not_modified ,304, status_class::redirection);
check(status::use_proxy ,305, status_class::redirection);
check(status::temporary_redirect ,307, status_class::redirection);
check(status::permanent_redirect ,308, status_class::redirection);
check(status::bad_request ,400, status_class::client_error);
check(status::unauthorized ,401, status_class::client_error);
check(status::payment_required ,402, status_class::client_error);
check(status::forbidden ,403, status_class::client_error);
check(status::not_found ,404, status_class::client_error);
check(status::method_not_allowed ,405, status_class::client_error);
check(status::not_acceptable ,406, status_class::client_error);
check(status::proxy_authentication_required ,407, status_class::client_error);
check(status::request_timeout ,408, status_class::client_error);
check(status::conflict ,409, status_class::client_error);
check(status::gone ,410, status_class::client_error);
check(status::length_required ,411, status_class::client_error);
check(status::precondition_failed ,412, status_class::client_error);
check(status::payload_too_large ,413, status_class::client_error);
check(status::uri_too_long ,414, status_class::client_error);
check(status::unsupported_media_type ,415, status_class::client_error);
check(status::range_not_satisfiable ,416, status_class::client_error);
check(status::expectation_failed ,417, status_class::client_error);
check(status::misdirected_request ,421, status_class::client_error);
check(status::unprocessable_entity ,422, status_class::client_error);
check(status::locked ,423, status_class::client_error);
check(status::failed_dependency ,424, status_class::client_error);
check(status::upgrade_required ,426, status_class::client_error);
check(status::precondition_required ,428, status_class::client_error);
check(status::too_many_requests ,429, status_class::client_error);
check(status::request_header_fields_too_large ,431, status_class::client_error);
check(status::connection_closed_without_response ,444, status_class::client_error);
check(status::unavailable_for_legal_reasons ,451, status_class::client_error);
check(status::client_closed_request ,499, status_class::client_error);
check(status::internal_server_error ,500, status_class::server_error);
check(status::not_implemented ,501, status_class::server_error);
check(status::bad_gateway ,502, status_class::server_error);
check(status::service_unavailable ,503, status_class::server_error);
check(status::gateway_timeout ,504, status_class::server_error);
check(status::http_version_not_supported ,505, status_class::server_error);
check(status::variant_also_negotiates ,506, status_class::server_error);
check(status::insufficient_storage ,507, status_class::server_error);
check(status::loop_detected ,508, status_class::server_error);
check(status::not_extended ,510, status_class::server_error);
check(status::network_authentication_required ,511, status_class::server_error);
check(status::network_connect_timeout_error ,599, status_class::server_error);
BEAST_EXPECT(to_status_class(1) == status_class::unknown);
BEAST_EXPECT(to_status_class(status::unknown) == status_class::unknown);
auto const good =
[&](status v)
{

View File

@ -62,11 +62,11 @@ public:
}
void
on_response(int status_,
on_response(int code,
string_view reason_,
int version_, error_code& ec)
{
status = status_;
status = code;
reason = std::string(
reason_.data(), reason_.size());
version = version_;

View File

@ -292,7 +292,7 @@ public:
{
message<false, string_body, fields> m;
m.version = 10;
m.status = 200;
m.result(status::ok);
m.reason("OK");
m.fields.insert("Server", "test");
m.fields.insert("Content-Length", "5");
@ -311,7 +311,7 @@ public:
{
message<false, string_body, fields> m;
m.version = 11;
m.status = 200;
m.result(status::ok);
m.reason("OK");
m.fields.insert("Server", "test");
m.fields.insert("Transfer-Encoding", "chunked");
@ -786,7 +786,7 @@ public:
message<false, Body, fields> m0;
m0.version = 11;
m0.status = 200;
m0.result(status::ok);
m0.reason("OK");
m0.fields.insert("Server", "test");
m0.body.s = "Hello, world!\n";