| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/proxy_resolution/win/windows_system_proxy_resolution_service.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/sequence_checker.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "net/base/network_isolation_key.h" |
| #include "net/base/proxy_server.h" |
| #include "net/base/proxy_string_util.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/proxy_resolution/configured_proxy_resolution_service.h" |
| #include "net/proxy_resolution/proxy_config.h" |
| #include "net/proxy_resolution/proxy_info.h" |
| #include "net/proxy_resolution/proxy_list.h" |
| #include "net/proxy_resolution/win/windows_system_proxy_resolution_request.h" |
| #include "net/proxy_resolution/win/windows_system_proxy_resolver.h" |
| #include "net/proxy_resolution/win/winhttp_status.h" |
| #include "net/test/gtest_util.h" |
| #include "net/test/test_with_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| using net::test::IsError; |
| using net::test::IsOk; |
| |
| namespace net { |
| |
| namespace { |
| |
| const GURL kResourceUrl("https://example.test:8080/"); |
| |
| class MockRequest : public WindowsSystemProxyResolver::Request { |
| public: |
| MockRequest(WindowsSystemProxyResolutionRequest* callback_target, |
| const ProxyList& proxy_list, |
| WinHttpStatus winhttp_status, |
| int windows_error) { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&MockRequest::DoCallback, weak_ptr_factory_.GetWeakPtr(), |
| callback_target, proxy_list, winhttp_status, |
| windows_error)); |
| } |
| ~MockRequest() override = default; |
| |
| private: |
| void DoCallback(WindowsSystemProxyResolutionRequest* callback_target, |
| const ProxyList& proxy_list, |
| WinHttpStatus winhttp_status, |
| int windows_error) { |
| callback_target->ProxyResolutionComplete(proxy_list, winhttp_status, |
| windows_error); |
| } |
| |
| base::WeakPtrFactory<MockRequest> weak_ptr_factory_{this}; |
| }; |
| |
| class MockWindowsSystemProxyResolver : public WindowsSystemProxyResolver { |
| public: |
| MockWindowsSystemProxyResolver() = default; |
| ~MockWindowsSystemProxyResolver() override = default; |
| |
| void add_server_to_proxy_list(const ProxyServer& proxy_server) { |
| proxy_list_.AddProxyServer(proxy_server); |
| } |
| |
| void set_winhttp_status(WinHttpStatus winhttp_status) { |
| winhttp_status_ = winhttp_status; |
| } |
| |
| void set_windows_error(int windows_error) { windows_error_ = windows_error; } |
| |
| std::unique_ptr<Request> GetProxyForUrl( |
| const GURL& url, |
| WindowsSystemProxyResolutionRequest* callback_target) override { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return std::make_unique<MockRequest>(callback_target, proxy_list_, |
| winhttp_status_, windows_error_); |
| } |
| |
| private: |
| ProxyList proxy_list_; |
| WinHttpStatus winhttp_status_ = WinHttpStatus::kOk; |
| int windows_error_ = 0; |
| |
| SEQUENCE_CHECKER(sequence_checker_); |
| }; |
| |
| } // namespace |
| |
| // These tests verify the behavior of the WindowsSystemProxyResolutionService in |
| // isolation by mocking out the WindowsSystemProxyResolver. |
| class WindowsSystemProxyResolutionServiceTest : public TestWithTaskEnvironment { |
| public: |
| void SetUp() override { |
| testing::Test::SetUp(); |
| |
| if (!WindowsSystemProxyResolutionService::IsSupported()) { |
| GTEST_SKIP() |
| << "Windows System Proxy Resolution is only supported on Windows 8+."; |
| } |
| |
| auto proxy_resolver = std::make_unique<MockWindowsSystemProxyResolver>(); |
| proxy_resolver_ = proxy_resolver.get(); |
| proxy_resolution_service_ = WindowsSystemProxyResolutionService::Create( |
| std::move(proxy_resolver), /*net_log=*/nullptr); |
| ASSERT_TRUE(proxy_resolution_service_); |
| } |
| |
| WindowsSystemProxyResolutionService* service() { |
| return proxy_resolution_service_.get(); |
| } |
| |
| MockWindowsSystemProxyResolver* resolver() { return proxy_resolver_; } |
| |
| void ResetProxyResolutionService() { proxy_resolution_service_.reset(); } |
| |
| void DoResolveProxyTest(const ProxyList& expected_proxy_list) { |
| ProxyInfo info; |
| TestCompletionCallback callback; |
| NetLogWithSource log; |
| std::unique_ptr<ProxyResolutionRequest> request; |
| int result = service()->ResolveProxy(kResourceUrl, std::string(), |
| NetworkAnonymizationKey(), &info, |
| callback.callback(), &request, log); |
| |
| ASSERT_THAT(result, IsError(ERR_IO_PENDING)); |
| ASSERT_NE(request, nullptr); |
| |
| // Wait for result to come back. |
| EXPECT_THAT(callback.GetResult(result), IsOk()); |
| |
| EXPECT_TRUE(expected_proxy_list.Equals(info.proxy_list())); |
| EXPECT_NE(request, nullptr); |
| } |
| |
| private: |
| std::unique_ptr<WindowsSystemProxyResolutionService> |
| proxy_resolution_service_; |
| raw_ptr<MockWindowsSystemProxyResolver> proxy_resolver_; |
| }; |
| |
| TEST_F(WindowsSystemProxyResolutionServiceTest, CreateWithNullResolver) { |
| std::unique_ptr<WindowsSystemProxyResolutionService> |
| proxy_resolution_service = WindowsSystemProxyResolutionService::Create( |
| /*windows_system_proxy_resolver=*/nullptr, /*net_log=*/nullptr); |
| EXPECT_FALSE(proxy_resolution_service); |
| } |
| |
| TEST_F(WindowsSystemProxyResolutionServiceTest, ResolveProxyFailed) { |
| resolver()->set_winhttp_status(WinHttpStatus::kAborted); |
| |
| // Make sure there would be a proxy result on success. |
| const ProxyServer proxy_server = |
| PacResultElementToProxyServer("HTTPS foopy:8443"); |
| resolver()->add_server_to_proxy_list(proxy_server); |
| |
| ProxyInfo info; |
| TestCompletionCallback callback; |
| NetLogWithSource log; |
| std::unique_ptr<ProxyResolutionRequest> request; |
| int result = service()->ResolveProxy(kResourceUrl, std::string(), |
| NetworkAnonymizationKey(), &info, |
| callback.callback(), &request, log); |
| |
| ASSERT_THAT(result, IsError(ERR_IO_PENDING)); |
| ASSERT_NE(request, nullptr); |
| |
| // Wait for result to come back. |
| EXPECT_THAT(callback.GetResult(result), IsOk()); |
| |
| EXPECT_TRUE(info.is_direct()); |
| EXPECT_NE(request, nullptr); |
| } |
| |
| TEST_F(WindowsSystemProxyResolutionServiceTest, ResolveProxyCancelled) { |
| // Make sure there would be a proxy result on success. |
| const ProxyServer proxy_server = |
| PacResultElementToProxyServer("HTTPS foopy:8443"); |
| resolver()->add_server_to_proxy_list(proxy_server); |
| |
| ProxyInfo info; |
| TestCompletionCallback callback; |
| NetLogWithSource log; |
| std::unique_ptr<ProxyResolutionRequest> request; |
| int result = service()->ResolveProxy(kResourceUrl, std::string(), |
| NetworkAnonymizationKey(), &info, |
| callback.callback(), &request, log); |
| |
| ASSERT_THAT(result, IsError(ERR_IO_PENDING)); |
| ASSERT_NE(request, nullptr); |
| |
| // Cancel the request. |
| request.reset(); |
| |
| // The proxy shouldn't resolve. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(callback.have_result()); |
| } |
| |
| TEST_F(WindowsSystemProxyResolutionServiceTest, ResolveProxyEmptyResults) { |
| ProxyList expected_proxy_list; |
| DoResolveProxyTest(expected_proxy_list); |
| } |
| |
| TEST_F(WindowsSystemProxyResolutionServiceTest, ResolveProxyWithResults) { |
| ProxyList expected_proxy_list; |
| const ProxyServer proxy_server = |
| PacResultElementToProxyServer("HTTPS foopy:8443"); |
| resolver()->add_server_to_proxy_list(proxy_server); |
| expected_proxy_list.AddProxyServer(proxy_server); |
| |
| DoResolveProxyTest(expected_proxy_list); |
| } |
| |
| TEST_F(WindowsSystemProxyResolutionServiceTest, |
| MultipleProxyResolutionRequests) { |
| ProxyList expected_proxy_list; |
| const ProxyServer proxy_server = |
| PacResultElementToProxyServer("HTTPS foopy:8443"); |
| resolver()->add_server_to_proxy_list(proxy_server); |
| expected_proxy_list.AddProxyServer(proxy_server); |
| NetLogWithSource log; |
| |
| ProxyInfo first_proxy_info; |
| TestCompletionCallback first_callback; |
| std::unique_ptr<ProxyResolutionRequest> first_request; |
| int result = service()->ResolveProxy( |
| kResourceUrl, std::string(), NetworkAnonymizationKey(), &first_proxy_info, |
| first_callback.callback(), &first_request, log); |
| ASSERT_THAT(result, IsError(ERR_IO_PENDING)); |
| ASSERT_NE(first_request, nullptr); |
| |
| ProxyInfo second_proxy_info; |
| TestCompletionCallback second_callback; |
| std::unique_ptr<ProxyResolutionRequest> second_request; |
| result = service()->ResolveProxy( |
| kResourceUrl, std::string(), NetworkAnonymizationKey(), &second_proxy_info, |
| second_callback.callback(), &second_request, log); |
| ASSERT_THAT(result, IsError(ERR_IO_PENDING)); |
| ASSERT_NE(second_request, nullptr); |
| |
| // Wait for results to come back. |
| EXPECT_THAT(first_callback.GetResult(result), IsOk()); |
| EXPECT_THAT(second_callback.GetResult(result), IsOk()); |
| |
| EXPECT_TRUE(expected_proxy_list.Equals(first_proxy_info.proxy_list())); |
| EXPECT_NE(first_request, nullptr); |
| EXPECT_TRUE(expected_proxy_list.Equals(second_proxy_info.proxy_list())); |
| EXPECT_NE(second_request, nullptr); |
| } |
| |
| TEST_F(WindowsSystemProxyResolutionServiceTest, |
| ProxyResolutionServiceDestructionWithInFlightRequests) { |
| ProxyList expected_proxy_list; |
| const ProxyServer proxy_server = |
| PacResultElementToProxyServer("HTTPS foopy:8443"); |
| resolver()->add_server_to_proxy_list(proxy_server); |
| expected_proxy_list.AddProxyServer(proxy_server); |
| NetLogWithSource log; |
| |
| ProxyInfo first_proxy_info; |
| TestCompletionCallback first_callback; |
| std::unique_ptr<ProxyResolutionRequest> first_request; |
| int result = service()->ResolveProxy( |
| kResourceUrl, std::string(), NetworkAnonymizationKey(), &first_proxy_info, |
| first_callback.callback(), &first_request, log); |
| ASSERT_THAT(result, IsError(ERR_IO_PENDING)); |
| ASSERT_NE(first_request, nullptr); |
| |
| ProxyInfo second_proxy_info; |
| TestCompletionCallback second_callback; |
| std::unique_ptr<ProxyResolutionRequest> second_request; |
| result = service()->ResolveProxy( |
| kResourceUrl, std::string(), NetworkAnonymizationKey(), &second_proxy_info, |
| second_callback.callback(), &second_request, log); |
| ASSERT_THAT(result, IsError(ERR_IO_PENDING)); |
| ASSERT_NE(second_request, nullptr); |
| |
| // There are now 2 in-flight proxy resolution requests. Deleting the proxy |
| // resolution service should call the callbacks immediately and do any |
| // appropriate error handling. |
| ResetProxyResolutionService(); |
| EXPECT_TRUE(first_callback.have_result()); |
| EXPECT_TRUE(second_callback.have_result()); |
| |
| EXPECT_TRUE(first_proxy_info.is_direct()); |
| EXPECT_TRUE(second_proxy_info.is_direct()); |
| } |
| |
| TEST_F(WindowsSystemProxyResolutionServiceTest, |
| CastToConfiguredProxyResolutionService) { |
| auto configured_service = ConfiguredProxyResolutionService::CreateDirect(); |
| ConfiguredProxyResolutionService* casted_service = configured_service.get(); |
| EXPECT_FALSE( |
| service()->CastToConfiguredProxyResolutionService(&casted_service)); |
| EXPECT_EQ(nullptr, casted_service); |
| } |
| |
| } // namespace net |