blob: 7e312c2eba3c343af0419868149eb6309f50d57b [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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 <limits>
#include <memory>
#include <string>
#include <unordered_map>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/sequence_checker.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "net/base/network_isolation_key.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_api_wrapper.h"
#include "net/test/gtest_util.h"
#include "net/test/test_with_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.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 MockWindowsSystemProxyResolver : public WindowsSystemProxyResolver {
public:
MockWindowsSystemProxyResolver() : WindowsSystemProxyResolver(nullptr) {}
void set_get_proxy_for_url_success(bool get_proxy_for_url_success) {
get_proxy_for_url_success_ = get_proxy_for_url_success;
}
bool GetProxyForUrl(WindowsSystemProxyResolutionRequest* callback_target,
const std::string& url) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!get_proxy_for_url_success_)
return false;
const int request_handle = proxy_resolver_identifier_++;
pending_callback_target_map_[callback_target] = request_handle;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&MockWindowsSystemProxyResolver::DoQueryComplete,
base::Unretained(this), callback_target,
request_handle));
return get_proxy_for_url_success_;
}
void RemovePendingCallbackTarget(
WindowsSystemProxyResolutionRequest* callback_target) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_callback_target_map_.erase(callback_target);
}
bool HasPendingCallbackTarget(
WindowsSystemProxyResolutionRequest* callback_target) const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return (pending_callback_target_map_.find(callback_target) !=
pending_callback_target_map_.end());
}
void add_server_to_proxy_list(const ProxyServer& proxy_server) {
proxy_list_.AddProxyServer(proxy_server);
}
void set_net_error(int net_error) { net_error_ = net_error; }
void set_windows_error(int windows_error) { windows_error_ = windows_error; }
private:
~MockWindowsSystemProxyResolver() override {
if (!pending_callback_target_map_.empty())
ADD_FAILURE()
<< "The WindowsSystemProxyResolutionRequests must account for all "
"pending requests in the WindowsSystemProxyResolver.";
}
void DoQueryComplete(WindowsSystemProxyResolutionRequest* callback_target,
int request_handle) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (HasPendingCallbackTarget(callback_target) &&
pending_callback_target_map_[callback_target] == request_handle)
callback_target->AsynchronousProxyResolutionComplete(
proxy_list_, net_error_, windows_error_);
}
bool get_proxy_for_url_success_ = true;
ProxyList proxy_list_;
int net_error_ = OK;
// TODO(https://crbug.com/1032820): Add tests for the |windows_error_|
// code when it is used.
int windows_error_ = 0;
int proxy_resolver_identifier_ = 1;
std::unordered_map<WindowsSystemProxyResolutionRequest*, int>
pending_callback_target_map_;
SEQUENCE_CHECKER(sequence_checker_);
};
scoped_refptr<WindowsSystemProxyResolver>
CreateWindowsSystemProxyResolverFails() {
return nullptr;
}
} // 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+.";
}
proxy_resolver_ = base::MakeRefCounted<MockWindowsSystemProxyResolver>();
proxy_resolution_service_ =
WindowsSystemProxyResolutionService::Create(/*net_log=*/nullptr);
proxy_resolution_service_->SetWindowsSystemProxyResolverForTesting(
proxy_resolver_);
}
WindowsSystemProxyResolutionService* service() {
return proxy_resolution_service_.get();
}
scoped_refptr<MockWindowsSystemProxyResolver> resolver() {
return proxy_resolver_;
}
size_t PendingRequestSizeForTesting() {
return proxy_resolution_service_->PendingRequestSizeForTesting();
}
void ResetProxyResolutionService() { proxy_resolution_service_.reset(); }
void DoResolveProxyCompletedSynchronouslyTest() {
// Make sure there would be a proxy result on success.
const ProxyServer proxy_server =
ProxyServer::FromPacString("HTTPS foopy:8443");
resolver()->add_server_to_proxy_list(proxy_server);
ProxyInfo info;
TestCompletionCallback callback;
NetLogWithSource log;
std::unique_ptr<ProxyResolutionRequest> request;
const int result = service()->ResolveProxy(
kResourceUrl, std::string(), NetworkIsolationKey(), &info,
callback.callback(), &request, log);
EXPECT_THAT(result, IsOk());
EXPECT_TRUE(info.is_direct());
EXPECT_FALSE(callback.have_result());
EXPECT_EQ(PendingRequestSizeForTesting(), 0u);
EXPECT_EQ(request, nullptr);
}
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(),
NetworkIsolationKey(), &info,
callback.callback(), &request, log);
ASSERT_THAT(result, IsError(ERR_IO_PENDING));
ASSERT_EQ(PendingRequestSizeForTesting(), 1u);
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_EQ(PendingRequestSizeForTesting(), 0u);
EXPECT_NE(request, nullptr);
}
scoped_refptr<WindowsSystemProxyResolver>
CreateMockWindowsSystemProxyResolver() {
return proxy_resolver_;
}
private:
std::unique_ptr<WindowsSystemProxyResolutionService>
proxy_resolution_service_;
scoped_refptr<MockWindowsSystemProxyResolver> proxy_resolver_;
};
TEST_F(WindowsSystemProxyResolutionServiceTest,
ResolveProxyFailedToCreateResolver) {
service()->SetWindowsSystemProxyResolverForTesting(nullptr);
service()->SetCreateWindowsSystemProxyResolverFunctionForTesting(
&CreateWindowsSystemProxyResolverFails);
DoResolveProxyCompletedSynchronouslyTest();
}
TEST_F(WindowsSystemProxyResolutionServiceTest,
ResolveProxyCompletedSynchronously) {
resolver()->set_get_proxy_for_url_success(false);
DoResolveProxyCompletedSynchronouslyTest();
}
TEST_F(WindowsSystemProxyResolutionServiceTest,
ResolveProxyFailedAsynchronously) {
resolver()->set_net_error(ERR_FAILED);
// Make sure there would be a proxy result on success.
const ProxyServer proxy_server =
ProxyServer::FromPacString("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(),
NetworkIsolationKey(), &info,
callback.callback(), &request, log);
ASSERT_THAT(result, IsError(ERR_IO_PENDING));
ASSERT_EQ(PendingRequestSizeForTesting(), 1u);
ASSERT_NE(request, nullptr);
// Wait for result to come back.
EXPECT_THAT(callback.GetResult(result), IsOk());
EXPECT_TRUE(info.is_direct());
EXPECT_EQ(PendingRequestSizeForTesting(), 0u);
EXPECT_NE(request, nullptr);
}
TEST_F(WindowsSystemProxyResolutionServiceTest, ResolveProxyEmptyResults) {
ProxyList expected_proxy_list;
DoResolveProxyTest(expected_proxy_list);
}
TEST_F(WindowsSystemProxyResolutionServiceTest, ResolveProxyWithResults) {
ProxyList expected_proxy_list;
const ProxyServer proxy_server =
ProxyServer::FromPacString("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 =
ProxyServer::FromPacString("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(), NetworkIsolationKey(), &first_proxy_info,
first_callback.callback(), &first_request, log);
ASSERT_THAT(result, IsError(ERR_IO_PENDING));
ASSERT_EQ(PendingRequestSizeForTesting(), 1u);
ASSERT_NE(first_request, nullptr);
ProxyInfo second_proxy_info;
TestCompletionCallback second_callback;
std::unique_ptr<ProxyResolutionRequest> second_request;
result = service()->ResolveProxy(
kResourceUrl, std::string(), NetworkIsolationKey(), &second_proxy_info,
second_callback.callback(), &second_request, log);
ASSERT_THAT(result, IsError(ERR_IO_PENDING));
ASSERT_EQ(PendingRequestSizeForTesting(), 2u);
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);
EXPECT_EQ(PendingRequestSizeForTesting(), 0u);
}
TEST_F(WindowsSystemProxyResolutionServiceTest,
ProxyResolutionServiceDestructionWithInFlightRequests) {
ProxyList expected_proxy_list;
const ProxyServer proxy_server =
ProxyServer::FromPacString("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(), NetworkIsolationKey(), &first_proxy_info,
first_callback.callback(), &first_request, log);
ASSERT_THAT(result, IsError(ERR_IO_PENDING));
ASSERT_EQ(PendingRequestSizeForTesting(), 1u);
ASSERT_NE(first_request, nullptr);
ProxyInfo second_proxy_info;
TestCompletionCallback second_callback;
std::unique_ptr<ProxyResolutionRequest> second_request;
result = service()->ResolveProxy(
kResourceUrl, std::string(), NetworkIsolationKey(), &second_proxy_info,
second_callback.callback(), &second_request, log);
ASSERT_THAT(result, IsError(ERR_IO_PENDING));
ASSERT_EQ(PendingRequestSizeForTesting(), 2u);
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