diff --git a/CHANGELOG.md b/CHANGELOG.md index bbfb7372..ac1404b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 -------------------------------------------------------------------------------- diff --git a/doc/4_2_message.qbk b/doc/4_2_message.qbk index 0005d07d..d21064de 100644 --- a/doc/4_2_message.qbk +++ b/doc/4_2_message.qbk @@ -147,8 +147,7 @@ objects: ``` response 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()); diff --git a/doc/4_3_streams.qbk b/doc/4_3_streams.qbk index 847e4f48..10be7a88 100644 --- a/doc/4_3_streams.qbk +++ b/doc/4_3_streams.qbk @@ -128,8 +128,7 @@ the number of octets received prior to the server closing the connection: { response res; res.version = 11; - res.status = 200; - res.reason("OK"); + res.result(status::ok); res.fields.insert("Server", "Beast"); res.body = "Hello, world!"; diff --git a/doc/4_4_serializer_streams.qbk b/doc/4_4_serializer_streams.qbk index 3494a20a..acf36bdb 100644 --- a/doc/4_4_serializer_streams.qbk +++ b/doc/4_4_serializer_streams.qbk @@ -164,7 +164,7 @@ send_expect_100_continue( { response 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 res; - res.status = 200; + res.result(status::ok); res.version = 11; res.fields.insert("Server", "Beast"); res.fields.insert("Transfer-Encoding", "chunked"); diff --git a/doc/4_5_parser_streams.qbk b/doc/4_5_parser_streams.qbk index 6002b28c..6b2fa291 100644 --- a/doc/4_5_parser_streams.qbk +++ b/doc/4_5_parser_streams.qbk @@ -197,8 +197,7 @@ receive_expect_100_continue( // send 100 response response 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) diff --git a/doc/4_8_custom_parsers.qbk b/doc/4_8_custom_parsers.qbk index 45542076..75a9a9af 100644 --- a/doc/4_8_custom_parsers.qbk +++ b/doc/4_8_custom_parsers.qbk @@ -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 diff --git a/doc/6_1_http_message.qbk b/doc/6_1_http_message.qbk index 08f4c4bd..45a0cbb3 100644 --- a/doc/6_1_http_message.qbk +++ b/doc/6_1_http_message.qbk @@ -256,13 +256,13 @@ struct header 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; diff --git a/doc/images/message.png b/doc/images/message.png index 6e964762..159df8c0 100644 Binary files a/doc/images/message.png and b/doc/images/message.png differ diff --git a/doc/quickref.xml b/doc/quickref.xml index a0b9bd4d..06f36e68 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -88,6 +88,7 @@ connection error status + status_class verb Type Traits diff --git a/examples/http_async_server.hpp b/examples/http_async_server.hpp index 9d359232..82998566 100644 --- a/examples/http_async_server.hpp +++ b/examples/http_async_server.hpp @@ -234,8 +234,7 @@ private: if(! boost::filesystem::exists(path)) { response 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 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"); diff --git a/examples/http_sync_server.hpp b/examples/http_sync_server.hpp index 4b80cb47..b26db6b0 100644 --- a/examples/http_sync_server.hpp +++ b/examples/http_sync_server.hpp @@ -164,8 +164,7 @@ private: if(! boost::filesystem::exists(path)) { response 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 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"); diff --git a/include/beast/http/fields.hpp b/include/beast/http/fields.hpp index aa9869a9..bf61a758 100644 --- a/include/beast/http/fields.hpp +++ b/include/beast/http/fields.hpp @@ -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); } }; diff --git a/include/beast/http/impl/error.ipp b/include/beast/http/impl/error.ipp index 72eb5d38..72330914 100644 --- a/include/beast/http/impl/error.ipp +++ b/include/beast/http/impl/error.ipp @@ -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"; diff --git a/include/beast/http/impl/fields.ipp b/include/beast/http/impl/fields.ipp index 9e23de45..fae1a451 100644 --- a/include/beast/http/impl/fields.ipp +++ b/include/beast/http/impl/fields.ipp @@ -230,8 +230,7 @@ erase(string_view name) template void basic_fields:: -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 void basic_fields:: -replace(string_view name, - string_view value) +replace(string_view name, string_view value) { value = detail::trim(value); erase(name); insert(name, value); } -template -string_view -basic_fields:: -method_string() const -{ - return (*this)[":method"]; -} - -template -void -basic_fields:: -method_string(string_view s) -{ - if(s.empty()) - this->erase(":method"); - else - this->replace(":method", s); -} - } // http } // beast diff --git a/include/beast/http/impl/message.ipp b/include/beast/http/impl/message.ipp index 71df4b0f..881b20d3 100644 --- a/include/beast/http/impl/message.ipp +++ b/include/beast/http/impl/message.ipp @@ -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 @@ -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 @@ -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 +template +string_view +header:: +get_reason() const +{ + auto const s = fields.reason_impl(); + if(! s.empty()) + return s; + return obsolete_reason(result_); } template @@ -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 @@ -242,9 +254,9 @@ prepare(message& msg, operator()(message& 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); diff --git a/include/beast/http/impl/serializer.ipp b/include/beast/http/impl/serializer.ipp index dcf812c9..7801e382 100644 --- a/include/beast/http/impl/serializer.ipp +++ b/include/beast/http/impl/serializer.ipp @@ -23,6 +23,7 @@ void write_start_line(std::ostream& os, header 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 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(msg.status)) << "\r\n"; + os << msg.result_int() << " " << msg.reason() << "\r\n"; } template diff --git a/include/beast/http/impl/status.ipp b/include/beast/http/impl/status.ipp index 93f9e104..5a7d579b 100644 --- a/include/beast/http/impl/status.ipp +++ b/include/beast/http/impl/status.ipp @@ -8,10 +8,101 @@ #ifndef BEAST_HTTP_IMPL_STATUS_IPP #define BEAST_HTTP_IMPL_STATUS_IPP +#include + namespace beast { namespace http { namespace detail { +template +status +int_to_status(int v) +{ + switch(static_cast(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(v); + + default: + break; + } + return status::unknown; +} + template string_view status_to_string(int v) @@ -92,11 +183,49 @@ status_to_string(int v) default: break; } - return "Unknown Status"; + return ""; +} + +template +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(v)); +} + inline string_view obsolete_reason(status v) diff --git a/include/beast/http/message.hpp b/include/beast/http/message.hpp index bb15d847..e997ba3a 100644 --- a/include/beast/http/message.hpp +++ b/include/beast/http/message.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -58,104 +59,6 @@ struct header /// 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().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 - void - target(Value&& value) - { - fields.target(std::forward(value)); - } - - /// The HTTP field values. - fields_type fields; - /// Default constructor header() = default; @@ -198,13 +101,106 @@ struct header { } + /** 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 + template friend void - swap( - header& m1, - header& m2); + swap(header& m1, header& m2); template string_view @@ -256,7 +252,7 @@ struct header /// The HTTP field values. fields_type fields; - /// Default constructor + /// Default constructor. header() = default; /// Move constructor @@ -293,11 +289,62 @@ struct header } #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(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(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(result_); + } + /** Return the Reason-Phrase. @@ -305,26 +352,47 @@ struct header @note This function is only available if `isRequest == false`. */ - auto - reason() const -> - decltype(std::declval().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 void - reason(Value&& value) + reason(string_view s) { - fields.reason(std::forward(value)); + fields.reason_impl(s); } - + +private: + template + friend + void + swap(header& m1, header& m2); + + template + string_view + get_reason() const; + + status result_; }; /** A container for a complete HTTP message. diff --git a/include/beast/http/parser.hpp b/include/beast/http/parser.hpp index 13edb2e1..1f770602 100644 --- a/include/beast/http/parser.hpp +++ b/include/beast/http/parser.hpp @@ -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); } diff --git a/include/beast/http/status.hpp b/include/beast/http/status.hpp index 3fcfd7ce..3e35fc30 100644 --- a/include/beast/http/status.hpp +++ b/include/beast/http/status.hpp @@ -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 diff --git a/include/beast/http/verb.hpp b/include/beast/http/verb.hpp index a77020b9..2e303223 100644 --- a/include/beast/http/verb.hpp +++ b/include/beast/http/verb.hpp @@ -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); diff --git a/include/beast/websocket/impl/accept.ipp b/include/beast/websocket/impl/accept.ipp index c9c761fb..944e7725 100644 --- a/include/beast/websocket/impl/accept.ipp +++ b/include/beast/websocket/impl/accept.ipp @@ -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) { diff --git a/include/beast/websocket/impl/stream.ipp b/include/beast/websocket/impl/stream.ipp index dc533ffa..865f302a 100644 --- a/include/beast/websocket/impl/stream.ipp +++ b/include/beast/websocket/impl/stream.ipp @@ -112,7 +112,7 @@ do_accept(http::header 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 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 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 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 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; diff --git a/test/http/design.cpp b/test/http/design.cpp index 3eb30b34..2aef02f9 100644 --- a/test/http/design.cpp +++ b/test/http/design.cpp @@ -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 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 res; - res.status = 200; + res.result(status::ok); res.version = 11; res.fields.insert("Server", "Beast"); res.fields.insert("Transfer-Encoding", "chunked"); diff --git a/test/http/fields.cpp b/test/http/fields.cpp index 3dfd23e7..9607b91f 100644 --- a/test/http/fields.cpp +++ b/test/http/fields.cpp @@ -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 diff --git a/test/http/message.cpp b/test/http/message.cpp index 0de2542b..e006f31c 100644 --- a/test/http/message.cpp +++ b/test/http/message.cpp @@ -262,19 +262,20 @@ public: { message m1; message 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 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 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(); } }; diff --git a/test/http/parser.cpp b/test/http/parser.cpp index 8ace8e0f..1b1ce19a 100644 --- a/test/http/parser.cpp +++ b/test/http/parser.cpp @@ -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"); diff --git a/test/http/status.cpp b/test/http/status.cpp index e7389d5a..69cdcbff 100644 --- a/test/http/status.cpp +++ b/test/http/status.cpp @@ -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) { diff --git a/test/http/test_parser.hpp b/test/http/test_parser.hpp index 0f88ba06..f0de6e05 100644 --- a/test/http/test_parser.hpp +++ b/test/http/test_parser.hpp @@ -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_; diff --git a/test/http/write.cpp b/test/http/write.cpp index e79d7907..a603cfd8 100644 --- a/test/http/write.cpp +++ b/test/http/write.cpp @@ -292,7 +292,7 @@ public: { message 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 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 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";