From 2dcf4fd187d2ca6ce32a9b1ad9b01bcdb8fb08d5 Mon Sep 17 00:00:00 2001 From: Owen Rudge Date: Mon, 17 Jul 2017 12:03:05 +0100 Subject: [PATCH 1/2] Add support for retrieving remote address in HTTP listener --- Release/include/cpprest/http_msg.h | 12 ++++++++++++ Release/src/http/listener/http_server_asio.cpp | 3 +++ .../src/http/listener/http_server_httpsys.cpp | 16 ++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/Release/include/cpprest/http_msg.h b/Release/include/cpprest/http_msg.h index 72a07fdd62..8ca25edc69 100644 --- a/Release/include/cpprest/http_msg.h +++ b/Release/include/cpprest/http_msg.h @@ -715,6 +715,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena _ASYNCRTIMP void set_request_uri(const uri&); + const utility::string_t& remote_address() const { return m_remote_address; } + const pplx::cancellation_token &cancellation_token() const { return m_cancellationToken; } void set_cancellation_token(const pplx::cancellation_token &token) @@ -755,6 +757,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_remote_address(const utility::string_t &remote_address) { m_remote_address = remote_address; } + private: // Actual initiates sending the response, without checking if a response has already been sent. @@ -778,6 +782,8 @@ class _http_request final : public http::details::http_msg_base, public std::ena std::shared_ptr m_progress_handler; pplx::task_completion_event m_response; + + utility::string_t m_remote_address; }; @@ -869,6 +875,12 @@ class http_request /// const http_headers &headers() const { return _m_impl->headers(); } + /// + /// Returns a string representation of the remote IP address. + /// + /// The remote IP address. + const utility::string_t& get_remote_address() const { return _m_impl->remote_address(); } + /// /// Extract the body of the request message as a string value, checking that the content type is a MIME text type. /// A body can only be extracted once because in some cases an optimization is made where the data is 'moved' out. diff --git a/Release/src/http/listener/http_server_asio.cpp b/Release/src/http/listener/http_server_asio.cpp index 8b155c23f2..99351c13a0 100644 --- a/Release/src/http/listener/http_server_asio.cpp +++ b/Release/src/http/listener/http_server_asio.cpp @@ -654,6 +654,9 @@ will_deref_and_erase_t asio_server_connection::handle_http_line(const boost::sys 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())); + return handle_headers(); } } diff --git a/Release/src/http/listener/http_server_httpsys.cpp b/Release/src/http/listener/http_server_httpsys.cpp index ce1717ea3d..2604bfc8e8 100644 --- a/Release/src/http/listener/http_server_httpsys.cpp +++ b/Release/src/http/listener/http_server_httpsys.cpp @@ -555,6 +555,22 @@ 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()); + // Retrieve the remote IP address + std::vector remoteAddressBuffer(50); + PVOID inAddr; + + if (m_request->Address.pRemoteAddress->sa_family == AF_INET6) + { + inAddr = &reinterpret_cast(m_request->Address.pRemoteAddress)->sin6_addr; + } + else + { + inAddr = &reinterpret_cast(m_request->Address.pRemoteAddress)->sin_addr; + } + + InetNtopW(m_request->Address.pRemoteAddress->sa_family, inAddr, &remoteAddressBuffer[0], remoteAddressBuffer.size()); + m_msg._get_impl()->_set_remote_address(std::wstring(&remoteAddressBuffer[0])); + // Start reading in body from the network. m_msg._get_impl()->_prepare_to_receive_data(); read_request_body_chunk(); From 48244346d3ef270976dab2a61c56631072da4eb1 Mon Sep 17 00:00:00 2001 From: Owen Rudge Date: Mon, 17 Jul 2017 14:19:25 +0100 Subject: [PATCH 2/2] Add test for request.remote_address() --- .../http/listener/request_handler_tests.cpp | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Release/tests/functional/http/listener/request_handler_tests.cpp b/Release/tests/functional/http/listener/request_handler_tests.cpp index 8c9a525a74..33f7d0ea85 100644 --- a/Release/tests/functional/http/listener/request_handler_tests.cpp +++ b/Release/tests/functional/http/listener/request_handler_tests.cpp @@ -448,6 +448,41 @@ TEST_FIXTURE(uri_address, test_leaks) listener.close().wait(); } +TEST_FIXTURE(uri_address, remote_address) +{ + 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 string_t& remoteAddr = request.get_remote_address(); + const string_t& localhost4 = string_t(U("127.0.0.1")); + const string_t& localhost6 = string_t(U("::1")); + + // We can't guarantee that the host has both IPv4 and IPv6 available, so check for either IP + VERIFY_IS_TRUE((remoteAddr == localhost4) || (remoteAddr == localhost6)); + + 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(); +} + } }}}}