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"));