diff --git a/Release/include/cpprest/http_msg.h b/Release/include/cpprest/http_msg.h index 8ca25edc69..2defd81ffd 100644 --- a/Release/include/cpprest/http_msg.h +++ b/Release/include/cpprest/http_msg.h @@ -43,6 +43,34 @@ namespace client class http_client; } +/// +/// Represents the HTTP protocol version of a message, as {major, minor}. +/// +struct http_version +{ + uint8_t major; + uint8_t minor; + + inline bool operator==(const http_version& other) const { return major == other.major && minor == other.minor; } + inline bool operator<(const http_version& other) const { return major < other.major || (major == other.major && minor < other.minor); } + + inline bool operator!=(const http_version& other) const { return !(*this == other); } + 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; } +}; + +/// +/// Predefined HTTP protocol versions. +/// +class http_versions +{ +public: + _ASYNCRTIMP static const http_version HTTP_0_9; + _ASYNCRTIMP static const http_version HTTP_1_0; + _ASYNCRTIMP static const http_version HTTP_1_1; +}; + /// /// Predefined method strings for the standard HTTP methods mentioned in the /// HTTP 1.1 specification. @@ -715,6 +743,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena _ASYNCRTIMP void set_request_uri(const uri&); + http::http_version http_version() const { return m_http_version; } + const utility::string_t& remote_address() const { return m_remote_address; } const pplx::cancellation_token &cancellation_token() const { return m_cancellationToken; } @@ -757,6 +787,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena void _set_base_uri(const http::uri &base_uri) { m_base_uri = base_uri; } + void _set_http_version(const http::http_version &http_version) { m_http_version = http_version; } + void _set_remote_address(const utility::string_t &remote_address) { m_remote_address = remote_address; } private: @@ -783,6 +815,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena pplx::task_completion_event m_response; + http::http_version m_http_version; + utility::string_t m_remote_address; }; @@ -875,10 +909,19 @@ class http_request /// const http_headers &headers() const { return _m_impl->headers(); } + /// + /// Returns the HTTP protocol version of this request message. + /// + /// The HTTP protocol version. + http::http_version http_version() const { return _m_impl->http_version(); } + /// /// Returns a string representation of the remote IP address. /// /// The remote IP address. + const utility::string_t& remote_address() const { return _m_impl->remote_address(); } + + CASABLANCA_DEPRECATED("Use `remote_address()` instead.") const utility::string_t& get_remote_address() const { return _m_impl->remote_address(); } /// diff --git a/Release/src/http/common/http_msg.cpp b/Release/src/http/common/http_msg.cpp index 6a857d7531..abe927b118 100644 --- a/Release/src/http/common/http_msg.cpp +++ b/Release/src/http/common/http_msg.cpp @@ -995,7 +995,8 @@ details::_http_request::_http_request(http::method mtd) : m_method(std::move(mtd)), m_initiated_response(0), m_server_context(), - m_cancellationToken(pplx::cancellation_token::none()) + m_cancellationToken(pplx::cancellation_token::none()), + m_http_version(http::http_version{0, 0}) { if(m_method.empty()) { @@ -1006,10 +1007,15 @@ details::_http_request::_http_request(http::method mtd) details::_http_request::_http_request(std::unique_ptr server_context) : m_initiated_response(0), m_server_context(std::move(server_context)), - m_cancellationToken(pplx::cancellation_token::none()) + m_cancellationToken(pplx::cancellation_token::none()), + m_http_version(http::http_version{0, 0}) { } +const http_version http_versions::HTTP_0_9 = { 0, 9 }; +const http_version http_versions::HTTP_1_0 = { 1, 0 }; +const http_version http_versions::HTTP_1_1 = { 1, 1 }; + #define _METHODS #define DAT(a,b) const method methods::a = b; #include "cpprest/details/http_constants.dat" diff --git a/Release/src/http/listener/http_server_asio.cpp b/Release/src/http/listener/http_server_asio.cpp index 99351c13a0..c807e70556 100644 --- a/Release/src/http/listener/http_server_asio.cpp +++ b/Release/src/http/listener/http_server_asio.cpp @@ -648,14 +648,32 @@ will_deref_and_erase_t asio_server_connection::handle_http_line(const boost::sys // Get the version 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); + } + // if HTTP version is 1.0 then disable pipelining - if (http_version == "HTTP/1.0") + if (parsed_version == web::http::http_versions::HTTP_1_0) { m_close = true; } // Get the remote IP address - m_request._get_impl()->_set_remote_address(utility::conversions::to_string_t(m_socket->remote_endpoint().address().to_string())); + boost::system::error_code socket_ec; + auto endpoint = m_socket->remote_endpoint(socket_ec); + if (!socket_ec) + { + m_request._get_impl()->_set_remote_address(utility::conversions::to_string_t(endpoint.address().to_string())); + } return handle_headers(); } diff --git a/Release/src/http/listener/http_server_httpsys.cpp b/Release/src/http/listener/http_server_httpsys.cpp index 9b58d20ef1..17761fc46e 100644 --- a/Release/src/http/listener/http_server_httpsys.cpp +++ b/Release/src/http/listener/http_server_httpsys.cpp @@ -557,6 +557,8 @@ void windows_request_context::read_headers_io_completion(DWORD error_code, DWORD m_msg.set_method(parse_request_method(m_request)); parse_http_headers(m_request->Headers, m_msg.headers()); + m_msg._get_impl()->_set_http_version({ (uint8_t)m_request->Version.MajorVersion, (uint8_t)m_request->Version.MinorVersion }); + // Retrieve the remote IP address std::vector remoteAddressBuffer(50); diff --git a/Release/tests/functional/http/listener/request_handler_tests.cpp b/Release/tests/functional/http/listener/request_handler_tests.cpp index 33f7d0ea85..3a1e135b13 100644 --- a/Release/tests/functional/http/listener/request_handler_tests.cpp +++ b/Release/tests/functional/http/listener/request_handler_tests.cpp @@ -448,6 +448,39 @@ TEST_FIXTURE(uri_address, test_leaks) listener.close().wait(); } +TEST_FIXTURE(uri_address, http_version) +{ + http_listener listener(U("http://localhost:45678/path1")); + listener.open().wait(); + + test_http_client::scoped_client client(U("http://localhost:45678")); + test_http_client * p_client = client.client(); + + volatile unsigned long requestCount = 0; + + listener.support(methods::GET, [&requestCount](http_request request) + { + const auto& httpVersion = request.http_version(); + + // All clients currently use HTTP/1.1 + VERIFY_IS_TRUE(httpVersion == http_versions::HTTP_1_1); + + os_utilities::interlocked_increment(&requestCount); + request.reply(status_codes::NoContent); + }); + + // Send a request to the listener + VERIFY_ARE_EQUAL(0, p_client->request(methods::GET, U("/path1"))); + + p_client->next_response().then([](test_response *p_response) + { + http_asserts::assert_test_response_equals(p_response, status_codes::NoContent); + }).wait(); + + VERIFY_IS_TRUE(requestCount >= 1); + listener.close().wait(); +} + TEST_FIXTURE(uri_address, remote_address) { http_listener listener(U("http://localhost:45678/path1")); @@ -460,7 +493,7 @@ TEST_FIXTURE(uri_address, remote_address) listener.support(methods::GET, [&requestCount](http_request request) { - const string_t& remoteAddr = request.get_remote_address(); + const string_t& remoteAddr = request.remote_address(); const string_t& localhost4 = string_t(U("127.0.0.1")); const string_t& localhost6 = string_t(U("::1"));