diff --git a/include/boost/http_proto/response_base.hpp b/include/boost/http_proto/response_base.hpp index 3708b0d9..9eb75280 100644 --- a/include/boost/http_proto/response_base.hpp +++ b/include/boost/http_proto/response_base.hpp @@ -110,6 +110,8 @@ class response_base @throw std::length_error Max capacity would be exceeded. + @throw std::invalid_argument + `sc == status::unknown` @param sc The status code to set. This must not be @ref status::unknown. @@ -122,12 +124,57 @@ class response_base http_proto::version v = http_proto::version::http_1_1) { - set_start_line_impl( - sc, - static_cast< - unsigned short>(sc), + set_start_line_impl(sc, + static_cast(sc), + obsolete_reason(sc), v); + } + + /** Set the HTTP version of the response + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if maximum capacity exceeded. + + @throw std::length_error + Maximum capacity would be exceeded. + + @param v The version to set. + */ + BOOST_HTTP_PROTO_DECL + void + set_version( + http_proto::version v); + + /** Set the status code of the response. + + The reason-phrase will be set to the + standard text for the specified status + code. The version will remain unchanged. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if maximum capacity exceeded. + + @throw std::length_error + Maximum capacity would be exceeded. + @throw std::invalid_argument + `sc == status::unknown` + + @param sc The status code to set. This + must not be @ref status::unknown. + */ + void + set_status( + http_proto::status sc) + { + if(sc == http_proto::status::unknown) + detail::throw_invalid_argument(); + set_start_line_impl(sc, + static_cast(sc), obsolete_reason(sc), - v); + version()); } /** Set the status code and version of the response. diff --git a/include/boost/http_proto/serializer.hpp b/include/boost/http_proto/serializer.hpp index f65f13ad..8f3b8f22 100644 --- a/include/boost/http_proto/serializer.hpp +++ b/include/boost/http_proto/serializer.hpp @@ -158,11 +158,13 @@ class serializer void reset() noexcept; - /** Prepare the serializer for a new message without a body. + /** Start serializing a message with an empty body - Initializes the serializer with the HTTP - start-line and headers from `m`, and - without a body. + This function prepares the serializer to create a message which + has an empty body. + Ownership of the specified message is not transferred; the caller is + responsible for ensuring the lifetime of the object extends until the + serializer is done. @par Preconditions @code @@ -176,19 +178,15 @@ class serializer @par Exception Safety Strong guarantee. - Exceptions thrown if there is insufficient - internal buffer space to start the - operation. + Exceptions thrown if there is insufficient internal buffer space + to start the operation. - @throw std::logic_error - `this->is_done() == true`. + @throw std::logic_error `this->is_done() == true`. - @throw std::length_error if there is - insufficient internal buffer space to - start the operation. + @throw std::length_error if there is insufficient internal buffer + space to start the operation. - @param m The message to read the HTTP - start-line and headers from. + @param m The request or response headers to serialize. @see @ref message_base. @@ -197,17 +195,19 @@ class serializer BOOST_HTTP_PROTO_DECL start(message_base const& m); - /** Prepare the serializer for a new message with a ConstBufferSequence body. + /** Start serializing a message with a buffer sequence body - Initializes the serializer with the HTTP - start-line and headers from `m`, and the - provided `buffers` for reading the - message body from. + Initializes the serializer with the HTTP start-line and headers from `m`, + and the provided `buffers` for reading the message body from. - Changing the contents of the message - after calling this function and before - @ref is_done returns `true` results in - undefined behavior. + Changing the contents of the message after calling this function and + before @ref is_done returns `true` results in undefined behavior. + + At least one copy of the specified buffer sequence is maintained until + the serializer is done, gets reset, or ios destroyed, after which all + of its copies are destroyed. Ownership of the underlying memory is not + transferred; the caller must ensure the memory remains valid until the + serializer’s copies are destroyed. @par Preconditions @code @@ -221,26 +221,23 @@ class serializer @par Constraints @code - buffers::is_const_buffer_sequence::value == true + buffers::is_const_buffer_sequence_v == true @endcode @par Exception Safety Strong guarantee. - Exceptions thrown if there is insufficient - internal buffer space to start the - operation. + Exceptions thrown if there is insufficient internal buffer space + to start the operation. - @throw std::logic_error - `this->is_done() == true`. + @throw std::logic_error `this->is_done() == true`. - @throw std::length_error if there is - insufficient internal buffer space to - start the operation. + @throw std::length_error If there is insufficient internal buffer + space to start the operation. - @param m The message to read the HTTP - start-line and headers from. + @param m The message to read the HTTP start-line and headers from. + + @param buffers A buffer sequence containing the message body. - @param buffers One or more buffers containing the message body data. While the buffers object is copied, ownership of the underlying memory remains with the @@ -261,12 +258,10 @@ class serializer message_base const& m, ConstBufferSequence&& buffers); - /** Prepare the serializer for a new message with a Source body. + /** Start serializing a message with a @em Source body - Initializes the serializer with the - HTTP start-line and headers from `m`, - and constructs a `Source` object to read - the message body. + Initializes the serializer with the HTTP start-line and headers from + `m`, and constructs a `Source` object to provide the message body. Changing the contents of the message after calling this function and before diff --git a/src/response_base.cpp b/src/response_base.cpp index f8e4cdbe..d6fd9a19 100644 --- a/src/response_base.cpp +++ b/src/response_base.cpp @@ -57,5 +57,37 @@ set_start_line_impl( h_.on_start_line(); } +void +response_base:: +set_version( + http_proto::version v) +{ + if(v == h_.version) + return; + if(h_.is_default()) + { + auto def = h_.get_default(detail::kind::response); + return set_start_line_impl( + def->res.status, def->res.status_int, + core::string_view( + def->cbuf + 13, def->prefix - 15), v); + } + + // Introduce a new scope so that prefix_op's + // destructor runs before h_.on_start_line(). + { + auto op = prefix_op_t( + *this, h_.prefix, nullptr); + char* dest = h_.buf; + if(v == http_proto::version::http_1_1) + dest[7] = '1'; + else + dest[7] = '0'; + h_.version = v; + } + + h_.on_start_line(); +} + } // http_proto } // boost diff --git a/src/status.cpp b/src/status.cpp index cddde8c6..2ad4fb5c 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -8,7 +8,8 @@ // #include -#include +#include +//#include #include namespace boost { diff --git a/test/unit/response.cpp b/test/unit/response.cpp index 3128b090..175d89b8 100644 --- a/test/unit/response.cpp +++ b/test/unit/response.cpp @@ -283,6 +283,34 @@ class response_test BOOST_TEST_EQ(it->value, "0"); } } + + // set_version + { + response res; + res.set_version(version::http_1_1); + BOOST_TEST_EQ(res.buffer(), + "HTTP/1.1 200 OK\r\n\r\n"); + res.set_version(version::http_1_0); + BOOST_TEST_EQ(res.buffer(), + "HTTP/1.0 200 OK\r\n\r\n"); + res.set_version(version::http_1_1); + BOOST_TEST_EQ(res.buffer(), + "HTTP/1.1 200 OK\r\n\r\n"); + } + + // set_status + { + response res; + res.set_status(status::not_found); + BOOST_TEST_EQ(res.buffer(), + "HTTP/1.1 404 Not Found\r\n\r\n"); + res.set_status(status::bad_request); + BOOST_TEST_EQ(res.buffer(), + "HTTP/1.1 400 Bad Request\r\n\r\n"); + res.set_status(status::ok); + BOOST_TEST_EQ(res.buffer(), + "HTTP/1.1 200 OK\r\n\r\n"); + } } void