Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for retrieving HTTP version of a request in HTTP listener #565

Merged
merged 7 commits into from
Jan 24, 2018
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 @@ -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())
{
Expand All @@ -1006,10 +1007,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 @@ -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();
}
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 @@ -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<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