summaryrefslogtreecommitdiff
path: root/chromium/net/proxy_resolution
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-29 10:46:47 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-11-02 12:02:10 +0000
commit99677208ff3b216fdfec551fbe548da5520cd6fb (patch)
tree476a4865c10320249360e859d8fdd3e01833b03a /chromium/net/proxy_resolution
parentc30a6232df03e1efbd9f3b226777b07e087a1122 (diff)
downloadqtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/proxy_resolution')
-rw-r--r--chromium/net/proxy_resolution/configured_proxy_resolution_service.cc46
-rw-r--r--chromium/net/proxy_resolution/configured_proxy_resolution_service.h6
-rw-r--r--chromium/net/proxy_resolution/pac_file_fetcher_impl.cc2
-rw-r--r--chromium/net/proxy_resolution/proxy_bypass_rules.cc6
-rw-r--r--chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc2
-rw-r--r--chromium/net/proxy_resolution/proxy_info.cc7
-rw-r--r--chromium/net/proxy_resolution/proxy_info.h26
-rw-r--r--chromium/net/proxy_resolution/proxy_resolution_service.h7
-rw-r--r--chromium/net/proxy_resolution/proxy_server_unittest.cc335
-rw-r--r--chromium/net/proxy_resolution/win/proxy_config_service_win.cc12
-rw-r--r--chromium/net/proxy_resolution/win/proxy_config_service_win.h2
-rw-r--r--chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc162
-rw-r--r--chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h82
-rw-r--r--chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc179
-rw-r--r--chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h74
-rw-r--r--chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc346
-rw-r--r--chromium/net/proxy_resolution/win/windows_system_proxy_resolver.cc368
-rw-r--r--chromium/net/proxy_resolution/win/windows_system_proxy_resolver.h131
-rw-r--r--chromium/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc864
-rw-r--r--chromium/net/proxy_resolution/win/winhttp_api_wrapper.cc118
-rw-r--r--chromium/net/proxy_resolution/win/winhttp_api_wrapper.h130
-rw-r--r--chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.cc41
-rw-r--r--chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.h47
23 files changed, 2565 insertions, 428 deletions
diff --git a/chromium/net/proxy_resolution/configured_proxy_resolution_service.cc b/chromium/net/proxy_resolution/configured_proxy_resolution_service.cc
index 68fc89b5455..7a7ca7518ee 100644
--- a/chromium/net/proxy_resolution/configured_proxy_resolution_service.cc
+++ b/chromium/net/proxy_resolution/configured_proxy_resolution_service.cc
@@ -25,7 +25,6 @@
#include "net/base/proxy_delegate.h"
#include "net/base/url_util.h"
#include "net/log/net_log.h"
-#include "net/log/net_log_capture_mode.h"
#include "net/log/net_log_event_type.h"
#include "net/log/net_log_util.h"
#include "net/log/net_log_with_source.h"
@@ -44,7 +43,7 @@
#elif defined(OS_IOS)
#include "net/proxy_resolution/proxy_config_service_ios.h"
#include "net/proxy_resolution/proxy_resolver_mac.h"
-#elif defined(OS_MACOSX)
+#elif defined(OS_MAC)
#include "net/proxy_resolution/proxy_config_service_mac.h"
#include "net/proxy_resolution/proxy_resolver_mac.h"
#elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
@@ -60,7 +59,7 @@ namespace net {
namespace {
-#if defined(OS_WIN) || defined(OS_IOS) || defined(OS_MACOSX) || \
+#if defined(OS_WIN) || defined(OS_APPLE) || \
(defined(OS_LINUX) && !defined(OS_CHROMEOS))
constexpr net::NetworkTrafficAnnotationTag kSystemProxyConfigTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("proxy_config_system", R"(
@@ -257,7 +256,7 @@ class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory {
std::unique_ptr<ProxyResolverFactory> CreateProxyResolverFactory() override {
#if defined(OS_WIN)
return std::make_unique<ProxyResolverFactoryWinHttp>();
-#elif defined(OS_MACOSX)
+#elif defined(OS_APPLE)
return std::make_unique<ProxyResolverFactoryMac>();
#else
NOTREACHED();
@@ -266,7 +265,7 @@ class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory {
}
static bool IsSupported() {
-#if defined(OS_WIN) || defined(OS_MACOSX)
+#if defined(OS_WIN) || defined(OS_APPLE)
return true;
#else
return false;
@@ -1348,39 +1347,38 @@ void ConfiguredProxyResolutionService::ForceReloadProxyConfig() {
ApplyProxyConfigIfAvailable();
}
-std::unique_ptr<base::DictionaryValue>
-ConfiguredProxyResolutionService::GetProxyNetLogValues(int info_sources) {
- std::unique_ptr<base::DictionaryValue> net_info_dict(
- new base::DictionaryValue());
+base::Value ConfiguredProxyResolutionService::GetProxyNetLogValues(
+ int info_sources) {
+ base::Value net_info_dict(base::Value::Type::DICTIONARY);
if (info_sources & NET_INFO_PROXY_SETTINGS) {
- std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+ base::Value dict(base::Value::Type::DICTIONARY);
if (fetched_config_)
- dict->SetKey("original", fetched_config_->value().ToValue());
+ dict.SetKey("original", fetched_config_->value().ToValue());
if (config_)
- dict->SetKey("effective", config_->value().ToValue());
+ dict.SetKey("effective", config_->value().ToValue());
- net_info_dict->Set(NetInfoSourceToString(NET_INFO_PROXY_SETTINGS),
- std::move(dict));
+ net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_PROXY_SETTINGS),
+ std::move(dict));
}
if (info_sources & NET_INFO_BAD_PROXIES) {
- auto list = std::make_unique<base::ListValue>();
+ base::Value list(base::Value::Type::LIST);
- for (auto& it : proxy_retry_info_) {
+ for (const auto& it : proxy_retry_info_) {
const std::string& proxy_uri = it.first;
const ProxyRetryInfo& retry_info = it.second;
- auto dict = std::make_unique<base::DictionaryValue>();
- dict->SetString("proxy_uri", proxy_uri);
- dict->SetString("bad_until",
- NetLog::TickCountToString(retry_info.bad_until));
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetStringKey("proxy_uri", proxy_uri);
+ dict.SetStringKey("bad_until",
+ NetLog::TickCountToString(retry_info.bad_until));
- list->Append(std::move(dict));
+ list.Append(std::move(dict));
}
- net_info_dict->Set(NetInfoSourceToString(NET_INFO_BAD_PROXIES),
- std::move(list));
+ net_info_dict.SetKey(NetInfoSourceToString(NET_INFO_BAD_PROXIES),
+ std::move(list));
}
return net_info_dict;
@@ -1402,7 +1400,7 @@ ConfiguredProxyResolutionService::CreateSystemProxyConfigService(
#elif defined(OS_IOS)
return std::make_unique<ProxyConfigServiceIOS>(
kSystemProxyConfigTrafficAnnotation);
-#elif defined(OS_MACOSX)
+#elif defined(OS_MAC)
return std::make_unique<ProxyConfigServiceMac>(
main_task_runner, kSystemProxyConfigTrafficAnnotation);
#elif defined(OS_CHROMEOS)
diff --git a/chromium/net/proxy_resolution/configured_proxy_resolution_service.h b/chromium/net/proxy_resolution/configured_proxy_resolution_service.h
index 768ca0f7530..d2ea119490b 100644
--- a/chromium/net/proxy_resolution/configured_proxy_resolution_service.h
+++ b/chromium/net/proxy_resolution/configured_proxy_resolution_service.h
@@ -171,8 +171,7 @@ class NET_EXPORT ConfiguredProxyResolutionService
void ForceReloadProxyConfig();
// ProxyResolutionService
- std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues(
- int info_sources) override;
+ base::Value GetProxyNetLogValues(int info_sources) override;
// ProxyResolutionService
bool CastToConfiguredProxyResolutionService(
@@ -363,9 +362,6 @@ class NET_EXPORT ConfiguredProxyResolutionService
base::Optional<ProxyConfigWithAnnotation> fetched_config_;
base::Optional<ProxyConfigWithAnnotation> config_;
- // The time when the proxy configuration was last read from the system.
- base::TimeTicks config_last_update_time_;
-
// Map of the known bad proxies and the information about the retry time.
ProxyRetryInfoMap proxy_retry_info_;
diff --git a/chromium/net/proxy_resolution/pac_file_fetcher_impl.cc b/chromium/net/proxy_resolution/pac_file_fetcher_impl.cc
index f31ae9a7b22..4a388eb742f 100644
--- a/chromium/net/proxy_resolution/pac_file_fetcher_impl.cc
+++ b/chromium/net/proxy_resolution/pac_file_fetcher_impl.cc
@@ -83,7 +83,7 @@ void ConvertResponseToUTF16(const std::string& charset,
// Guess the charset by looking at the BOM.
base::StringPiece bytes_str(bytes);
for (const auto& bom : kBomMappings) {
- if (bytes_str.starts_with(bom.prefix)) {
+ if (base::StartsWith(bytes_str, bom.prefix)) {
return ConvertResponseToUTF16(
bom.charset,
// Strip the BOM in the converted response.
diff --git a/chromium/net/proxy_resolution/proxy_bypass_rules.cc b/chromium/net/proxy_resolution/proxy_bypass_rules.cc
index 8c54fa92e38..1540016bed3 100644
--- a/chromium/net/proxy_resolution/proxy_bypass_rules.cc
+++ b/chromium/net/proxy_resolution/proxy_bypass_rules.cc
@@ -34,8 +34,8 @@ const char kBypassSimpleHostnames[] = "<local>";
bool IsLinkLocalIP(const GURL& url) {
// Quick fail if definitely not link-local, to avoid doing unnecessary work in
// common case.
- if (!(url.host_piece().starts_with("169.254.") ||
- url.host_piece().starts_with("["))) {
+ if (!(base::StartsWith(url.host_piece(), "169.254.") ||
+ base::StartsWith(url.host_piece(), "["))) {
return false;
}
@@ -53,7 +53,7 @@ bool IsLinkLocalIP(const GURL& url) {
// addresses. However for proxy resolving such URLs should bypass the use
// of a PAC script, since the destination is local.
bool IsIPv4MappedLoopback(const GURL& url) {
- if (!url.host_piece().starts_with("[::ffff"))
+ if (!base::StartsWith(url.host_piece(), "[::ffff"))
return false;
IPAddress ip_address;
diff --git a/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc b/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc
index 2d422c7ca58..eeee2ded55d 100644
--- a/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc
+++ b/chromium/net/proxy_resolution/proxy_config_service_linux_unittest.cc
@@ -433,7 +433,7 @@ class ProxyConfigServiceLinuxTest : public PlatformTest,
void TearDown() override {
// Delete the temporary KDE home directory.
- base::DeleteFileRecursively(user_home_);
+ base::DeletePathRecursively(user_home_);
PlatformTest::TearDown();
}
diff --git a/chromium/net/proxy_resolution/proxy_info.cc b/chromium/net/proxy_resolution/proxy_info.cc
index 0172e0edf98..2396fefa698 100644
--- a/chromium/net/proxy_resolution/proxy_info.cc
+++ b/chromium/net/proxy_resolution/proxy_info.cc
@@ -19,7 +19,6 @@ void ProxyInfo::Use(const ProxyInfo& other) {
proxy_resolve_end_time_ = other.proxy_resolve_end_time_;
proxy_list_ = other.proxy_list_;
proxy_retry_info_ = other.proxy_retry_info_;
- traffic_annotation_ = other.traffic_annotation_;
did_bypass_proxy_ = other.did_bypass_proxy_;
}
@@ -57,10 +56,6 @@ void ProxyInfo::OverrideProxyList(const ProxyList& proxy_list) {
proxy_list_ = proxy_list;
}
-void ProxyInfo::SetAlternativeProxy(const ProxyServer& proxy_server) {
- alternative_proxy_ = proxy_server;
-}
-
std::string ProxyInfo::ToPacString() const {
return proxy_list_.ToPacString();
}
@@ -82,9 +77,7 @@ void ProxyInfo::Reset() {
proxy_resolve_start_time_ = base::TimeTicks();
proxy_resolve_end_time_ = base::TimeTicks();
proxy_list_.Clear();
- alternative_proxy_ = net::ProxyServer();
proxy_retry_info_.clear();
- traffic_annotation_.reset();
did_bypass_proxy_ = false;
}
diff --git a/chromium/net/proxy_resolution/proxy_info.h b/chromium/net/proxy_resolution/proxy_info.h
index 76558417d35..69cae35f612 100644
--- a/chromium/net/proxy_resolution/proxy_info.h
+++ b/chromium/net/proxy_resolution/proxy_info.h
@@ -67,10 +67,6 @@ class NET_EXPORT ProxyInfo {
// proxy configuration.
void OverrideProxyList(const ProxyList& proxy_list);
- // Sets the alternative service to try when connecting to the first valid
- // proxy server, but does not otherwise reset the proxy configuration.
- void SetAlternativeProxy(const ProxyServer& proxy_server);
-
// Returns true if this proxy info specifies a direct connection.
bool is_direct() const {
// We don't implicitly fallback to DIRECT unless it was added to the list.
@@ -90,6 +86,21 @@ class NET_EXPORT ProxyInfo {
return proxy_server().is_https();
}
+ // Returns true if the first proxy server is an HTTP compatible proxy.
+ bool is_http_like() const {
+ if (is_empty())
+ return false;
+ return proxy_server().is_http_like();
+ }
+
+ // Returns true if the first proxy server is an HTTP compatible proxy over a
+ // secure connection.
+ bool is_secure_http_like() const {
+ if (is_empty())
+ return false;
+ return proxy_server().is_secure_http_like();
+ }
+
// Returns true if the first valid proxy server is an http proxy.
bool is_http() const {
if (is_empty())
@@ -146,9 +157,6 @@ class NET_EXPORT ProxyInfo {
// Deletes any entry which doesn't have one of the specified proxy schemes.
void RemoveProxiesWithoutScheme(int scheme_bit_field);
- // Returns the alternative proxy, which may be invalid.
- const ProxyServer& alternative_proxy() const { return alternative_proxy_; }
-
void set_proxy_resolve_start_time(
const base::TimeTicks& proxy_resolve_start_time) {
proxy_resolve_start_time_ = proxy_resolve_start_time;
@@ -188,10 +196,6 @@ class NET_EXPORT ProxyInfo {
// try. If proxy_list_ is empty, then there is nothing left to fall back to.
ProxyList proxy_list_;
- // An alternative to proxy_server() (in the sense of HTTP Alternative
- // Services).
- ProxyServer alternative_proxy_;
-
// List of proxies that have been tried already.
ProxyRetryInfoMap proxy_retry_info_;
diff --git a/chromium/net/proxy_resolution/proxy_resolution_service.h b/chromium/net/proxy_resolution/proxy_resolution_service.h
index 4a49fc8caa7..93673f76126 100644
--- a/chromium/net/proxy_resolution/proxy_resolution_service.h
+++ b/chromium/net/proxy_resolution/proxy_resolution_service.h
@@ -18,10 +18,6 @@
#include "net/proxy_resolution/proxy_info.h"
#include "url/gurl.h"
-namespace base {
-class DictionaryValue;
-} // namespace base
-
namespace net {
class ConfiguredProxyResolutionService;
@@ -99,8 +95,7 @@ class NET_EXPORT ProxyResolutionService {
// Returns proxy related debug information to be included in the NetLog. The
// data should be appropriate for any capture mode. |info_sources| is a bit
// field of NET_INFO_SOURCE.
- virtual std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues(
- int info_sources) = 0;
+ virtual base::Value GetProxyNetLogValues(int info_sources) = 0;
// Returns true if |this| is an instance of ConfiguredProxyResolutionService
// and assigns |this| to the out parameter. Otherwise returns false and sets
diff --git a/chromium/net/proxy_resolution/proxy_server_unittest.cc b/chromium/net/proxy_resolution/proxy_server_unittest.cc
deleted file mode 100644
index e0b9c91c660..00000000000
--- a/chromium/net/proxy_resolution/proxy_server_unittest.cc
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright (c) 2010 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/base/proxy_server.h"
-#include "base/stl_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-namespace {
-
-// Test the creation of ProxyServer using ProxyServer::FromURI, which parses
-// inputs of the form [<scheme>"://"]<host>[":"<port>]. Verify that each part
-// was labelled correctly, and the accessors all give the right data.
-TEST(ProxyServerTest, FromURI) {
- const struct {
- const char* const input_uri;
- const char* const expected_uri;
- ProxyServer::Scheme expected_scheme;
- const char* const expected_host;
- int expected_port;
- const char* const expected_pac_string;
- } tests[] = {
- // HTTP proxy URIs:
- {"foopy:10", // No scheme.
- "foopy:10",
- ProxyServer::SCHEME_HTTP,
- "foopy",
- 10,
- "PROXY foopy:10"},
- {"http://foopy", // No port.
- "foopy:80",
- ProxyServer::SCHEME_HTTP,
- "foopy",
- 80,
- "PROXY foopy:80"},
- {"http://foopy:10",
- "foopy:10",
- ProxyServer::SCHEME_HTTP,
- "foopy",
- 10,
- "PROXY foopy:10"},
-
- // IPv6 HTTP proxy URIs:
- {"[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10", // No scheme.
- "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10",
- ProxyServer::SCHEME_HTTP,
- "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210",
- 10,
- "PROXY [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:10"},
- {"http://[3ffe:2a00:100:7031::1]", // No port.
- "[3ffe:2a00:100:7031::1]:80",
- ProxyServer::SCHEME_HTTP,
- "3ffe:2a00:100:7031::1",
- 80,
- "PROXY [3ffe:2a00:100:7031::1]:80"},
- {"http://[::192.9.5.5]",
- "[::192.9.5.5]:80",
- ProxyServer::SCHEME_HTTP,
- "::192.9.5.5",
- 80,
- "PROXY [::192.9.5.5]:80"},
- {"http://[::FFFF:129.144.52.38]:80",
- "[::FFFF:129.144.52.38]:80",
- ProxyServer::SCHEME_HTTP,
- "::FFFF:129.144.52.38",
- 80,
- "PROXY [::FFFF:129.144.52.38]:80"},
-
- // SOCKS4 proxy URIs:
- {"socks4://foopy", // No port.
- "socks4://foopy:1080",
- ProxyServer::SCHEME_SOCKS4,
- "foopy",
- 1080,
- "SOCKS foopy:1080"},
- {"socks4://foopy:10",
- "socks4://foopy:10",
- ProxyServer::SCHEME_SOCKS4,
- "foopy",
- 10,
- "SOCKS foopy:10"},
-
- // SOCKS5 proxy URIs
- {"socks5://foopy", // No port.
- "socks5://foopy:1080",
- ProxyServer::SCHEME_SOCKS5,
- "foopy",
- 1080,
- "SOCKS5 foopy:1080"},
- {"socks5://foopy:10",
- "socks5://foopy:10",
- ProxyServer::SCHEME_SOCKS5,
- "foopy",
- 10,
- "SOCKS5 foopy:10"},
-
- // SOCKS proxy URIs (should default to SOCKS5)
- {"socks://foopy", // No port.
- "socks5://foopy:1080",
- ProxyServer::SCHEME_SOCKS5,
- "foopy",
- 1080,
- "SOCKS5 foopy:1080"},
- {"socks://foopy:10",
- "socks5://foopy:10",
- ProxyServer::SCHEME_SOCKS5,
- "foopy",
- 10,
- "SOCKS5 foopy:10"},
-
- // HTTPS proxy URIs:
- {"https://foopy", // No port
- "https://foopy:443",
- ProxyServer::SCHEME_HTTPS,
- "foopy",
- 443,
- "HTTPS foopy:443"},
- {"https://foopy:10", // Non-standard port
- "https://foopy:10",
- ProxyServer::SCHEME_HTTPS,
- "foopy",
- 10,
- "HTTPS foopy:10"},
- {"https://1.2.3.4:10", // IP Address
- "https://1.2.3.4:10",
- ProxyServer::SCHEME_HTTPS,
- "1.2.3.4",
- 10,
- "HTTPS 1.2.3.4:10"},
- };
-
- for (size_t i = 0; i < base::size(tests); ++i) {
- ProxyServer uri =
- ProxyServer::FromURI(tests[i].input_uri, ProxyServer::SCHEME_HTTP);
- EXPECT_TRUE(uri.is_valid());
- EXPECT_FALSE(uri.is_direct());
- EXPECT_EQ(tests[i].expected_uri, uri.ToURI());
- EXPECT_EQ(tests[i].expected_scheme, uri.scheme());
- EXPECT_EQ(tests[i].expected_host, uri.host_port_pair().host());
- EXPECT_EQ(tests[i].expected_port, uri.host_port_pair().port());
- EXPECT_EQ(tests[i].expected_pac_string, uri.ToPacString());
- }
-}
-
-TEST(ProxyServerTest, DefaultConstructor) {
- ProxyServer proxy_server;
- EXPECT_FALSE(proxy_server.is_valid());
-}
-
-// Test parsing of the special URI form "direct://". Analagous to the "DIRECT"
-// entry in a PAC result.
-TEST(ProxyServerTest, Direct) {
- ProxyServer uri = ProxyServer::FromURI("direct://", ProxyServer::SCHEME_HTTP);
- EXPECT_TRUE(uri.is_valid());
- EXPECT_TRUE(uri.is_direct());
- EXPECT_EQ("direct://", uri.ToURI());
- EXPECT_EQ("DIRECT", uri.ToPacString());
-}
-
-// Test parsing some invalid inputs.
-TEST(ProxyServerTest, Invalid) {
- const char* const tests[] = {
- "",
- " ",
- "dddf:", // not a valid port
- "dddd:d", // not a valid port
- "http://", // not a valid host/port.
- "direct://xyz", // direct is not allowed a host/port.
- "http:/", // ambiguous, but will fail because of bad port.
- "http:", // ambiguous, but will fail because of bad port.
- };
-
- for (size_t i = 0; i < base::size(tests); ++i) {
- ProxyServer uri = ProxyServer::FromURI(tests[i], ProxyServer::SCHEME_HTTP);
- EXPECT_FALSE(uri.is_valid());
- EXPECT_FALSE(uri.is_direct());
- EXPECT_FALSE(uri.is_http());
- EXPECT_FALSE(uri.is_socks());
- }
-}
-
-// Test that LWS (SP | HT) is disregarded from the ends.
-TEST(ProxyServerTest, Whitespace) {
- const char* const tests[] = {
- " foopy:80",
- "foopy:80 \t",
- " \tfoopy:80 ",
- };
-
- for (size_t i = 0; i < base::size(tests); ++i) {
- ProxyServer uri = ProxyServer::FromURI(tests[i], ProxyServer::SCHEME_HTTP);
- EXPECT_EQ("foopy:80", uri.ToURI());
- }
-}
-
-// Test parsing a ProxyServer from a PAC representation.
-TEST(ProxyServerTest, FromPACString) {
- const struct {
- const char* const input_pac;
- const char* const expected_uri;
- } tests[] = {
- {
- "PROXY foopy:10",
- "foopy:10",
- },
- {
- " PROXY foopy:10 ",
- "foopy:10",
- },
- {
- "pRoXy foopy:10",
- "foopy:10",
- },
- {
- "PROXY foopy", // No port.
- "foopy:80",
- },
- {
- "socks foopy",
- "socks4://foopy:1080",
- },
- {
- "socks4 foopy",
- "socks4://foopy:1080",
- },
- {
- "socks5 foopy",
- "socks5://foopy:1080",
- },
- {
- "socks5 foopy:11",
- "socks5://foopy:11",
- },
- {
- " direct ",
- "direct://",
- },
- {
- "https foopy",
- "https://foopy:443",
- },
- {
- "https foopy:10",
- "https://foopy:10",
- },
- };
-
- for (size_t i = 0; i < base::size(tests); ++i) {
- ProxyServer uri = ProxyServer::FromPacString(tests[i].input_pac);
- EXPECT_TRUE(uri.is_valid());
- EXPECT_EQ(tests[i].expected_uri, uri.ToURI());
- }
-}
-
-// Test parsing a ProxyServer from an invalid PAC representation.
-TEST(ProxyServerTest, FromPACStringInvalid) {
- const char* const tests[] = {
- "PROXY", // missing host/port.
- "HTTPS", // missing host/port.
- "SOCKS", // missing host/port.
- "DIRECT foopy:10", // direct cannot have host/port.
- };
-
- for (size_t i = 0; i < base::size(tests); ++i) {
- ProxyServer uri = ProxyServer::FromPacString(tests[i]);
- EXPECT_FALSE(uri.is_valid());
- }
-}
-
-TEST(ProxyServerTest, ComparatorAndEquality) {
- const struct {
- // Inputs.
- ProxyServer server1;
- ProxyServer server2;
-
- // Expectation.
- // -1 means server1 is less than server2
- // 0 means server1 equals server2
- // 1 means server1 is greater than server2
- int expected_comparison;
- } kTests[] = {
- {// Equal.
- ProxyServer::FromURI("foo:11", ProxyServer::SCHEME_HTTP),
- ProxyServer::FromURI("http://foo:11", ProxyServer::SCHEME_HTTP), 0},
- {// Port is different.
- ProxyServer::FromURI("foo:333", ProxyServer::SCHEME_HTTP),
- ProxyServer::FromURI("foo:444", ProxyServer::SCHEME_HTTP), -1},
- {// Host is different.
- ProxyServer::FromURI("foo:33", ProxyServer::SCHEME_HTTP),
- ProxyServer::FromURI("bar:33", ProxyServer::SCHEME_HTTP), 1},
- {// Scheme is different.
- ProxyServer::FromURI("socks4://foo:33", ProxyServer::SCHEME_HTTP),
- ProxyServer::FromURI("http://foo:33", ProxyServer::SCHEME_HTTP), 1},
- {// Trusted is different.
- ProxyServer(ProxyServer::SCHEME_HTTPS, HostPortPair("foo", 33),
- false /* is_trusted_proxy */),
- ProxyServer(ProxyServer::SCHEME_HTTPS, HostPortPair("foo", 33),
- true /* is_trusted_proxy */),
- -1},
- };
-
- for (const auto& test : kTests) {
- EXPECT_TRUE(test.server1.is_valid());
- EXPECT_TRUE(test.server2.is_valid());
-
- switch (test.expected_comparison) {
- case -1:
- EXPECT_TRUE(test.server1 < test.server2);
- EXPECT_FALSE(test.server2 < test.server1);
- EXPECT_FALSE(test.server2 == test.server1);
- EXPECT_FALSE(test.server1 == test.server2);
- break;
- case 0:
- EXPECT_FALSE(test.server1 < test.server2);
- EXPECT_FALSE(test.server2 < test.server1);
- EXPECT_TRUE(test.server2 == test.server1);
- EXPECT_TRUE(test.server1 == test.server2);
- break;
- case 1:
- EXPECT_FALSE(test.server1 < test.server2);
- EXPECT_TRUE(test.server2 < test.server1);
- EXPECT_FALSE(test.server2 == test.server1);
- EXPECT_FALSE(test.server1 == test.server2);
- break;
- default:
- FAIL() << "Invalid expectation. Can be only -1, 0, 1";
- }
- }
-}
-
-} // namespace
-
-} // namespace net
diff --git a/chromium/net/proxy_resolution/win/proxy_config_service_win.cc b/chromium/net/proxy_resolution/win/proxy_config_service_win.cc
index 04dd08d5f57..5c7b72a216d 100644
--- a/chromium/net/proxy_resolution/win/proxy_config_service_win.cc
+++ b/chromium/net/proxy_resolution/win/proxy_config_service_win.cc
@@ -95,21 +95,19 @@ void ProxyConfigServiceWin::StartWatchingRegistryForChanges() {
AddKeyToWatchList(
HKEY_CURRENT_USER,
- STRING16_LITERAL("Software\\Microsoft\\Windows\\CurrentVersion\\")
- STRING16_LITERAL("Internet Settings"));
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
AddKeyToWatchList(
HKEY_LOCAL_MACHINE,
- STRING16_LITERAL("Software\\Microsoft\\Windows\\CurrentVersion\\")
- STRING16_LITERAL("Internet Settings"));
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
AddKeyToWatchList(HKEY_LOCAL_MACHINE,
- STRING16_LITERAL("SOFTWARE\\Policies\\Microsoft\\Windows\\")
- STRING16_LITERAL("CurrentVersion\\Internet Settings"));
+ L"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\"
+ L"Internet Settings");
}
bool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey,
- const base::char16* subkey) {
+ const wchar_t* subkey) {
std::unique_ptr<base::win::RegKey> key =
std::make_unique<base::win::RegKey>();
if (key->Create(rootkey, subkey, KEY_NOTIFY) != ERROR_SUCCESS)
diff --git a/chromium/net/proxy_resolution/win/proxy_config_service_win.h b/chromium/net/proxy_resolution/win/proxy_config_service_win.h
index 937c2028c93..de544576add 100644
--- a/chromium/net/proxy_resolution/win/proxy_config_service_win.h
+++ b/chromium/net/proxy_resolution/win/proxy_config_service_win.h
@@ -70,7 +70,7 @@ class NET_EXPORT_PRIVATE ProxyConfigServiceWin
// Creates a new key and appends it to |keys_to_watch_|. If the key fails to
// be created, it is not appended to the list and we return false.
- bool AddKeyToWatchList(HKEY rootkey, const base::char16* subkey);
+ bool AddKeyToWatchList(HKEY rootkey, const wchar_t* subkey);
// This is called whenever one of the registry keys we are watching change.
void OnObjectSignaled(base::win::RegKey* key);
diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc
index 707c427ba8f..742f1534346 100644
--- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc
+++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.cc
@@ -4,15 +4,167 @@
#include "net/proxy_resolution/win/windows_system_proxy_resolution_request.h"
+#include <utility>
+
+#include "net/base/net_errors.h"
+#include "net/proxy_resolution/proxy_info.h"
+#include "net/proxy_resolution/proxy_list.h"
+#include "net/proxy_resolution/win/windows_system_proxy_resolution_service.h"
+#include "net/proxy_resolution/win/windows_system_proxy_resolver.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+
namespace net {
-WindowsSystemProxyResolutionRequest::WindowsSystemProxyResolutionRequest() =
- default;
-WindowsSystemProxyResolutionRequest::~WindowsSystemProxyResolutionRequest() =
- default;
+namespace {
+
+constexpr net::NetworkTrafficAnnotationTag kWindowsResolverTrafficAnnotation =
+ net::DefineNetworkTrafficAnnotation("proxy_config_windows_resolver", R"(
+ semantics {
+ sender: "Proxy Config for Windows System Resolver"
+ description:
+ "Establishing a connection through a proxy server using system proxy "
+ "settings and Windows system proxy resolution code."
+ trigger:
+ "Whenever a network request is made when the system proxy settings "
+ "are used, the Windows system proxy resolver is enabled, and the "
+ "result indicates usage of a proxy server."
+ data:
+ "Proxy configuration."
+ destination: OTHER
+ destination_other:
+ "The proxy server specified in the configuration."
+ }
+ policy {
+ cookies_allowed: NO
+ setting:
+ "User cannot override system proxy settings, but can change them "
+ "through 'Advanced/System/Open proxy settings'."
+ policy_exception_justification:
+ "Using either of 'ProxyMode', 'ProxyServer', or 'ProxyPacUrl' "
+ "policies can set Chrome to use a specific proxy settings and avoid "
+ "system proxy."
+ })");
+
+} // namespace
+
+WindowsSystemProxyResolutionRequest::WindowsSystemProxyResolutionRequest(
+ WindowsSystemProxyResolutionService* service,
+ const GURL& url,
+ const std::string& method,
+ ProxyInfo* results,
+ CompletionOnceCallback user_callback,
+ const NetLogWithSource& net_log,
+ scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver)
+ : windows_system_proxy_resolver_(windows_system_proxy_resolver),
+ service_(service),
+ user_callback_(std::move(user_callback)),
+ results_(results),
+ url_(url),
+ method_(method),
+ net_log_(net_log),
+ creation_time_(base::TimeTicks::Now()) {
+ DCHECK(!user_callback_.is_null());
+ DCHECK(windows_system_proxy_resolver_);
+}
+
+WindowsSystemProxyResolutionRequest::~WindowsSystemProxyResolutionRequest() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (service_) {
+ service_->RemovePendingRequest(this);
+ net_log_.AddEvent(NetLogEventType::CANCELLED);
+
+ if (IsStarted())
+ CancelResolveJob();
+
+ net_log_.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
+ }
+}
LoadState WindowsSystemProxyResolutionRequest::GetLoadState() const {
- return LOAD_STATE_IDLE;
+ // TODO(https://crbug.com/1032820): Consider adding a LoadState for "We're
+ // waiting on system APIs to do their thing".
+ return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
+}
+
+int WindowsSystemProxyResolutionRequest::Start() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!was_completed());
+ DCHECK(!IsStarted());
+
+ // Kicks off an asynchronous call that'll eventually call back into
+ // AsynchronousProxyResolutionComplete() with a result.
+ if (!windows_system_proxy_resolver_->GetProxyForUrl(this, url_.spec()))
+ return ERR_FAILED;
+
+ // Asynchronous proxy resolution has begun.
+ return ERR_IO_PENDING;
+}
+
+void WindowsSystemProxyResolutionRequest::CancelResolveJob() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(IsStarted());
+ // The request may already be running in the resolver.
+ // TODO(https://crbug.com/1032820): Cancel callback instead of just ignoring
+ // it.
+ windows_system_proxy_resolver_->RemovePendingCallbackTarget(this);
+ DCHECK(!IsStarted());
+}
+
+bool WindowsSystemProxyResolutionRequest::IsStarted() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return windows_system_proxy_resolver_->HasPendingCallbackTarget(this);
+}
+
+int WindowsSystemProxyResolutionRequest::UpdateResultsOnProxyResolutionComplete(
+ const ProxyList& proxy_list,
+ int net_error) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!was_completed());
+
+ results_->UseProxyList(proxy_list);
+
+ // Make sure IsStarted() returns false while DidFinishResolvingProxy() runs.
+ windows_system_proxy_resolver_->RemovePendingCallbackTarget(this);
+
+ // Note that DidFinishResolvingProxy might modify |results_|.
+ const int updated_result = service_->DidFinishResolvingProxy(
+ url_, method_, results_, net_error, net_log_);
+
+ // Make a note in the results which configuration was in use at the
+ // time of the resolve.
+ results_->set_proxy_resolve_start_time(creation_time_);
+ results_->set_proxy_resolve_end_time(base::TimeTicks::Now());
+ results_->set_traffic_annotation(
+ MutableNetworkTrafficAnnotationTag(kWindowsResolverTrafficAnnotation));
+
+ return updated_result;
+}
+
+int WindowsSystemProxyResolutionRequest::SynchronousProxyResolutionComplete(
+ int net_error) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ ProxyList proxy_list;
+ const int updated_result =
+ UpdateResultsOnProxyResolutionComplete(proxy_list, net_error);
+ service_ = nullptr;
+ return updated_result;
+}
+
+void WindowsSystemProxyResolutionRequest::AsynchronousProxyResolutionComplete(
+ const ProxyList& proxy_list,
+ int net_error,
+ int windows_error) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // TODO(https://crbug.com/1032820): Log Windows error |windows_error|.
+
+ net_error = UpdateResultsOnProxyResolutionComplete(proxy_list, net_error);
+
+ CompletionOnceCallback callback = std::move(user_callback_);
+
+ service_->RemovePendingRequest(this);
+ service_ = nullptr;
+ user_callback_.Reset();
+ std::move(callback).Run(net_error);
}
} // namespace net
diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h
index 575dcffde76..e5af2b644de 100644
--- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h
+++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_request.h
@@ -5,17 +5,40 @@
#ifndef NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLUTION_REQUEST_H_
#define NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLUTION_REQUEST_H_
+#include <memory>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+#include "base/time/time.h"
+#include "net/base/completion_once_callback.h"
+#include "net/base/net_export.h"
+#include "net/base/network_isolation_key.h"
+#include "net/log/net_log_with_source.h"
#include "net/proxy_resolution/proxy_resolution_request.h"
+#include "url/gurl.h"
namespace net {
+class ProxyInfo;
+class ProxyList;
+class WindowsSystemProxyResolutionService;
+class WindowsSystemProxyResolver;
+
// This is the concrete implementation of ProxyResolutionRequest used by
// WindowsSystemProxyResolutionService. Manages a single asynchronous proxy
// resolution request.
-class WindowsSystemProxyResolutionRequest final
+class NET_EXPORT WindowsSystemProxyResolutionRequest
: public ProxyResolutionRequest {
public:
- WindowsSystemProxyResolutionRequest();
+ WindowsSystemProxyResolutionRequest(
+ WindowsSystemProxyResolutionService* service,
+ const GURL& url,
+ const std::string& method,
+ ProxyInfo* results,
+ const CompletionOnceCallback user_callback,
+ const NetLogWithSource& net_log,
+ scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver);
WindowsSystemProxyResolutionRequest(
const WindowsSystemProxyResolutionRequest&) = delete;
@@ -24,7 +47,62 @@ class WindowsSystemProxyResolutionRequest final
~WindowsSystemProxyResolutionRequest() override;
+ // ProxyResolutionRequest
LoadState GetLoadState() const override;
+
+ // Starts the resolve proxy request.
+ int Start();
+
+ // Cancels the callback from the resolver for a previously started proxy
+ // resolution.
+ void CancelResolveJob();
+
+ bool IsStarted();
+
+ // Returns true if the request has been completed.
+ bool was_completed() const { return user_callback_.is_null(); }
+
+ // Helper to call after ProxyResolver completion (both synchronous and
+ // asynchronous). Fixes up the result that is to be returned to user.
+ int UpdateResultsOnProxyResolutionComplete(const ProxyList& proxy_list,
+ int net_error);
+
+ // Helper to call if the request completes synchronously, since in that case
+ // the request will not be added to |pending_requests_| (in
+ // WindowsSystemProxyResolutionService).
+ int SynchronousProxyResolutionComplete(int net_error);
+
+ // Callback for when the WinHttp request has completed. This is the main way
+ // that proxy resolutions will complete. The |proxy_list| is the list of
+ // proxies returned by WinHttp translated into Chromium-friendly terms. The
+ // |net_error| describes the status of the proxy resolution request. If
+ // WinHttp fails for some reason, |windows_error| contains the specific error
+ // returned by WinHttp.
+ virtual void AsynchronousProxyResolutionComplete(const ProxyList& proxy_list,
+ int net_error,
+ int windows_error);
+
+ protected:
+ // The resolver will do the work of talking to system APIs and translating the
+ // results into something Chromium understands.
+ scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver_;
+
+ private:
+ // Note that Request holds a bare pointer to the
+ // WindowsSystemProxyResolutionService. Outstanding requests are cancelled
+ // during ~WindowsSystemProxyResolutionService, so this is guaranteed to be
+ // valid throughout the lifetime of this object.
+ WindowsSystemProxyResolutionService* service_;
+ CompletionOnceCallback user_callback_;
+ ProxyInfo* results_;
+ const GURL url_;
+ const std::string method_;
+ NetLogWithSource net_log_;
+ // Time when the request was created. Stored here rather than in |results_|
+ // because the time in |results_| will be cleared.
+ base::TimeTicks creation_time_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace net
diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc
index 16fc5643558..1dcf0964681 100644
--- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc
+++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.cc
@@ -4,15 +4,68 @@
#include "net/proxy_resolution/win/windows_system_proxy_resolution_service.h"
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "base/values.h"
+#include "base/win/windows_version.h"
#include "net/base/net_errors.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_event_type.h"
+#include "net/log/net_log_with_source.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_proxy_resolver_functions.h"
namespace net {
-WindowsSystemProxyResolutionService::WindowsSystemProxyResolutionService() =
- default;
-WindowsSystemProxyResolutionService::~WindowsSystemProxyResolutionService() =
- default;
+// static
+bool WindowsSystemProxyResolutionService::IsSupported() {
+ if (base::win::GetVersion() < base::win::Version::WIN8) {
+ LOG(WARNING) << "WindowsSystemProxyResolutionService is only supported for "
+ "Windows 8 and later.";
+ return false;
+ }
+
+ if (!WinHttpProxyResolverFunctions::GetInstance()
+ .are_all_functions_loaded()) {
+ LOG(ERROR) << "Failed to load functions necessary for "
+ "WindowsSystemProxyResolutionService!";
+ return false;
+ }
+
+ return true;
+}
+
+// static
+std::unique_ptr<WindowsSystemProxyResolutionService>
+WindowsSystemProxyResolutionService::Create(NetLog* net_log) {
+ if (!IsSupported())
+ return nullptr;
+
+ return base::WrapUnique(new WindowsSystemProxyResolutionService(net_log));
+}
+
+WindowsSystemProxyResolutionService::WindowsSystemProxyResolutionService(
+ NetLog* net_log)
+ : create_proxy_resolver_function_for_testing_(nullptr), net_log_(net_log) {}
+
+WindowsSystemProxyResolutionService::~WindowsSystemProxyResolutionService() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // Cancel any in-progress requests.
+ // This cancels the internal requests, but leaves the responsibility of
+ // canceling the high-level Request (by deleting it) to the client.
+ // Since |pending_requests_| might be modified in one of the requests'
+ // callbacks (if it deletes another request), iterating through the set in a
+ // for-loop will not work.
+ while (!pending_requests_.empty()) {
+ WindowsSystemProxyResolutionRequest* req = *pending_requests_.begin();
+ ProxyList empty_list;
+ req->AsynchronousProxyResolutionComplete(empty_list, ERR_ABORTED, 0);
+ pending_requests_.erase(req);
+ }
+}
int WindowsSystemProxyResolutionService::ResolveProxy(
const GURL& url,
@@ -22,36 +75,74 @@ int WindowsSystemProxyResolutionService::ResolveProxy(
CompletionOnceCallback callback,
std::unique_ptr<ProxyResolutionRequest>* request,
const NetLogWithSource& net_log) {
- return ERR_NOT_IMPLEMENTED;
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(!callback.is_null());
+ DCHECK(request);
+
+ net_log.BeginEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
+
+ // TODO(https://crbug.com/1032820): Use a more detailed error.
+ if (!CreateWindowsSystemProxyResolverIfNeeded())
+ return DidFinishResolvingProxy(url, method, results, ERR_FAILED, net_log);
+
+ auto req = std::make_unique<WindowsSystemProxyResolutionRequest>(
+ this, url, method, results, std::move(callback), net_log,
+ windows_system_proxy_resolver_);
+
+ const int net_error = req->Start();
+ if (net_error != ERR_IO_PENDING)
+ return req->SynchronousProxyResolutionComplete(net_error);
+
+ DCHECK(!ContainsPendingRequest(req.get()));
+ pending_requests_.insert(req.get());
+
+ // Completion will be notified through |callback|, unless the caller cancels
+ // the request using |request|.
+ *request = std::move(req);
+ return net_error;
}
void WindowsSystemProxyResolutionService::ReportSuccess(
- const ProxyInfo& proxy_info) {}
+ const ProxyInfo& proxy_info) {
+ // TODO(https://crbug.com/1032820): Update proxy retry info with new proxy
+ // resolution data.
+}
void WindowsSystemProxyResolutionService::SetProxyDelegate(
- ProxyDelegate* delegate) {}
+ ProxyDelegate* delegate) {
+ // TODO(https://crbug.com/1032820): Implement proxy delegates.
+}
-void WindowsSystemProxyResolutionService::OnShutdown() {}
+void WindowsSystemProxyResolutionService::OnShutdown() {
+ // TODO(https://crbug.com/1032820): Add cleanup here as necessary. If cleanup
+ // is unnecessary, update the interface to not require an implementation for
+ // this so OnShutdown() can be removed.
+}
bool WindowsSystemProxyResolutionService::MarkProxiesAsBadUntil(
const ProxyInfo& results,
base::TimeDelta retry_delay,
const std::vector<ProxyServer>& additional_bad_proxies,
const NetLogWithSource& net_log) {
+ // TODO(https://crbug.com/1032820): Implement bad proxy cache. We should be
+ // able to share logic with the ConfiguredProxyResolutionService to accomplish
+ // this.
return false;
}
-void WindowsSystemProxyResolutionService::ClearBadProxiesCache() {}
+void WindowsSystemProxyResolutionService::ClearBadProxiesCache() {
+ proxy_retry_info_.clear();
+}
const ProxyRetryInfoMap& WindowsSystemProxyResolutionService::proxy_retry_info()
const {
return proxy_retry_info_;
}
-std::unique_ptr<base::DictionaryValue>
-WindowsSystemProxyResolutionService::GetProxyNetLogValues(int info_sources) {
- std::unique_ptr<base::DictionaryValue> net_info_dict(
- new base::DictionaryValue());
+base::Value WindowsSystemProxyResolutionService::GetProxyNetLogValues(
+ int info_sources) {
+ // TODO (https://crbug.com/1032820): Implement net logs.
+ base::Value net_info_dict(base::Value::Type::DICTIONARY);
return net_info_dict;
}
@@ -63,4 +154,66 @@ bool WindowsSystemProxyResolutionService::
return false;
}
+void WindowsSystemProxyResolutionService::
+ SetCreateWindowsSystemProxyResolverFunctionForTesting(
+ CreateWindowsSystemProxyResolverFunctionForTesting function) {
+ create_proxy_resolver_function_for_testing_ = function;
+}
+
+void WindowsSystemProxyResolutionService::
+ SetWindowsSystemProxyResolverForTesting(
+ scoped_refptr<WindowsSystemProxyResolver>
+ windows_system_proxy_resolver) {
+ windows_system_proxy_resolver_ = windows_system_proxy_resolver;
+}
+
+bool WindowsSystemProxyResolutionService::ContainsPendingRequest(
+ WindowsSystemProxyResolutionRequest* req) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return pending_requests_.count(req) == 1;
+}
+
+void WindowsSystemProxyResolutionService::RemovePendingRequest(
+ WindowsSystemProxyResolutionRequest* req) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(ContainsPendingRequest(req));
+ pending_requests_.erase(req);
+}
+
+bool WindowsSystemProxyResolutionService::
+ CreateWindowsSystemProxyResolverIfNeeded() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ if (windows_system_proxy_resolver_)
+ return true;
+
+ if (create_proxy_resolver_function_for_testing_) {
+ windows_system_proxy_resolver_ =
+ create_proxy_resolver_function_for_testing_();
+ } else {
+ windows_system_proxy_resolver_ =
+ WindowsSystemProxyResolver::CreateWindowsSystemProxyResolver();
+ }
+
+ return !!windows_system_proxy_resolver_;
+}
+
+int WindowsSystemProxyResolutionService::DidFinishResolvingProxy(
+ const GURL& url,
+ const std::string& method,
+ ProxyInfo* result,
+ int result_code,
+ const NetLogWithSource& net_log) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // TODO(https://crbug.com/1032820): Implement net logs.
+ // TODO(https://crbug.com/1032820): Implement proxy delegate.
+ // TODO(https://crbug.com/1032820): Implement proxy retry info.
+
+ if (result_code != OK)
+ result->UseDirect();
+
+ net_log.EndEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
+ return OK;
+}
+
} // namespace net
diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h
index f5b820f3f03..1e82a11d0c6 100644
--- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h
+++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service.h
@@ -7,12 +7,24 @@
#include "net/proxy_resolution/proxy_resolution_service.h"
+#include <memory>
+#include <set>
#include <string>
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
#include "net/base/net_export.h"
namespace net {
+class NetLog;
+class WindowsSystemProxyResolutionRequest;
+class WindowsSystemProxyResolver;
+
+using CreateWindowsSystemProxyResolverFunctionForTesting =
+ scoped_refptr<WindowsSystemProxyResolver> (*)();
+
// This class decides which proxy server(s) to use for a particular URL request.
// It does NOT support passing in fetched proxy configurations. Instead, it
// relies entirely on WinHttp APIs to determine the proxy that should be used
@@ -20,7 +32,14 @@ namespace net {
class NET_EXPORT WindowsSystemProxyResolutionService
: public ProxyResolutionService {
public:
- WindowsSystemProxyResolutionService();
+ // The WinHttp functions used in the resolver via the WinHttpAPIWrapper are
+ // only supported on Windows 8 and above.
+ static bool IsSupported() WARN_UNUSED_RESULT;
+
+ // Creates a WindowsSystemProxyResolutionService or returns nullptr if the
+ // runtime dependencies are not satisfied.
+ static std::unique_ptr<WindowsSystemProxyResolutionService> Create(
+ NetLog* net_log);
WindowsSystemProxyResolutionService(
const WindowsSystemProxyResolutionService&) = delete;
@@ -47,15 +66,64 @@ class NET_EXPORT WindowsSystemProxyResolutionService
const NetLogWithSource& net_log) override;
void ClearBadProxiesCache() override;
const ProxyRetryInfoMap& proxy_retry_info() const override;
- std::unique_ptr<base::DictionaryValue> GetProxyNetLogValues(
- int info_sources) override;
+ base::Value GetProxyNetLogValues(int info_sources) override;
bool CastToConfiguredProxyResolutionService(
ConfiguredProxyResolutionService** configured_proxy_resolution_service)
override WARN_UNUSED_RESULT;
+ // Used in tests to provide a fake |windows_system_proxy_resolver_|.
+ void SetCreateWindowsSystemProxyResolverFunctionForTesting(
+ CreateWindowsSystemProxyResolverFunctionForTesting function);
+ void SetWindowsSystemProxyResolverForTesting(
+ scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver);
+
private:
+ friend class WindowsSystemProxyResolutionRequest;
+ friend class WindowsSystemProxyResolutionServiceTest;
+
+ explicit WindowsSystemProxyResolutionService(NetLog* net_log);
+
+ typedef std::set<WindowsSystemProxyResolutionRequest*> PendingRequests;
+
+ bool ContainsPendingRequest(WindowsSystemProxyResolutionRequest* req)
+ WARN_UNUSED_RESULT;
+ void RemovePendingRequest(WindowsSystemProxyResolutionRequest* req);
+
+ // Lazily creates |windows_system_proxy_resolver_|.
+ bool CreateWindowsSystemProxyResolverIfNeeded() WARN_UNUSED_RESULT;
+
+ size_t PendingRequestSizeForTesting() const {
+ return pending_requests_.size();
+ }
+
+ // Called when proxy resolution has completed (either synchronously or
+ // asynchronously). Handles logging the result, and cleaning out
+ // bad entries from the results list.
+ int DidFinishResolvingProxy(const GURL& url,
+ const std::string& method,
+ ProxyInfo* result,
+ int result_code,
+ const NetLogWithSource& net_log);
+
+ CreateWindowsSystemProxyResolverFunctionForTesting
+ create_proxy_resolver_function_for_testing_;
+
// Map of the known bad proxies and the information about the retry time.
ProxyRetryInfoMap proxy_retry_info_;
+
+ // Set of pending/in-progress requests.
+ PendingRequests pending_requests_;
+
+ // This is the log for any generated events.
+ NetLog* net_log_;
+
+ // This object encapsulates all WinHttp logic in Chromium-friendly terms. It
+ // manages the lifetime of the WinHttp session (which is
+ // per-resolution-service). This will get handed off to individual resolution
+ // requests so that they can query/cancel proxy resolution as needed.
+ scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace net
diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc
index 94d3c7f8102..7e312c2eba3 100644
--- a/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc
+++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolution_service_unittest.cc
@@ -3,18 +3,356 @@
// 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 {
-TEST(WindowsSystemProxyResolutionServiceTest,
- CastToConfiguredProxyResolutionService) {
- WindowsSystemProxyResolutionService service;
+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_FALSE(
+ service()->CastToConfiguredProxyResolutionService(&casted_service));
EXPECT_EQ(nullptr, casted_service);
}
diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.cc
new file mode 100644
index 00000000000..8117b62b99a
--- /dev/null
+++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.cc
@@ -0,0 +1,368 @@
+// 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_resolver.h"
+
+#include <cwchar>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+#include "net/base/proxy_server.h"
+#include "net/proxy_resolution/win/windows_system_proxy_resolution_request.h"
+#include "net/proxy_resolution/win/winhttp_api_wrapper.h"
+#include "url/url_canon.h"
+
+namespace net {
+
+namespace {
+
+bool GetProxyServerFromWinHttpResultEntry(
+ const WINHTTP_PROXY_RESULT_ENTRY& result_entry,
+ ProxyServer* out_proxy_server) {
+ // TODO(https://crbug.com/1032820): Include net logs for proxy bypass
+ if (!result_entry.fProxy) {
+ *out_proxy_server = ProxyServer::Direct();
+ return true;
+ }
+
+ ProxyServer::Scheme scheme = ProxyServer::Scheme::SCHEME_INVALID;
+ switch (result_entry.ProxyScheme) {
+ case (INTERNET_SCHEME_HTTP):
+ scheme = ProxyServer::Scheme::SCHEME_HTTP;
+ break;
+ case (INTERNET_SCHEME_HTTPS):
+ scheme = ProxyServer::Scheme::SCHEME_HTTPS;
+ break;
+ case (INTERNET_SCHEME_SOCKS):
+ scheme = ProxyServer::Scheme::SCHEME_SOCKS4;
+ break;
+ default:
+ LOG(WARNING) << "Of the possible proxy schemes returned by WinHttp, "
+ "Chrome supports HTTP(S) and SOCKS4. The ProxyScheme "
+ "that triggered this message is: "
+ << result_entry.ProxyScheme;
+ break;
+ }
+
+ if (scheme == ProxyServer::Scheme::SCHEME_INVALID)
+ return false;
+
+ // Chrome expects a specific port from WinHttp. The WinHttp documentation on
+ // MSDN makes it unclear whether or not a specific port is guaranteed.
+ if (result_entry.ProxyPort == INTERNET_DEFAULT_PORT) {
+ LOG(WARNING) << "WinHttpGetProxyForUrlEx() returned a proxy with "
+ "INTERNET_PORT_DEFAULT!";
+ return false;
+ }
+
+ // Since there is a proxy in the result (i.e. |fProxy| is TRUE), the
+ // |pwszProxy| is guaranteed to be non-null and non-empty.
+ DCHECK(!!result_entry.pwszProxy);
+ DCHECK(wcslen(result_entry.pwszProxy) > 0);
+
+ base::string16 host_wide(result_entry.pwszProxy,
+ wcslen(result_entry.pwszProxy));
+ if (!base::IsStringASCII(host_wide)) {
+ const int kInitialBufferSize = 256;
+ url::RawCanonOutputT<base::char16, kInitialBufferSize> punycode_output;
+ if (!url::IDNToASCII(host_wide.data(), host_wide.length(),
+ &punycode_output))
+ return false;
+
+ host_wide.assign(punycode_output.data(), punycode_output.length());
+ }
+
+ // At this point the string in |host_wide| is ASCII.
+ std::string host;
+ if (!base::UTF16ToUTF8(host_wide.data(), host_wide.length(), &host))
+ return false;
+
+ HostPortPair host_and_port(host, result_entry.ProxyPort);
+ *out_proxy_server = ProxyServer(scheme, host_and_port);
+ return true;
+}
+
+} // namespace
+
+// static
+scoped_refptr<WindowsSystemProxyResolver>
+WindowsSystemProxyResolver::CreateWindowsSystemProxyResolver() {
+ scoped_refptr<WindowsSystemProxyResolver> resolver = base::WrapRefCounted(
+ new WindowsSystemProxyResolver(std::make_unique<WinHttpAPIWrapper>()));
+ if (resolver->Initialize()) {
+ return resolver;
+ }
+ return nullptr;
+}
+
+WindowsSystemProxyResolver::WindowsSystemProxyResolver(
+ std::unique_ptr<WinHttpAPIWrapper> winhttp_api_wrapper)
+ : winhttp_api_wrapper_(std::move(winhttp_api_wrapper)),
+ sequenced_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+WindowsSystemProxyResolver::~WindowsSystemProxyResolver() = default;
+
+bool WindowsSystemProxyResolver::Initialize() {
+ if (!winhttp_api_wrapper_->CallWinHttpOpen())
+ return false;
+
+ // Since this session handle will never be used for WinHTTP connections,
+ // these timeouts don't really mean much individually. However, WinHTTP's
+ // out of process PAC resolution will use a combined (sum of all timeouts)
+ // value to wait for an RPC reply.
+ if (!winhttp_api_wrapper_->CallWinHttpSetTimeouts(10000, 10000, 5000, 5000))
+ return false;
+
+ // This sets the entry point for every callback in the WinHttp session created
+ // above.
+ if (!winhttp_api_wrapper_->CallWinHttpSetStatusCallback(
+ &WindowsSystemProxyResolver::WinHttpStatusCallback))
+ return false;
+
+ return true;
+}
+
+bool WindowsSystemProxyResolver::GetProxyForUrl(
+ WindowsSystemProxyResolutionRequest* callback_target,
+ const std::string& url) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // Fetch the current system proxy settings. These are per current network
+ // interface and per current user.
+ ScopedIEConfig scoped_ie_config;
+ if (!winhttp_api_wrapper_->CallWinHttpGetIEProxyConfigForCurrentUser(
+ scoped_ie_config.config()))
+ return false;
+
+ // This will create a handle specifically for WinHttpGetProxyForUrlEx().
+ HINTERNET resolver_handle = nullptr;
+ if (!winhttp_api_wrapper_->CallWinHttpCreateProxyResolver(&resolver_handle))
+ return false;
+
+ // WinHttp will do all necessary proxy resolution fallback for this request.
+ // If automatic settings aren't configured or fail, it'll use any manually
+ // configured proxies on the machine. The WINHTTP_AUTOPROXY_ALLOW_STATIC flag
+ // tells the APIs to pick up manually configured proxies.
+ //
+ // Separately, Windows allows different proxy settings for different network
+ // interfaces. The WINHTTP_AUTOPROXY_OPTIONS flag tells WinHttp to
+ // differentiate between these settings and to get the proxy that's most
+ // specific to the current interface.
+ WINHTTP_AUTOPROXY_OPTIONS autoproxy_options = {0};
+ autoproxy_options.dwFlags =
+ WINHTTP_AUTOPROXY_ALLOW_STATIC | WINHTTP_AUTOPROXY_ALLOW_CM;
+
+ // The fAutoLogonIfChallenged option has been deprecated and should always be
+ // set to FALSE throughout Windows 10. Even in earlier versions of the OS,
+ // this feature did not work particularly well.
+ // https://support.microsoft.com/en-us/help/3161949/ms16-077-description-of-the-security-update-for-wpad-june-14-2016
+ autoproxy_options.fAutoLogonIfChallenged = FALSE;
+
+ // Sets a specific PAC URL if there was one in the IE configs.
+ if (scoped_ie_config.config()->lpszAutoConfigUrl) {
+ autoproxy_options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
+ autoproxy_options.lpszAutoConfigUrl =
+ scoped_ie_config.config()->lpszAutoConfigUrl;
+ }
+
+ // Similarly, allow WPAD if it was enabled in the IE configs.
+ if (!!scoped_ie_config.config()->fAutoDetect) {
+ autoproxy_options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT;
+
+ // Enable WPAD using both DNS and DHCP, since that is what idiomatic Windows
+ // applications do.
+ autoproxy_options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+ autoproxy_options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP;
+ }
+
+ // Now that everything is set-up, ask WinHTTP to get the actual proxy list.
+ const DWORD_PTR context = reinterpret_cast<DWORD_PTR>(this);
+ if (!winhttp_api_wrapper_->CallWinHttpGetProxyForUrlEx(
+ resolver_handle, url, &autoproxy_options, context)) {
+ winhttp_api_wrapper_->CallWinHttpCloseHandle(resolver_handle);
+ return false;
+ }
+
+ // Saves of the object which will receive the callback once the operation
+ // completes.
+ AddPendingCallbackTarget(callback_target, resolver_handle);
+
+ // On a successful call to WinHttpGetProxyForUrlEx(), the callback set by
+ // CallWinHttpSetStatusCallback() is guaranteed to be called exactly once.
+ // That may happen at any time on any thread. In order to make sure this
+ // object does not destruct before that callback occurs, it must AddRef()
+ // itself. This reference will be Release()'d in the callback.
+ base::RefCountedThreadSafe<WindowsSystemProxyResolver>::AddRef();
+
+ return true;
+}
+
+void WindowsSystemProxyResolver::AddPendingCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target,
+ HINTERNET resolver_handle) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ pending_callback_target_map_[callback_target] = resolver_handle;
+}
+
+void WindowsSystemProxyResolver::RemovePendingCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ pending_callback_target_map_.erase(callback_target);
+}
+
+bool WindowsSystemProxyResolver::HasPendingCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ return (pending_callback_target_map_.find(callback_target) !=
+ pending_callback_target_map_.end());
+}
+
+WindowsSystemProxyResolutionRequest*
+WindowsSystemProxyResolver::LookupCallbackTargetFromResolverHandle(
+ HINTERNET resolver_handle) const {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ WindowsSystemProxyResolutionRequest* pending_callback_target = nullptr;
+ for (auto target : pending_callback_target_map_) {
+ if (target.second == resolver_handle) {
+ pending_callback_target = target.first;
+ break;
+ }
+ }
+ return pending_callback_target;
+}
+
+// static
+void __stdcall WindowsSystemProxyResolver::WinHttpStatusCallback(
+ HINTERNET resolver_handle,
+ DWORD_PTR context,
+ DWORD status,
+ void* info,
+ DWORD info_len) {
+ DCHECK(resolver_handle);
+ DCHECK(context);
+ WindowsSystemProxyResolver* windows_system_proxy_resolver =
+ reinterpret_cast<WindowsSystemProxyResolver*>(context);
+
+ // Make a copy of any error information in |info| so it can be accessed from
+ // the subsequently posted task. The |info| pointer's lifetime is managed by
+ // WinHTTP and hence is not valid once this frame returns.
+ int windows_error = S_OK;
+ if (info && status == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) {
+ WINHTTP_ASYNC_RESULT* result = static_cast<WINHTTP_ASYNC_RESULT*>(info);
+ windows_error = result->dwError;
+ }
+
+ // It is possible for PostTask() to fail (ex: during shutdown). In that case,
+ // the WindowsSystemProxyResolver in |context| will leak. This is expected to
+ // be either unusual or to occur during shutdown, where a leak doesn't matter.
+ // Since calling the |context| on the wrong thread may be problematic, it will
+ // be allowed to leak here if PostTask() fails.
+ windows_system_proxy_resolver->sequenced_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&WindowsSystemProxyResolver::DoWinHttpStatusCallback,
+ windows_system_proxy_resolver, resolver_handle, status,
+ windows_error));
+}
+
+void WindowsSystemProxyResolver::DoWinHttpStatusCallback(
+ HINTERNET resolver_handle,
+ DWORD status,
+ int windows_error) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ // The |resolver_handle| should correspond to a HINTERNET resolver_handle in
+ // |pending_callback_target_map_| unless the associated attempt to get a proxy
+ // for an URL has been cancelled.
+ WindowsSystemProxyResolutionRequest* pending_callback_target =
+ LookupCallbackTargetFromResolverHandle(resolver_handle);
+
+ // There is no work to do if this request has been cancelled.
+ if (pending_callback_target) {
+ switch (status) {
+ case WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE:
+ GetProxyResultForCallbackTarget(pending_callback_target,
+ resolver_handle);
+ break;
+ case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
+ HandleErrorForCallbackTarget(pending_callback_target, windows_error);
+ break;
+ default:
+ LOG(WARNING) << "DoWinHttpStatusCallback() expects only callbacks for "
+ "WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE and "
+ "WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, not: "
+ << status;
+ HandleErrorForCallbackTarget(pending_callback_target, E_UNEXPECTED);
+ break;
+ }
+
+ // No matter what happened above, the |pending_callback_target| should no
+ // longer be in the |pending_callback_target_map_|. Either the callback was
+ // handled or it was cancelled. This pointer will be explicitly cleared to
+ // make it obvious that it can no longer be used safely.
+ DCHECK(!HasPendingCallbackTarget(pending_callback_target));
+ pending_callback_target = nullptr;
+ }
+
+ // The HINTERNET |resolver_handle| for this attempt at getting a proxy is no
+ // longer needed.
+ winhttp_api_wrapper_->CallWinHttpCloseHandle(resolver_handle);
+
+ // The current WindowsSystemProxyResolver object may now be Release()'d on the
+ // correct sequence after all work is done, thus balancing out the AddRef()
+ // from WinHttpGetProxyForUrlEx().
+ base::RefCountedThreadSafe<WindowsSystemProxyResolver>::Release();
+}
+
+void WindowsSystemProxyResolver::GetProxyResultForCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target,
+ HINTERNET resolver_handle) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(HasPendingCallbackTarget(callback_target));
+
+ WINHTTP_PROXY_RESULT proxy_result = {0};
+ if (!winhttp_api_wrapper_->CallWinHttpGetProxyResult(resolver_handle,
+ &proxy_result)) {
+ // TODO(https://crbug.com/1032820): Use a more detailed net error.
+ callback_target->AsynchronousProxyResolutionComplete(ProxyList(),
+ ERR_FAILED, 0);
+ return;
+ }
+
+ // Translate the results for ProxyInfo.
+ ProxyList proxy_list;
+ for (DWORD i = 0u; i < proxy_result.cEntries; ++i) {
+ ProxyServer proxy_server;
+ if (GetProxyServerFromWinHttpResultEntry(proxy_result.pEntries[i],
+ &proxy_server))
+ proxy_list.AddProxyServer(proxy_server);
+ }
+
+ // The |proxy_result| must be freed.
+ winhttp_api_wrapper_->CallWinHttpFreeProxyResult(&proxy_result);
+
+ // The consumer of this proxy resolution may not understand an empty proxy
+ // list. Thus, this case is considered an error.
+ int net_error = proxy_list.IsEmpty() ? ERR_FAILED : OK;
+ callback_target->AsynchronousProxyResolutionComplete(proxy_list, net_error,
+ 0);
+}
+
+void WindowsSystemProxyResolver::HandleErrorForCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target,
+ int windows_error) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DCHECK(HasPendingCallbackTarget(callback_target));
+
+ // TODO(https://crbug.com/1032820): Use a more detailed net error.
+ callback_target->AsynchronousProxyResolutionComplete(ProxyList(), ERR_FAILED,
+ windows_error);
+}
+
+} // namespace net
diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.h b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.h
new file mode 100644
index 00000000000..3596ec8f935
--- /dev/null
+++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver.h
@@ -0,0 +1,131 @@
+// 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.
+
+#ifndef NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLVER_H_
+#define NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLVER_H_
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "net/base/net_export.h"
+#include "net/proxy_resolution/proxy_list.h"
+
+namespace net {
+
+class WindowsSystemProxyResolutionRequest;
+class WinHttpAPIWrapper;
+
+// This provides a layer of abstraction between calling code Windows-specific
+// code. It is shared between the WindowsSystemProxyResolutionService and
+// inflight WinHttp callbacks. Internally, it takes care of all interaction with
+// WinHttp. The only time this object is ever access outside of its sequence is
+// during the WinHttp callback. For the sake of that callback, this must be
+// RefcountedThreadSafe.
+class NET_EXPORT WindowsSystemProxyResolver
+ : public base::RefCountedThreadSafe<WindowsSystemProxyResolver> {
+ public:
+ static scoped_refptr<WindowsSystemProxyResolver>
+ CreateWindowsSystemProxyResolver();
+
+ WindowsSystemProxyResolver(const WindowsSystemProxyResolver&) = delete;
+ WindowsSystemProxyResolver& operator=(const WindowsSystemProxyResolver&) =
+ delete;
+
+ // This will first fetch the current system proxy settings by calling into
+ // WinHttpGetIEProxyConfigForCurrentUser() and then resolve the proxy using
+ // those settings as an input into WinHttpGetProxyForUrlEx().
+ virtual bool GetProxyForUrl(
+ WindowsSystemProxyResolutionRequest* callback_target,
+ const std::string& url) WARN_UNUSED_RESULT;
+
+ // After calling GetProxyForUrl(), a |callback_target| is saved internally for
+ // when proxy resolution is complete. When a
+ // WindowsSystemProxyResolutionRequest wants to avoid receiving a callback,
+ // it must remove itself from the list of pending callback targets.
+ virtual void RemovePendingCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target);
+ virtual bool HasPendingCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target) const
+ WARN_UNUSED_RESULT;
+
+ protected:
+ explicit WindowsSystemProxyResolver(
+ std::unique_ptr<WinHttpAPIWrapper> winhttp_api_wrapper);
+ virtual ~WindowsSystemProxyResolver();
+
+ private:
+ friend class base::RefCountedThreadSafe<WindowsSystemProxyResolver>;
+ friend class WindowsSystemProxyResolverTest;
+
+ // Sets up the WinHttp session that will be used throughout the lifetime of
+ // this object.
+ bool Initialize();
+
+ // These will interact with |pending_callback_target_map_|.
+ void AddPendingCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target,
+ HINTERNET handle);
+ WindowsSystemProxyResolutionRequest* LookupCallbackTargetFromResolverHandle(
+ HINTERNET resolver_handle) const;
+
+ // This is the callback provided to WinHttp. Once a call to resolve a proxy
+ // succeeds or errors out, it'll call into here with |context| being a pointer
+ // to a WindowsSystemProxyResolver that has been kept alive. This callback can
+ // hit in any thread and will immediately post a task to the right sequence.
+ static void __stdcall WinHttpStatusCallback(HINTERNET resolver_handle,
+ DWORD_PTR context,
+ DWORD status,
+ void* info,
+ DWORD info_len);
+
+ // Called from WinHttpStatusCallback on the right sequence. This will make
+ // decisions about what to do from the results of the proxy resolution call.
+ // Note that the WindowsSystemProxyResolutionRequest that asked for this proxy
+ // may have decided they no longer need an answer (ex: the request has gone
+ // away), so this function has to deal with that situation too.
+ void DoWinHttpStatusCallback(HINTERNET resolver_handle,
+ DWORD status,
+ int windows_error);
+
+ // On a successful call to WinHttpGetProxyForUrlEx(), this translates WinHttp
+ // results into Chromium-friendly structures before notifying the right
+ // WindowsSystemProxyResolutionRequest.
+ void GetProxyResultForCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target,
+ HINTERNET resolver_handle);
+
+ // On a failed call to WinHttpGetProxyForUrlEx(), this will notify the right
+ // WindowsSystemProxyResolutionRequest of the error.
+ void HandleErrorForCallbackTarget(
+ WindowsSystemProxyResolutionRequest* callback_target,
+ int windows_error);
+
+ // This is a thin wrapper over WinHttp APIs that may be overridden for
+ // testing.
+ std::unique_ptr<WinHttpAPIWrapper> winhttp_api_wrapper_;
+
+ // This is the mapping of WindowsSystemProxyResolutionRequest objects that
+ // called GetProxyForUrl() to the handle that's being used for their proxy
+ // resolution call. Upon receiving a callback from WinHttp (which includes an
+ // HINTERNET handle), a reverse lookup here will get the right
+ // WindowsSystemProxyResolutionRequest to use.
+ std::unordered_map<WindowsSystemProxyResolutionRequest*, HINTERNET>
+ pending_callback_target_map_;
+
+ SEQUENCE_CHECKER(sequence_checker_);
+ scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
+};
+
+} // namespace net
+
+#endif // NET_PROXY_RESOLUTION_WIN_WINDOWS_SYSTEM_PROXY_RESOLVER_H_
diff --git a/chromium/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc
new file mode 100644
index 00000000000..9f48f4b88ab
--- /dev/null
+++ b/chromium/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc
@@ -0,0 +1,864 @@
+// 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_resolver.h"
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "net/base/net_errors.h"
+#include "net/base/proxy_server.h"
+#include "net/base/test_completion_callback.h"
+#include "net/proxy_resolution/proxy_config.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_resolution_service.h"
+#include "net/proxy_resolution/win/winhttp_api_wrapper.h"
+#include "net/test/test_with_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace net {
+
+namespace {
+
+constexpr char kUrl[] = "https://example.test:8080/";
+
+void CopySettingToIEProxyConfigString(const base::string16& setting,
+ LPWSTR* ie_proxy_config_string) {
+ *ie_proxy_config_string = static_cast<LPWSTR>(
+ GlobalAlloc(GPTR, sizeof(base::char16) * (setting.length() + 1)));
+ memcpy(*ie_proxy_config_string, setting.data(),
+ sizeof(wchar_t) * setting.length());
+}
+
+class MockProxyResolutionRequest final
+ : public WindowsSystemProxyResolutionRequest {
+ public:
+ MockProxyResolutionRequest(
+ CompletionOnceCallback user_callback,
+ const NetLogWithSource& net_log,
+ scoped_refptr<WindowsSystemProxyResolver> windows_system_proxy_resolver)
+ : WindowsSystemProxyResolutionRequest(/*service=*/nullptr,
+ GURL(),
+ /*method=*/std::string(),
+ /*results=*/nullptr,
+ std::move(user_callback),
+ net_log,
+ windows_system_proxy_resolver) {}
+ ~MockProxyResolutionRequest() override = default;
+
+ LoadState GetLoadState() const override {
+ return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
+ }
+
+ void AsynchronousProxyResolutionComplete(const ProxyList& proxy_list,
+ int net_error,
+ int windows_error) override {
+ run_loop_.Quit();
+ EXPECT_TRUE(windows_system_proxy_resolver_->HasPendingCallbackTarget(this));
+ windows_system_proxy_resolver_->RemovePendingCallbackTarget(this);
+ EXPECT_FALSE(
+ windows_system_proxy_resolver_->HasPendingCallbackTarget(this));
+
+ proxy_list_ = proxy_list;
+ net_error_ = net_error;
+ windows_error_ = windows_error;
+ }
+
+ void WaitForProxyResolutionComplete() { run_loop_.Run(); }
+
+ const ProxyList& proxy_list() const { return proxy_list_; }
+
+ int net_error() const { return net_error_; }
+
+ int windows_error() const { return windows_error_; }
+
+ private:
+ base::RunLoop run_loop_;
+ ProxyList proxy_list_;
+ int net_error_ = 0;
+ int windows_error_ = 0;
+};
+
+// This limit is arbitrary and exists only to make memory management in this
+// test easier.
+constexpr unsigned int kMaxProxyEntryLimit = 10u;
+
+// This class will internally validate behavior that MUST be present in the code
+// in order to successfully use WinHttp APIs.
+class MockWinHttpAPIWrapper : public WinHttpAPIWrapper {
+ public:
+ MockWinHttpAPIWrapper() {}
+ ~MockWinHttpAPIWrapper() override {
+ if (did_call_get_proxy_result_)
+ EXPECT_TRUE(did_call_free_proxy_result_);
+ EXPECT_TRUE(opened_proxy_resolvers_.empty());
+ ResetWinHttpResults();
+ }
+
+ void set_call_winhttp_open_success(bool open_success) {
+ open_success_ = open_success;
+ }
+ bool CallWinHttpOpen() override {
+ did_call_open_ = true;
+ return open_success_;
+ }
+
+ void set_call_winhttp_set_timeouts_success(bool set_timeouts_success) {
+ set_timeouts_success_ = set_timeouts_success;
+ }
+ bool CallWinHttpSetTimeouts(int resolve_timeout,
+ int connect_timeout,
+ int send_timeout,
+ int receive_timeout) override {
+ EXPECT_TRUE(did_call_open_);
+ did_call_set_timeouts_ = true;
+ return set_timeouts_success_;
+ }
+
+ void set_call_winhttp_set_status_callback_success(
+ bool set_status_callback_success) {
+ set_status_callback_success_ = set_status_callback_success;
+ }
+ bool CallWinHttpSetStatusCallback(
+ WINHTTP_STATUS_CALLBACK internet_callback) override {
+ EXPECT_TRUE(did_call_open_);
+ EXPECT_NE(internet_callback, nullptr);
+ EXPECT_EQ(callback_, nullptr);
+ callback_ = internet_callback;
+ did_call_set_status_callback_ = true;
+ return set_status_callback_success_;
+ }
+
+ void set_call_winhttp_get_ie_proxy_config_success(
+ bool get_ie_proxy_config_success) {
+ get_ie_proxy_config_success_ = get_ie_proxy_config_success;
+ }
+ void set_ie_proxy_config(bool is_autoproxy_enabled,
+ const base::string16& pac_url,
+ const base::string16& proxy,
+ const base::string16& proxy_bypass) {
+ is_autoproxy_enabled_ = is_autoproxy_enabled;
+ pac_url_ = pac_url;
+ proxy_ = proxy;
+ proxy_bypass_ = proxy_bypass;
+ }
+ bool CallWinHttpGetIEProxyConfigForCurrentUser(
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_proxy_config) override {
+ did_call_get_ie_proxy_config_ = true;
+ ie_proxy_config->fAutoDetect = is_autoproxy_enabled_ ? TRUE : FALSE;
+ if (!pac_url_.empty()) {
+ CopySettingToIEProxyConfigString(pac_url_,
+ &ie_proxy_config->lpszAutoConfigUrl);
+ }
+ if (!proxy_.empty()) {
+ CopySettingToIEProxyConfigString(proxy_, &ie_proxy_config->lpszProxy);
+ }
+ if (!proxy_bypass_.empty()) {
+ CopySettingToIEProxyConfigString(proxy_bypass_,
+ &ie_proxy_config->lpszProxyBypass);
+ }
+ return get_ie_proxy_config_success_;
+ }
+
+ void set_call_winhttp_create_proxy_resolver_success(
+ bool create_proxy_resolver_success) {
+ create_proxy_resolver_success_ = create_proxy_resolver_success;
+ }
+ bool CallWinHttpCreateProxyResolver(HINTERNET* out_resolver_handle) override {
+ EXPECT_TRUE(did_call_set_status_callback_);
+ EXPECT_NE(out_resolver_handle, nullptr);
+ if (!out_resolver_handle)
+ return false;
+
+ did_call_create_proxy_resolver_ = true;
+ if (!create_proxy_resolver_success_)
+ return false;
+
+ // The caller will be using this handle as an identifier later, so make this
+ // unique.
+ *out_resolver_handle =
+ reinterpret_cast<HINTERNET>(proxy_resolver_identifier_++);
+ EXPECT_EQ(opened_proxy_resolvers_.count(*out_resolver_handle), 0u);
+ opened_proxy_resolvers_.emplace(*out_resolver_handle);
+
+ return true;
+ }
+
+ void set_call_winhttp_get_proxy_for_url_success(
+ bool get_proxy_for_url_success) {
+ get_proxy_for_url_success_ = get_proxy_for_url_success;
+ }
+ bool CallWinHttpGetProxyForUrlEx(HINTERNET resolver_handle,
+ const std::string& url,
+ WINHTTP_AUTOPROXY_OPTIONS* autoproxy_options,
+ DWORD_PTR context) override {
+ // This API must be called only after the session has been correctly set up.
+ EXPECT_TRUE(did_call_open_);
+ EXPECT_TRUE(did_call_set_timeouts_);
+ EXPECT_TRUE(did_call_set_status_callback_);
+ EXPECT_NE(callback_, nullptr);
+ EXPECT_TRUE(did_call_get_ie_proxy_config_);
+ EXPECT_TRUE(did_call_create_proxy_resolver_);
+ EXPECT_TRUE(!did_call_get_proxy_result_);
+ EXPECT_TRUE(!did_call_free_proxy_result_);
+
+ // This API must always receive valid inputs.
+ EXPECT_TRUE(!url.empty());
+ EXPECT_TRUE(autoproxy_options);
+ EXPECT_FALSE(autoproxy_options->fAutoLogonIfChallenged);
+ EXPECT_TRUE(autoproxy_options->dwFlags & WINHTTP_AUTOPROXY_ALLOW_STATIC);
+ EXPECT_TRUE(autoproxy_options->dwFlags & WINHTTP_AUTOPROXY_ALLOW_CM);
+ if (autoproxy_options->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL) {
+ EXPECT_TRUE(autoproxy_options->lpszAutoConfigUrl);
+ } else {
+ EXPECT_TRUE(!autoproxy_options->lpszAutoConfigUrl);
+ }
+ if (autoproxy_options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) {
+ EXPECT_TRUE(autoproxy_options->dwAutoDetectFlags &
+ WINHTTP_AUTO_DETECT_TYPE_DNS_A);
+ EXPECT_TRUE(autoproxy_options->dwAutoDetectFlags &
+ WINHTTP_AUTO_DETECT_TYPE_DHCP);
+ } else {
+ EXPECT_TRUE(!autoproxy_options->dwAutoDetectFlags);
+ }
+
+ EXPECT_NE(resolver_handle, nullptr);
+ EXPECT_EQ(opened_proxy_resolvers_.count(resolver_handle), 1u);
+ EXPECT_NE(context, 0u);
+ if (!resolver_handle || !context || !callback_)
+ return false;
+
+ did_call_get_proxy_for_url_ = true;
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MockWinHttpAPIWrapper::RunCallback,
+ base::Unretained(this), resolver_handle, context));
+ return get_proxy_for_url_success_;
+ }
+
+ void set_call_winhttp_get_proxy_result_success(
+ bool get_proxy_result_success) {
+ get_proxy_result_success_ = get_proxy_result_success;
+ }
+ void SetCallbackStatusAndInfo(DWORD callback_status, DWORD info_error) {
+ callback_status_ = callback_status;
+ callback_info_ = std::make_unique<WINHTTP_ASYNC_RESULT>();
+ callback_info_->dwError = info_error;
+ }
+ void AddToProxyResults(const ProxyServer& proxy_server,
+ bool bypass = false,
+ bool skip_port = false) {
+ EXPECT_LT(proxy_result_.cEntries, kMaxProxyEntryLimit - 1);
+
+ // Assign memory as needed.
+ if (proxy_result_.cEntries == 0) {
+ proxy_result_.pEntries =
+ new WINHTTP_PROXY_RESULT_ENTRY[kMaxProxyEntryLimit];
+ std::memset(proxy_result_.pEntries, 0,
+ kMaxProxyEntryLimit * sizeof(WINHTTP_PROXY_RESULT_ENTRY));
+
+ // The memory of the strings above will be backed by a vector of strings.
+ proxy_list_.reserve(kMaxProxyEntryLimit);
+ }
+
+ if (bypass) {
+ proxy_result_.pEntries[proxy_result_.cEntries].fBypass = TRUE;
+ } else if (!proxy_server.is_direct()) {
+ // Now translate the ProxyServer into a WINHTTP_PROXY_RESULT_ENTRY and
+ // assign.
+ proxy_result_.pEntries[proxy_result_.cEntries].fProxy = TRUE;
+
+ switch (proxy_server.scheme()) {
+ case ProxyServer::Scheme::SCHEME_HTTP:
+ proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme =
+ INTERNET_SCHEME_HTTP;
+ break;
+ case ProxyServer::Scheme::SCHEME_HTTPS:
+ proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme =
+ INTERNET_SCHEME_HTTPS;
+ break;
+ case ProxyServer::Scheme::SCHEME_SOCKS4:
+ proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme =
+ INTERNET_SCHEME_SOCKS;
+ break;
+ default:
+ ADD_FAILURE()
+ << "Of the possible proxy schemes returned by WinHttp, Chrome "
+ "supports HTTP(S) and SOCKS4. The ProxyServer::Scheme that "
+ "triggered this message is: "
+ << proxy_server.scheme();
+ break;
+ }
+
+ base::string16 proxy_host(proxy_server.host_port_pair().host().begin(),
+ proxy_server.host_port_pair().host().end());
+ proxy_list_.push_back(proxy_host);
+
+ wchar_t* proxy_host_raw = const_cast<wchar_t*>(proxy_list_.back().data());
+ proxy_result_.pEntries[proxy_result_.cEntries].pwszProxy = proxy_host_raw;
+
+ if (skip_port)
+ proxy_result_.pEntries[proxy_result_.cEntries].ProxyPort =
+ INTERNET_DEFAULT_PORT;
+ else
+ proxy_result_.pEntries[proxy_result_.cEntries].ProxyPort =
+ proxy_server.host_port_pair().port();
+ }
+
+ proxy_result_.cEntries++;
+ }
+ bool CallWinHttpGetProxyResult(HINTERNET resolver_handle,
+ WINHTTP_PROXY_RESULT* proxy_result) override {
+ EXPECT_TRUE(did_call_get_proxy_for_url_);
+ EXPECT_NE(resolver_handle, nullptr);
+ EXPECT_EQ(opened_proxy_resolvers_.count(resolver_handle), 1u);
+ if (!get_proxy_result_success_)
+ return false;
+
+ EXPECT_NE(proxy_result, nullptr);
+ proxy_result->cEntries = proxy_result_.cEntries;
+ proxy_result->pEntries = proxy_result_.pEntries;
+
+ did_call_get_proxy_result_ = true;
+ return get_proxy_result_success_;
+ }
+
+ void CallWinHttpFreeProxyResult(WINHTTP_PROXY_RESULT* proxy_result) override {
+ EXPECT_TRUE(did_call_get_proxy_result_);
+ EXPECT_NE(proxy_result, nullptr);
+ did_call_free_proxy_result_ = true;
+ }
+
+ void CallWinHttpCloseHandle(HINTERNET internet_handle) override {
+ EXPECT_EQ(opened_proxy_resolvers_.count(internet_handle), 1u);
+ opened_proxy_resolvers_.erase(internet_handle);
+ }
+
+ void ResetWinHttpResults() {
+ if (proxy_result_.pEntries) {
+ delete[] proxy_result_.pEntries;
+ proxy_result_.pEntries = nullptr;
+ proxy_result_ = {0};
+ }
+ proxy_list_.clear();
+ }
+
+ private:
+ void RunCallback(HINTERNET resolver_handle, DWORD_PTR context) {
+ EXPECT_NE(callback_, nullptr);
+ EXPECT_NE(resolver_handle, nullptr);
+ EXPECT_EQ(opened_proxy_resolvers_.count(resolver_handle), 1u);
+ EXPECT_NE(context, 0u);
+ callback_(resolver_handle, context, callback_status_, callback_info_.get(),
+ sizeof(callback_info_.get()));
+
+ // As soon as the callback resolves, WinHttp may choose to delete the memory
+ // contained by |callback_info_|. This is simulated here.
+ callback_info_.reset();
+ }
+
+ // Data configurable by tests to simulate errors and results from WinHttp.
+ bool open_success_ = true;
+ bool set_timeouts_success_ = true;
+ bool set_status_callback_success_ = true;
+ bool get_ie_proxy_config_success_ = true;
+ bool create_proxy_resolver_success_ = true;
+ bool get_proxy_for_url_success_ = true;
+ bool get_proxy_result_success_ = true;
+ DWORD callback_status_ = WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE;
+ std::unique_ptr<WINHTTP_ASYNC_RESULT> callback_info_;
+ bool is_autoproxy_enabled_ = false;
+ base::string16 pac_url_;
+ base::string16 proxy_;
+ base::string16 proxy_bypass_;
+ WINHTTP_PROXY_RESULT proxy_result_ = {0};
+ std::vector<base::string16> proxy_list_;
+
+ // Data used internally in the mock to function and validate its own behavior.
+ bool did_call_open_ = false;
+ bool did_call_set_timeouts_ = false;
+ bool did_call_set_status_callback_ = false;
+ bool did_call_get_ie_proxy_config_ = false;
+ int proxy_resolver_identifier_ = 1;
+ std::set<HINTERNET> opened_proxy_resolvers_;
+ bool did_call_create_proxy_resolver_ = false;
+ bool did_call_get_proxy_for_url_ = false;
+ bool did_call_get_proxy_result_ = false;
+ bool did_call_free_proxy_result_ = false;
+ WINHTTP_STATUS_CALLBACK callback_ = nullptr;
+};
+
+} // namespace
+
+// These tests verify the behavior of the WindowsSystemProxyResolver in
+// isolation by mocking out the WinHttpAPIWrapper it uses and the
+// WindowsSystemProxyResolutionRequest it normally reports back to.
+class WindowsSystemProxyResolverTest : public TestWithTaskEnvironment {
+ public:
+ void SetUp() override {
+ testing::Test::SetUp();
+
+ if (!WindowsSystemProxyResolutionService::IsSupported()) {
+ GTEST_SKIP()
+ << "Windows System Proxy Resolution is only supported on Windows 8+.";
+ }
+
+ winhttp_api_wrapper_ = new MockWinHttpAPIWrapper();
+ // In general, the WindowsSystemProxyResolver should be created via
+ // CreateWindowsSystemProxyResolver(), so the constructor is protected. Thus
+ // base::MakeRefCounted cannot be used here.
+ proxy_resolver_ = base::WrapRefCounted(
+ new WindowsSystemProxyResolver(base::WrapUnique(winhttp_api_wrapper_)));
+ }
+
+ void TearDown() override {
+ EXPECT_TRUE(!proxy_resolver_ || proxy_resolver_->HasOneRef())
+ << "This test has a memory leak!";
+ ResetProxyResolutionService();
+
+ testing::Test::TearDown();
+ }
+
+ MockWinHttpAPIWrapper* winhttp_api_wrapper() { return winhttp_api_wrapper_; }
+
+ scoped_refptr<WindowsSystemProxyResolver> proxy_resolver() {
+ return proxy_resolver_;
+ }
+
+ bool InitializeResolver() { return proxy_resolver_->Initialize(); }
+
+ void AddNoPortProxyToResults() {
+ const ProxyServer proxy_result =
+ ProxyServer::FromPacString("PROXY foopy:8080");
+ winhttp_api_wrapper_->AddToProxyResults(proxy_result, /*bypass=*/false,
+ /*skip_port=*/true);
+ }
+
+ void AddDirectProxyToResults(ProxyList* out_proxy_list) {
+ winhttp_api_wrapper_->AddToProxyResults(ProxyServer::Direct());
+ out_proxy_list->AddProxyServer(ProxyServer::Direct());
+ }
+
+ void AddBypassedProxyToResults(ProxyList* out_proxy_list) {
+ winhttp_api_wrapper_->AddToProxyResults(ProxyServer::Direct(),
+ /*bypass=*/true);
+ out_proxy_list->AddProxyServer(ProxyServer::Direct());
+ }
+
+ void AddHTTPProxyToResults(ProxyList* out_proxy_list) {
+ const ProxyServer proxy_result =
+ ProxyServer::FromPacString("PROXY foopy:8080");
+ winhttp_api_wrapper_->AddToProxyResults(proxy_result);
+ out_proxy_list->AddProxyServer(proxy_result);
+ }
+
+ void AddHTTPSProxyToResults(ProxyList* out_proxy_list) {
+ const ProxyServer proxy_result =
+ ProxyServer::FromPacString("HTTPS foopy:8443");
+ winhttp_api_wrapper_->AddToProxyResults(proxy_result);
+ out_proxy_list->AddProxyServer(proxy_result);
+ }
+
+ void AddSOCKSProxyToResults(ProxyList* out_proxy_list) {
+ const ProxyServer proxy_result =
+ ProxyServer::FromPacString("SOCKS4 foopy:8080");
+ winhttp_api_wrapper_->AddToProxyResults(proxy_result);
+ out_proxy_list->AddProxyServer(proxy_result);
+ }
+
+ void AddIDNProxyToResults(ProxyList* out_proxy_list) {
+ const ProxyServer proxy_result =
+ ProxyServer::FromPacString("HTTPS föopy:8080");
+ winhttp_api_wrapper_->AddToProxyResults(proxy_result);
+
+ const ProxyServer expected_proxy_result =
+ ProxyServer::FromPacString("HTTPS xn--fopy-5jr83a:8080");
+ out_proxy_list->AddProxyServer(expected_proxy_result);
+ }
+
+ void PerformGetProxyForUrlAndValidateResult(const ProxyList& proxy_list,
+ int net_error,
+ int windows_error) {
+ ASSERT_TRUE(InitializeResolver());
+ TestCompletionCallback unused_callback;
+ NetLogWithSource unused_log;
+ MockProxyResolutionRequest proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ ASSERT_TRUE(
+ proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl));
+ ASSERT_TRUE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+
+ proxy_resolution_request.WaitForProxyResolutionComplete();
+
+ EXPECT_TRUE(proxy_list.Equals(proxy_resolution_request.proxy_list()));
+ EXPECT_EQ(proxy_resolution_request.net_error(), net_error);
+ EXPECT_EQ(proxy_resolution_request.windows_error(), windows_error);
+ }
+
+ void DoFailedGetProxyForUrlTest(int net_error, int windows_error) {
+ PerformGetProxyForUrlAndValidateResult(ProxyList(), net_error,
+ windows_error);
+ }
+
+ void DoProxyConfigTest(const ProxyConfig& proxy_config) {
+ ProxyList proxy_list;
+ AddHTTPSProxyToResults(&proxy_list);
+
+ base::string16 pac_url;
+ if (proxy_config.has_pac_url())
+ pac_url = base::UTF8ToUTF16(proxy_config.pac_url().spec());
+
+ base::string16 proxy;
+ if (!proxy_config.proxy_rules().single_proxies.IsEmpty())
+ proxy = base::UTF8ToUTF16(
+ proxy_config.proxy_rules().single_proxies.ToPacString());
+
+ base::string16 proxy_bypass;
+ if (!proxy_config.proxy_rules().bypass_rules.ToString().empty())
+ proxy_bypass =
+ base::UTF8ToUTF16(proxy_config.proxy_rules().bypass_rules.ToString());
+
+ winhttp_api_wrapper_->set_ie_proxy_config(proxy_config.auto_detect(),
+ pac_url, proxy, proxy_bypass);
+
+ PerformGetProxyForUrlAndValidateResult(proxy_list, OK, 0);
+ }
+
+ void DoGetProxyForUrlTest(const ProxyList& proxy_list) {
+ PerformGetProxyForUrlAndValidateResult(proxy_list, OK, 0);
+ }
+
+ void ResetProxyResolutionService() {
+ winhttp_api_wrapper_ = nullptr;
+ proxy_resolver_.reset();
+ }
+
+ private:
+ MockWinHttpAPIWrapper* winhttp_api_wrapper_ = nullptr;
+ scoped_refptr<WindowsSystemProxyResolver> proxy_resolver_;
+};
+
+TEST_F(WindowsSystemProxyResolverTest, InitializeFailOnOpen) {
+ winhttp_api_wrapper()->set_call_winhttp_open_success(false);
+ EXPECT_FALSE(InitializeResolver());
+}
+
+TEST_F(WindowsSystemProxyResolverTest, InitializeFailOnSetTimeouts) {
+ winhttp_api_wrapper()->set_call_winhttp_set_timeouts_success(false);
+ EXPECT_FALSE(InitializeResolver());
+}
+
+TEST_F(WindowsSystemProxyResolverTest, InitializeFailOnSetStatusCallback) {
+ winhttp_api_wrapper()->set_call_winhttp_set_status_callback_success(false);
+ EXPECT_FALSE(InitializeResolver());
+}
+
+TEST_F(WindowsSystemProxyResolverTest, InitializeSucceedsIfWinHttpAPIsWork) {
+ EXPECT_TRUE(InitializeResolver());
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnGetIEProxySettings) {
+ winhttp_api_wrapper()->set_call_winhttp_get_ie_proxy_config_success(false);
+ ASSERT_TRUE(InitializeResolver());
+ TestCompletionCallback unused_callback;
+ NetLogWithSource unused_log;
+ MockProxyResolutionRequest proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ EXPECT_FALSE(
+ proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl));
+ EXPECT_FALSE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+}
+
+TEST_F(WindowsSystemProxyResolverTest,
+ GetProxyForUrlFailOnCreateProxyResolver) {
+ winhttp_api_wrapper()->set_call_winhttp_create_proxy_resolver_success(false);
+ ASSERT_TRUE(InitializeResolver());
+ TestCompletionCallback unused_callback;
+ NetLogWithSource unused_log;
+ MockProxyResolutionRequest proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ EXPECT_FALSE(
+ proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl));
+ EXPECT_FALSE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+}
+
+TEST_F(WindowsSystemProxyResolverTest,
+ GetProxyForUrlFailOnWinHttpGetProxyForUrlEx) {
+ winhttp_api_wrapper()->set_call_winhttp_get_proxy_for_url_success(false);
+ ASSERT_TRUE(InitializeResolver());
+ TestCompletionCallback unused_callback;
+ NetLogWithSource unused_log;
+ MockProxyResolutionRequest proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ EXPECT_FALSE(
+ proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl));
+ EXPECT_FALSE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnFailedCallback) {
+ winhttp_api_wrapper()->SetCallbackStatusAndInfo(
+ WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, API_RECEIVE_RESPONSE);
+ DoFailedGetProxyForUrlTest(ERR_FAILED, API_RECEIVE_RESPONSE);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnGetProxyResult) {
+ winhttp_api_wrapper()->set_call_winhttp_get_proxy_result_success(false);
+ DoFailedGetProxyForUrlTest(ERR_FAILED, 0);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnDefaultPort) {
+ AddNoPortProxyToResults();
+ DoFailedGetProxyForUrlTest(ERR_FAILED, 0);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnNoResults) {
+ DoFailedGetProxyForUrlTest(ERR_FAILED, 0);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlCancellation) {
+ ASSERT_TRUE(InitializeResolver());
+
+ // This extra scope is needed so that the MockProxyResolutionRequest destructs
+ // before the end of the test. This should help catch any use-after-free issue
+ // in the code.
+ {
+ TestCompletionCallback unused_callback;
+ NetLogWithSource unused_log;
+ MockProxyResolutionRequest proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ ASSERT_TRUE(
+ proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl));
+ ASSERT_TRUE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+
+ proxy_resolver()->RemovePendingCallbackTarget(&proxy_resolution_request);
+ EXPECT_FALSE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+ }
+
+ // There must never be a callback and the resolver must not be leaked.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlCancelAndRestart) {
+ ProxyList expected_proxy_list;
+ AddHTTPSProxyToResults(&expected_proxy_list);
+
+ ASSERT_TRUE(InitializeResolver());
+ TestCompletionCallback unused_callback;
+ NetLogWithSource unused_log;
+ MockProxyResolutionRequest proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ ASSERT_TRUE(
+ proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl));
+ ASSERT_TRUE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+
+ // Abandon the proxy resolution for this request.
+ proxy_resolver()->RemovePendingCallbackTarget(&proxy_resolution_request);
+ EXPECT_FALSE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+
+ // Start a new proxy resolution for the request.
+ ASSERT_TRUE(
+ proxy_resolver()->GetProxyForUrl(&proxy_resolution_request, kUrl));
+ ASSERT_TRUE(
+ proxy_resolver()->HasPendingCallbackTarget(&proxy_resolution_request));
+
+ // The received callback must be for the second GetProxyForUrl().
+ proxy_resolution_request.WaitForProxyResolutionComplete();
+ EXPECT_TRUE(
+ expected_proxy_list.Equals(proxy_resolution_request.proxy_list()));
+ EXPECT_EQ(proxy_resolution_request.net_error(), OK);
+ EXPECT_EQ(proxy_resolution_request.windows_error(), 0);
+
+ // There must never be a callback for the first request and the resolver must
+ // not be leaked.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigDirect) {
+ DoProxyConfigTest(ProxyConfig::CreateDirect());
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigAutoDetect) {
+ DoProxyConfigTest(ProxyConfig::CreateAutoDetect());
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigPacUrl) {
+ const GURL pac_url("http://pac-site.test/path/to/pac-url.pac");
+ DoProxyConfigTest(ProxyConfig::CreateFromCustomPacURL(pac_url));
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigSingleProxy) {
+ ProxyConfig config;
+ const ProxyServer proxy_server =
+ ProxyServer::FromPacString("HTTPS ignored:33");
+ config.proxy_rules().single_proxies.AddProxyServer(proxy_server);
+ DoProxyConfigTest(config);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigBypass) {
+ ProxyConfig config;
+ config.proxy_rules().bypass_rules.AddRuleFromString("example.test");
+ DoProxyConfigTest(config);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlConfigMultipleSettings) {
+ ProxyConfig config;
+ config.set_auto_detect(true);
+
+ const GURL pac_url("http://pac-site.test/path/to/pac-url.pac");
+ config.set_pac_url(pac_url);
+
+ const ProxyServer proxy_server =
+ ProxyServer::FromPacString("HTTPS ignored:33");
+ config.proxy_rules().single_proxies.AddProxyServer(proxy_server);
+
+ DoProxyConfigTest(config);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlDirect) {
+ ProxyList expected_proxy_list;
+ AddDirectProxyToResults(&expected_proxy_list);
+ DoGetProxyForUrlTest(expected_proxy_list);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlBypass) {
+ ProxyList expected_proxy_list;
+ AddBypassedProxyToResults(&expected_proxy_list);
+ DoGetProxyForUrlTest(expected_proxy_list);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlHTTP) {
+ ProxyList expected_proxy_list;
+ AddHTTPProxyToResults(&expected_proxy_list);
+ DoGetProxyForUrlTest(expected_proxy_list);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlHTTPS) {
+ ProxyList expected_proxy_list;
+ AddHTTPSProxyToResults(&expected_proxy_list);
+ DoGetProxyForUrlTest(expected_proxy_list);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlSOCKS) {
+ ProxyList expected_proxy_list;
+ AddSOCKSProxyToResults(&expected_proxy_list);
+ DoGetProxyForUrlTest(expected_proxy_list);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlIDNProxy) {
+ ProxyList expected_proxy_list;
+ AddIDNProxyToResults(&expected_proxy_list);
+ DoGetProxyForUrlTest(expected_proxy_list);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlMultipleResults) {
+ ProxyList expected_proxy_list;
+ AddHTTPSProxyToResults(&expected_proxy_list);
+ AddDirectProxyToResults(&expected_proxy_list);
+ DoGetProxyForUrlTest(expected_proxy_list);
+}
+
+TEST_F(WindowsSystemProxyResolverTest, MultipleCallsToGetProxyForUrl) {
+ ProxyList expected_proxy_list;
+ AddHTTPSProxyToResults(&expected_proxy_list);
+ AddDirectProxyToResults(&expected_proxy_list);
+ ASSERT_TRUE(InitializeResolver());
+
+ TestCompletionCallback unused_callback;
+ NetLogWithSource unused_log;
+ MockProxyResolutionRequest first_proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ ASSERT_TRUE(
+ proxy_resolver()->GetProxyForUrl(&first_proxy_resolution_request, kUrl));
+ ASSERT_TRUE(proxy_resolver()->HasPendingCallbackTarget(
+ &first_proxy_resolution_request));
+
+ MockProxyResolutionRequest second_proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ ASSERT_TRUE(
+ proxy_resolver()->GetProxyForUrl(&second_proxy_resolution_request, kUrl));
+ ASSERT_TRUE(proxy_resolver()->HasPendingCallbackTarget(
+ &second_proxy_resolution_request));
+
+ first_proxy_resolution_request.WaitForProxyResolutionComplete();
+ second_proxy_resolution_request.WaitForProxyResolutionComplete();
+
+ EXPECT_TRUE(
+ expected_proxy_list.Equals(first_proxy_resolution_request.proxy_list()));
+ EXPECT_EQ(first_proxy_resolution_request.net_error(), OK);
+ EXPECT_EQ(first_proxy_resolution_request.windows_error(), 0);
+
+ EXPECT_TRUE(
+ expected_proxy_list.Equals(second_proxy_resolution_request.proxy_list()));
+ EXPECT_EQ(second_proxy_resolution_request.net_error(), OK);
+ EXPECT_EQ(second_proxy_resolution_request.windows_error(), 0);
+}
+
+TEST_F(WindowsSystemProxyResolverTest,
+ MultipleCallsToGetProxyForUrlWithOneCancellation) {
+ ProxyList expected_proxy_list;
+ AddHTTPSProxyToResults(&expected_proxy_list);
+ AddDirectProxyToResults(&expected_proxy_list);
+ ASSERT_TRUE(InitializeResolver());
+
+ // This extra scope is needed so that the MockProxyResolutionRequests destruct
+ // before the end of the test. This should help catch any use-after-free issue
+ // in the code.
+ {
+ TestCompletionCallback unused_callback;
+ NetLogWithSource unused_log;
+ MockProxyResolutionRequest first_proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ ASSERT_TRUE(proxy_resolver()->GetProxyForUrl(
+ &first_proxy_resolution_request, kUrl));
+ ASSERT_TRUE(proxy_resolver()->HasPendingCallbackTarget(
+ &first_proxy_resolution_request));
+
+ MockProxyResolutionRequest second_proxy_resolution_request(
+ unused_callback.callback(), unused_log, proxy_resolver());
+ ASSERT_TRUE(proxy_resolver()->GetProxyForUrl(
+ &second_proxy_resolution_request, kUrl));
+ ASSERT_TRUE(proxy_resolver()->HasPendingCallbackTarget(
+ &second_proxy_resolution_request));
+
+ proxy_resolver()->RemovePendingCallbackTarget(
+ &first_proxy_resolution_request);
+ EXPECT_FALSE(proxy_resolver()->HasPendingCallbackTarget(
+ &first_proxy_resolution_request));
+ second_proxy_resolution_request.WaitForProxyResolutionComplete();
+
+ EXPECT_TRUE(expected_proxy_list.Equals(
+ second_proxy_resolution_request.proxy_list()));
+ EXPECT_EQ(second_proxy_resolution_request.net_error(), OK);
+ EXPECT_EQ(second_proxy_resolution_request.windows_error(), 0);
+ }
+
+ // There must never be a callback for the first request and the resolver must
+ // not be leaked.
+ base::RunLoop().RunUntilIdle();
+}
+
+} // namespace net
diff --git a/chromium/net/proxy_resolution/win/winhttp_api_wrapper.cc b/chromium/net/proxy_resolution/win/winhttp_api_wrapper.cc
new file mode 100644
index 00000000000..fe4a0328683
--- /dev/null
+++ b/chromium/net/proxy_resolution/win/winhttp_api_wrapper.cc
@@ -0,0 +1,118 @@
+// 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/winhttp_api_wrapper.h"
+
+#include <utility>
+
+#include "base/check_op.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "net/proxy_resolution/win/winhttp_proxy_resolver_functions.h"
+
+namespace net {
+
+// TODO(https://crbug.com/1032820): Capture telemetry for WinHttp APIs if
+// interesting.
+
+ScopedIEConfig::ScopedIEConfig() = default;
+ScopedIEConfig::~ScopedIEConfig() {
+ if (ie_config.lpszAutoConfigUrl)
+ GlobalFree(ie_config.lpszAutoConfigUrl);
+ if (ie_config.lpszProxy)
+ GlobalFree(ie_config.lpszProxy);
+ if (ie_config.lpszProxyBypass)
+ GlobalFree(ie_config.lpszProxyBypass);
+}
+
+WinHttpAPIWrapper::WinHttpAPIWrapper() = default;
+WinHttpAPIWrapper::~WinHttpAPIWrapper() {
+ if (session_handle_)
+ ignore_result(CallWinHttpSetStatusCallback(nullptr));
+ CloseSessionHandle();
+}
+
+bool WinHttpAPIWrapper::CallWinHttpOpen() {
+ DCHECK_EQ(nullptr, session_handle_);
+ session_handle_ =
+ WinHttpOpen(nullptr, WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
+ return (session_handle_ != nullptr);
+}
+
+bool WinHttpAPIWrapper::CallWinHttpSetTimeouts(int resolve_timeout,
+ int connect_timeout,
+ int send_timeout,
+ int receive_timeout) {
+ DCHECK_NE(nullptr, session_handle_);
+ return (!!WinHttpSetTimeouts(session_handle_, resolve_timeout,
+ connect_timeout, send_timeout, receive_timeout));
+}
+
+bool WinHttpAPIWrapper::CallWinHttpSetStatusCallback(
+ WINHTTP_STATUS_CALLBACK internet_callback) {
+ DCHECK_NE(nullptr, session_handle_);
+ const WINHTTP_STATUS_CALLBACK winhttp_status_callback =
+ WinHttpSetStatusCallback(
+ session_handle_, internet_callback,
+ WINHTTP_CALLBACK_FLAG_REQUEST_ERROR |
+ WINHTTP_CALLBACK_FLAG_GETPROXYFORURL_COMPLETE,
+ 0);
+ return (winhttp_status_callback != WINHTTP_INVALID_STATUS_CALLBACK);
+}
+
+bool WinHttpAPIWrapper::CallWinHttpGetIEProxyConfigForCurrentUser(
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_proxy_config) {
+ return !!WinHttpGetIEProxyConfigForCurrentUser(ie_proxy_config);
+}
+
+bool WinHttpAPIWrapper::CallWinHttpCreateProxyResolver(
+ HINTERNET* out_resolver_handle) {
+ DCHECK_NE(nullptr, session_handle_);
+ const DWORD result =
+ WinHttpProxyResolverFunctions::GetInstance().create_proxy_resolver(
+ session_handle_, out_resolver_handle);
+ return (result == ERROR_SUCCESS);
+}
+
+bool WinHttpAPIWrapper::CallWinHttpGetProxyForUrlEx(
+ HINTERNET resolver_handle,
+ const std::string& url,
+ WINHTTP_AUTOPROXY_OPTIONS* autoproxy_options,
+ DWORD_PTR context) {
+ const base::string16 wide_url(url.begin(), url.end());
+ // TODO(https://crbug.com/1032820): Upgrade to WinHttpGetProxyForUrlEx2()
+ // if there is a clear reason to do so.
+ const DWORD result =
+ WinHttpProxyResolverFunctions::GetInstance().get_proxy_for_url_ex(
+ resolver_handle, wide_url.data(), autoproxy_options, context);
+ return (result == ERROR_IO_PENDING);
+}
+
+bool WinHttpAPIWrapper::CallWinHttpGetProxyResult(
+ HINTERNET resolver_handle,
+ WINHTTP_PROXY_RESULT* proxy_result) {
+ const DWORD result =
+ WinHttpProxyResolverFunctions::GetInstance().get_proxy_result(
+ resolver_handle, proxy_result);
+ return (result == ERROR_SUCCESS);
+}
+
+VOID WinHttpAPIWrapper::CallWinHttpFreeProxyResult(
+ WINHTTP_PROXY_RESULT* proxy_result) {
+ WinHttpProxyResolverFunctions::GetInstance().free_proxy_result(proxy_result);
+}
+
+void WinHttpAPIWrapper::CallWinHttpCloseHandle(HINTERNET internet_handle) {
+ WinHttpCloseHandle(internet_handle);
+}
+
+void WinHttpAPIWrapper::CloseSessionHandle() {
+ if (session_handle_) {
+ CallWinHttpCloseHandle(session_handle_);
+ session_handle_ = nullptr;
+ }
+}
+
+} // namespace net
diff --git a/chromium/net/proxy_resolution/win/winhttp_api_wrapper.h b/chromium/net/proxy_resolution/win/winhttp_api_wrapper.h
new file mode 100644
index 00000000000..f9b641d9453
--- /dev/null
+++ b/chromium/net/proxy_resolution/win/winhttp_api_wrapper.h
@@ -0,0 +1,130 @@
+// 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.
+
+#ifndef NET_PROXY_RESOLUTION_WIN_WINHTTP_API_WRAPPER_H_
+#define NET_PROXY_RESOLUTION_WIN_WINHTTP_API_WRAPPER_H_
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// This is a utility class that encapsulates the memory management necessary for
+// WINHTTP_CURRENT_USER_IE_PROXY_CONFIG in RAII style.
+class ScopedIEConfig final {
+ public:
+ ScopedIEConfig();
+
+ ScopedIEConfig(const ScopedIEConfig&) = delete;
+ ScopedIEConfig& operator=(const ScopedIEConfig&) = delete;
+
+ ~ScopedIEConfig();
+
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* config() { return &ie_config; }
+
+ private:
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0};
+};
+
+// This provides a layer of abstraction between calling code and WinHTTP APIs,
+// allowing them to be mocked out for testing. This object is not thread safe
+// and it's expected that the caller will handle using it on the same thread or
+// sequence. In general, documentation for these APIs can be found here:
+// https://docs.microsoft.com/en-us/windows/win32/api/winhttp/
+class NET_EXPORT WinHttpAPIWrapper {
+ public:
+ WinHttpAPIWrapper();
+
+ WinHttpAPIWrapper(const WinHttpAPIWrapper&) = delete;
+ WinHttpAPIWrapper& operator=(const WinHttpAPIWrapper&) = delete;
+
+ virtual ~WinHttpAPIWrapper();
+
+ // Creates our WinHttp session handle |session_handle_|. The lifetime of that
+ // session handle is determined by the lifetime of this object. It'll get
+ // closed when this object destructs.
+ virtual bool CallWinHttpOpen() WARN_UNUSED_RESULT;
+
+ // Controls the timeout for WinHttpGetProxyForUrlEx().
+ virtual bool CallWinHttpSetTimeouts(int resolve_timeout,
+ int connect_timeout,
+ int send_timeout,
+ int receive_timeout) WARN_UNUSED_RESULT;
+
+ // Sets the callback WinHttp will call into with the result of any
+ // asynchronous call.
+ virtual bool CallWinHttpSetStatusCallback(
+ WINHTTP_STATUS_CALLBACK internet_callback) WARN_UNUSED_RESULT;
+
+ // Fetches the proxy configs for the current active network connection and
+ // current Windows user. The |ie_proxy_config| says whether or not
+ // AutoProxy (WPAD) is enabled and if there's a PAC URL configured for this
+ // connection/user.
+ virtual bool CallWinHttpGetIEProxyConfigForCurrentUser(
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_proxy_config) WARN_UNUSED_RESULT;
+
+ // Creates a handle |resolver_handle| that should be used for the call to
+ // WinHttpGetProxyForUrlEx().
+ virtual bool CallWinHttpCreateProxyResolver(HINTERNET* out_resolver_handle)
+ WARN_UNUSED_RESULT;
+
+ // Using the specific |resolver_handle| handle from
+ // CallWinHttpCreateProxyResolver(), resolve a proxy for a specific |url| with
+ // the aid of some |autoproxy_options|. When WinHttpGetProxyForUrlEx()
+ // finishes its work or hits an error, it'll call into the callback set by
+ // CallWinHttpSetStatusCallback() above exactly once and supply the provided
+ // |context|.
+ //
+ // WinHttpGetProxyForUrlEx() will go async to do all necessary logic. As long
+ // as it receives good inputs (valid handle, valid combination of flags,
+ // non-null PAC URL if needed), this API will almost always return
+ // ERROR_IO_PENDING. It'll only fail for reasons like running out of memory.
+ // When it returns ERROR_IO_PENDING, CallWinHttpGetProxyForUrlEx() will return
+ // true.
+ //
+ // WinHttpGetProxyForUrlEx() will do proxy fallback internally and return to
+ // a proxy result. It will first check WPAD (if enabled). If that fails, it'll
+ // attempt to download and run any provided PAC script. If the PAC script was
+ // not provided or if it fails, it'll use the right per-interface static
+ // proxy. If all else fails or isn't configured, it'll simply return DIRECT.
+ // WinHttpGetProxyForUrlEx() supports commonly used enterprise proxy features
+ // such as DirectAccess/NRPT.
+ virtual bool CallWinHttpGetProxyForUrlEx(
+ HINTERNET resolver_handle,
+ const std::string& url,
+ WINHTTP_AUTOPROXY_OPTIONS* autoproxy_options,
+ DWORD_PTR context) WARN_UNUSED_RESULT;
+
+ // As long as CallWinHttpGetProxyForUrlEx() doesn't hit any errors, there will
+ // be a proxy result to examine. This function retrieves that proxy resolution
+ // result |proxy_result| using the resolver's handle |resolver_handle|. The
+ // result must be freed with CallWinHttpFreeProxyResult().
+ virtual bool CallWinHttpGetProxyResult(HINTERNET resolver_handle,
+ WINHTTP_PROXY_RESULT* proxy_result)
+ WARN_UNUSED_RESULT;
+
+ // Frees the |proxy_result| retrieved by CallWinHttpGetProxyResult().
+ virtual void CallWinHttpFreeProxyResult(WINHTTP_PROXY_RESULT* proxy_result);
+
+ // Every opened HINTERNET handle must be closed. This closes handle
+ // |internet_handle|. After being closed, WinHttp calls cannot be made using
+ // that handle.
+ virtual void CallWinHttpCloseHandle(HINTERNET internet_handle);
+
+ private:
+ // Closes |session_handle_|.
+ void CloseSessionHandle();
+
+ HINTERNET session_handle_ = nullptr;
+};
+
+} // namespace net
+
+#endif // NET_PROXY_RESOLUTION_WIN_WINHTTP_API_WRAPPER_H_
diff --git a/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.cc b/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.cc
new file mode 100644
index 00000000000..9fb8b4613f2
--- /dev/null
+++ b/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.cc
@@ -0,0 +1,41 @@
+// 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/winhttp_proxy_resolver_functions.h"
+
+namespace net {
+
+WinHttpProxyResolverFunctions::WinHttpProxyResolverFunctions() {
+ HMODULE winhttp_module =
+ LoadLibraryEx(L"winhttp.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (winhttp_module) {
+ create_proxy_resolver = reinterpret_cast<WinHttpCreateProxyResolverFunc>(
+ ::GetProcAddress(winhttp_module, "WinHttpCreateProxyResolver"));
+ get_proxy_for_url_ex = reinterpret_cast<WinHttpGetProxyForUrlExFunc>(
+ ::GetProcAddress(winhttp_module, "WinHttpGetProxyForUrlEx"));
+ get_proxy_result = reinterpret_cast<WinHttpGetProxyResultFunc>(
+ ::GetProcAddress(winhttp_module, "WinHttpGetProxyResult"));
+ free_proxy_result = reinterpret_cast<WinHttpFreeProxyResultFunc>(
+ ::GetProcAddress(winhttp_module, "WinHttpFreeProxyResult"));
+ }
+}
+
+// Never called due to base::NoDestructor.
+WinHttpProxyResolverFunctions::~WinHttpProxyResolverFunctions() = default;
+
+bool WinHttpProxyResolverFunctions::are_all_functions_loaded() const {
+ return create_proxy_resolver && get_proxy_for_url_ex && get_proxy_result &&
+ free_proxy_result;
+}
+
+// static
+const WinHttpProxyResolverFunctions&
+WinHttpProxyResolverFunctions::GetInstance() {
+ // This is a singleton for performance reasons. This avoids having to load
+ // proxy resolver functions multiple times.
+ static base::NoDestructor<WinHttpProxyResolverFunctions> instance;
+ return *instance;
+}
+
+} // namespace net
diff --git a/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.h b/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.h
new file mode 100644
index 00000000000..51b512a0038
--- /dev/null
+++ b/chromium/net/proxy_resolution/win/winhttp_proxy_resolver_functions.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef NET_PROXY_RESOLUTION_WIN_WINHTTP_PROXY_RESOLVER_FUNCTIONS_H_
+#define NET_PROXY_RESOLUTION_WIN_WINHTTP_PROXY_RESOLVER_FUNCTIONS_H_
+
+#include <windows.h>
+#include <winhttp.h>
+
+#include "base/no_destructor.h"
+
+namespace net {
+
+// Not all WinHttp APIs we'll be using exist in all versions of Windows.
+// Several only exist in Windows 8+. Thus, each function entry point must be
+// loaded dynamically.
+struct WinHttpProxyResolverFunctions {
+ public:
+ WinHttpProxyResolverFunctions(const WinHttpProxyResolverFunctions&) = delete;
+ WinHttpProxyResolverFunctions& operator=(
+ const WinHttpProxyResolverFunctions&) = delete;
+
+ bool are_all_functions_loaded() const;
+
+ static const WinHttpProxyResolverFunctions& GetInstance();
+
+ using WinHttpCreateProxyResolverFunc = decltype(WinHttpCreateProxyResolver)*;
+ using WinHttpGetProxyForUrlExFunc = decltype(WinHttpGetProxyForUrlEx)*;
+ using WinHttpGetProxyResultFunc = decltype(WinHttpGetProxyResult)*;
+ using WinHttpFreeProxyResultFunc = decltype(WinHttpFreeProxyResult)*;
+
+ WinHttpCreateProxyResolverFunc create_proxy_resolver = nullptr;
+ WinHttpGetProxyForUrlExFunc get_proxy_for_url_ex = nullptr;
+ WinHttpGetProxyResultFunc get_proxy_result = nullptr;
+ WinHttpFreeProxyResultFunc free_proxy_result = nullptr;
+
+ private:
+ friend class base::NoDestructor<WinHttpProxyResolverFunctions>;
+
+ WinHttpProxyResolverFunctions();
+ ~WinHttpProxyResolverFunctions();
+};
+
+} // namespace net
+
+#endif // NET_PROXY_RESOLUTION_WIN_WINHTTP_PROXY_RESOLVER_FUNCTIONS_H_