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 * Refactor method and verb
* Canonicalize string_view parameter types * 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; response<string_body> res;
res.version = 11; // HTTP/1.1 res.version = 11; // HTTP/1.1
res.status = 200; res.result(status::ok);
res.reason("OK");
res.body = "Hello, world!"; res.body = "Hello, world!";
res.fields.insert("Server", "Beast"); res.fields.insert("Server", "Beast");
res.fields.insert("Content-Length", res.body.size()); 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; response<string_body> res;
res.version = 11; res.version = 11;
res.status = 200; res.result(status::ok);
res.reason("OK");
res.fields.insert("Server", "Beast"); res.fields.insert("Server", "Beast");
res.body = "Hello, world!"; res.body = "Hello, world!";

View File

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

View File

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

View File

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

View File

@@ -256,13 +256,13 @@ struct header<false, Fields>
string_view string_view
reason() const reason() const
{ {
return fields.reason(); return fields.reason_impl();
} }
void void
reason(string_view s) reason(string_view s)
{ {
fields.reason(s); fields.reason_impl(s);
} }
Fields fields; 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__connection">connection</link></member>
<member><link linkend="beast.ref.http__error">error</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">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> <member><link linkend="beast.ref.http__verb">verb</link></member>
</simplelist> </simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead> <bridgehead renderas="sect3">Type Traits</bridgehead>

View File

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

View File

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

View File

@@ -286,33 +286,48 @@ private:
#endif #endif
string_view string_view
method_string() const; method_impl() const
{
return (*this)[":method"];
}
void void
method_string(string_view s); method_impl(string_view s)
{
if(s.empty())
this->erase(":method");
else
this->replace(":method", s);
}
string_view string_view
target() const target_impl() const
{ {
return (*this)[":target"]; return (*this)[":target"];
} }
void 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 string_view
reason() const reason_impl() const
{ {
return (*this)[":reason"]; return (*this)[":reason"];
} }
void 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::end_of_stream: return "end of stream";
case error::partial_message: return "partial message"; case error::partial_message: return "partial message";
case error::need_more: return "need more"; 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::buffer_overflow: return "buffer overflow";
case error::bad_line_ending: return "bad line ending"; case error::bad_line_ending: return "bad line ending";
case error::bad_method: return "bad method"; case error::bad_method: return "bad method";

View File

@@ -230,8 +230,7 @@ erase(string_view name)
template<class Allocator> template<class Allocator>
void void
basic_fields<Allocator>:: basic_fields<Allocator>::
insert(string_view name, insert(string_view name, string_view value)
string_view value)
{ {
value = detail::trim(value); value = detail::trim(value);
auto const p = alloc_traits::allocate(this->member(), 1); auto const p = alloc_traits::allocate(this->member(), 1);
@@ -243,33 +242,13 @@ insert(string_view name,
template<class Allocator> template<class Allocator>
void void
basic_fields<Allocator>:: basic_fields<Allocator>::
replace(string_view name, replace(string_view name, string_view value)
string_view value)
{ {
value = detail::trim(value); value = detail::trim(value);
erase(name); erase(name);
insert(name, value); 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 } // http
} // beast } // beast

View File

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

View File

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

View File

@@ -8,10 +8,101 @@
#ifndef BEAST_HTTP_IMPL_STATUS_IPP #ifndef BEAST_HTTP_IMPL_STATUS_IPP
#define BEAST_HTTP_IMPL_STATUS_IPP #define BEAST_HTTP_IMPL_STATUS_IPP
#include <boost/throw_exception.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
namespace detail { 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> template<class = void>
string_view string_view
status_to_string(int v) status_to_string(int v)
@@ -92,11 +183,49 @@ status_to_string(int v)
default: default:
break; 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 } // 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 inline
string_view string_view
obsolete_reason(status v) obsolete_reason(status v)

View File

@@ -11,6 +11,7 @@
#include <beast/config.hpp> #include <beast/config.hpp>
#include <beast/http/fields.hpp> #include <beast/http/fields.hpp>
#include <beast/http/verb.hpp> #include <beast/http/verb.hpp>
#include <beast/http/status.hpp>
#include <beast/core/string_view.hpp> #include <beast/core/string_view.hpp>
#include <beast/core/detail/integer_sequence.hpp> #include <beast/core/detail/integer_sequence.hpp>
#include <boost/throw_exception.hpp> #include <boost/throw_exception.hpp>
@@ -58,104 +59,6 @@ struct header<true, Fields>
/// The type representing the fields. /// The type representing the fields.
using fields_type = 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 /// Default constructor
header() = default; 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: private:
template<class Fields> template<class T>
friend friend
void void
swap( swap(header<true, T>& m1, header<true, T>& m2);
header<true, Fields>& m1,
header<true, Fields>& m2);
template<class = void> template<class = void>
string_view string_view
@@ -256,7 +252,7 @@ struct header<false, Fields>
/// The HTTP field values. /// The HTTP field values.
fields_type fields; fields_type fields;
/// Default constructor /// Default constructor.
header() = default; header() = default;
/// Move constructor /// Move constructor
@@ -293,11 +289,62 @@ struct header<false, Fields>
} }
#endif #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`. @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. /** Return the Reason-Phrase.
@@ -305,26 +352,47 @@ struct header<false, Fields>
@note This function is only available if `isRequest == false`. @note This function is only available if `isRequest == false`.
*/ */
auto string_view
reason() const -> reason() const
decltype(std::declval<Fields>().reason()) const
{ {
return fields.reason(); return get_reason();
} }
/** Set the Reason-Phrase /** 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. @param value A value that represents the reason phrase.
@note This function is only available if `isRequest == false`. @note This function is only available if `isRequest == false`.
*/ */
template<class Value>
void 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. /** A container for a complete HTTP message.

View File

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

View File

@@ -15,8 +15,17 @@
namespace beast { namespace beast {
namespace http { 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, continue_ = 100,
switching_protocols = 101, switching_protocols = 101,
processing = 102, processing = 102,
@@ -85,13 +94,66 @@ enum class status : int
network_connect_timeout_error = 599 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 string_view
obsolete_reason(status v); 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& std::ostream&
operator<<(std::ostream& os, status v); operator<<(std::ostream&, status);
} // http } // http
} // beast } // beast

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,82 @@ public:
void void
testStatus() 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 = auto const good =
[&](status v) [&](status v)
{ {

View File

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

View File

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