Skip to content

Commit

Permalink
Add support for retrieving HTTP version of a request in HTTP listener (
Browse files Browse the repository at this point in the history
…microsoft#565)

* Grab HTTP protocol version from the underlying request message and expose in http_request

* Use error_code overload to resolve microsoft#545

* Rename get_http_version and get_remote_address without 'get_' prefix for consistency with rest of class public interface

* Add test case for http_version

* Fix GCC "error: changes meaning of 'http_version'..." that should have been in commit:1ba5ebfd

* Use struct for http_version instead of pair.

* Restore get_remote_address to avoid breaking source compat
  • Loading branch information
garethsb authored and ras0219-msft committed Jan 24, 2018
1 parent 7db3585 commit 6397261
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 5 deletions.
43 changes: 43 additions & 0 deletions Release/include/cpprest/http_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,34 @@ namespace client
class http_client;
}

/// <summary>
/// Represents the HTTP protocol version of a message, as {major, minor}.
/// </summary>
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; }
};

/// <summary>
/// Predefined HTTP protocol versions.
/// </summary>
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;
};

/// <summary>
/// Predefined method strings for the standard HTTP methods mentioned in the
/// HTTP 1.1 specification.
Expand Down Expand Up @@ -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; }
Expand Down Expand Up @@ -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:
Expand All @@ -783,6 +815,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena

pplx::task_completion_event<http_response> m_response;

http::http_version m_http_version;

utility::string_t m_remote_address;
};

Expand Down Expand Up @@ -875,10 +909,19 @@ class http_request
/// </remarks>
const http_headers &headers() const { return _m_impl->headers(); }

/// <summary>
/// Returns the HTTP protocol version of this request message.
/// </summary>
/// <returns>The HTTP protocol version.</returns>
http::http_version http_version() const { return _m_impl->http_version(); }

/// <summary>
/// Returns a string representation of the remote IP address.
/// </summary>
/// <returns>The remote IP address.</returns>
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(); }

/// <summary>
Expand Down
10 changes: 8 additions & 2 deletions Release/src/http/common/http_msg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,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())
{
Expand All @@ -1009,10 +1010,15 @@ details::_http_request::_http_request(http::method mtd)
details::_http_request::_http_request(std::unique_ptr<http::details::_http_server_context> 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"
Expand Down
22 changes: 20 additions & 2 deletions Release/src/http/listener/http_server_asio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,14 +657,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();
}
Expand Down
2 changes: 2 additions & 0 deletions Release/src/http/listener/http_server_httpsys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,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<wchar_t> remoteAddressBuffer(50);

Expand Down
35 changes: 34 additions & 1 deletion Release/tests/functional/http/listener/request_handler_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand All @@ -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"));

Expand Down

0 comments on commit 6397261

Please sign in to comment.