diff --git a/Release/include/cpprest/http_msg.h b/Release/include/cpprest/http_msg.h index 6b49bc4960..00f58e1350 100644 --- a/Release/include/cpprest/http_msg.h +++ b/Release/include/cpprest/http_msg.h @@ -58,6 +58,17 @@ struct http_version inline bool operator>=(const http_version& other) const { return !(*this < other); } inline bool operator>(const http_version& other) const { return !(*this < other || *this == other); } inline bool operator<=(const http_version& other) const { return *this < other || *this == other; } + + /// + /// Creates http_version from an HTTP-Version string, "HTTP" "/" 1*DIGIT "." 1*DIGIT. + /// + /// Returns a http_version of {0, 0} if not successful. + static _ASYNCRTIMP http_version __cdecl from_string(const utility::string_t& http_version_string); + + /// + /// Returns the string representation of the http_version. + /// + _ASYNCRTIMP std::string to_utf8string() const; }; /// diff --git a/Release/src/http/common/http_msg.cpp b/Release/src/http/common/http_msg.cpp index 5e6eb73386..459aff5609 100644 --- a/Release/src/http/common/http_msg.cpp +++ b/Release/src/http/common/http_msg.cpp @@ -254,6 +254,35 @@ void parse_headers_string(_Inout_z_ utf16char *headersStr, http_headers &headers } +http_version __cdecl http_version::from_string(const utility::string_t& http_version_string) +{ + utility::istringstream_t str(http_version_string); + str.imbue(std::locale::classic()); + + utility::string_t http; std::getline(str, http, _XPLATSTR('/')); + unsigned int major = 0; str >> major; + utility::char_t dot = _XPLATSTR('\0'); str >> dot; + unsigned int minor = 0; str >> minor; + + // check no failure, fully consumed, and correct fixed text + if (!str.fail() && str.eof() && _XPLATSTR("HTTP") == http && _XPLATSTR('.') == dot) + { + return{ (uint8_t)major, (uint8_t)minor }; + } + return{ 0, 0 }; +} + +std::string http_version::to_utf8string() const +{ + std::string ret; + ret.reserve(8); + ret.append("HTTP/"); + ret.append(std::to_string(static_cast(major))); + ret.append("."); + ret.append(std::to_string(static_cast(minor))); + return ret; +} + static const utility::char_t * stream_was_set_explicitly = _XPLATSTR("A stream was set on the message and extraction is not possible"); static const utility::char_t * unsupported_charset = _XPLATSTR("Charset must be iso-8859-1, utf-8, utf-16, utf-16le, or utf-16be to be extracted."); @@ -1052,4 +1081,4 @@ const http_version http_versions::HTTP_1_1 = { 1, 1 }; #undef DAT #endif }} // namespace web::http - + diff --git a/Release/src/http/listener/http_server_asio.cpp b/Release/src/http/listener/http_server_asio.cpp index 1a28a3ce6d..e7f9232816 100644 --- a/Release/src/http/listener/http_server_asio.cpp +++ b/Release/src/http/listener/http_server_asio.cpp @@ -659,16 +659,8 @@ will_deref_and_erase_t asio_server_connection::handle_http_line(const boost::sys std::string http_version = http_path_and_version.substr(http_path_and_version.size() - VersionPortionSize + 1, VersionPortionSize - 2); auto m_request_impl = m_request._get_impl().get(); - web::http::http_version parsed_version = { 0, 0 }; - if (boost::starts_with(http_version, "HTTP/")) - { - std::istringstream version{ http_version.substr(5) }; - version >> parsed_version.major; - char dot; version >> dot; - version >> parsed_version.minor; - - m_request_impl->_set_http_version(parsed_version); - } + web::http::http_version parsed_version = web::http::http_version::from_string(utility::conversions::to_string_t(http_version)); + m_request_impl->_set_http_version(parsed_version); // if HTTP version is 1.0 then disable pipelining if (parsed_version == web::http::http_versions::HTTP_1_0) diff --git a/Release/tests/functional/http/listener/request_handler_tests.cpp b/Release/tests/functional/http/listener/request_handler_tests.cpp index 3a1e135b13..269a0e23b0 100644 --- a/Release/tests/functional/http/listener/request_handler_tests.cpp +++ b/Release/tests/functional/http/listener/request_handler_tests.cpp @@ -450,6 +450,29 @@ TEST_FIXTURE(uri_address, test_leaks) TEST_FIXTURE(uri_address, http_version) { + // formatting should succeed + VERIFY_IS_TRUE("HTTP/0.9" == http_versions::HTTP_0_9.to_utf8string()); + VERIFY_IS_TRUE("HTTP/1.0" == http_versions::HTTP_1_0.to_utf8string()); + VERIFY_IS_TRUE("HTTP/1.1" == http_versions::HTTP_1_1.to_utf8string()); + VERIFY_IS_TRUE("HTTP/12.3" == (http_version{ 12, 3 }).to_utf8string()); + // parsing should succeed + VERIFY_IS_TRUE(http_version::from_string(U("HTTP/0.9")) == http_versions::HTTP_0_9); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP/1.0")) == http_versions::HTTP_1_0); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP/1.1")) == http_versions::HTTP_1_1); + VERIFY_IS_TRUE((http_version::from_string(U("HTTP/12.3")) == http_version{ 12, 3 })); + // parsing should fail + http_version unknown = { 0, 0 }; + VERIFY_IS_TRUE(http_version::from_string(U("http/12.3")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP/12.3foo")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP/12.")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP/12")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP/.3")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP/")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("HTTP")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("foo")) == unknown); + VERIFY_IS_TRUE(http_version::from_string(U("")) == unknown); + http_listener listener(U("http://localhost:45678/path1")); listener.open().wait();