1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "services/network/network_service_proxy_delegate.h"
6 
7 #include <string>
8 
9 #include "base/test/task_environment.h"
10 #include "mojo/public/cpp/bindings/remote.h"
11 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
12 #include "net/url_request/url_request_test_util.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 
16 namespace network {
17 namespace {
18 
19 constexpr char kHttpUrl[] = "http://example.com";
20 constexpr char kLocalhost[] = "http://localhost";
21 constexpr char kHttpsUrl[] = "https://example.com";
22 constexpr char kWebsocketUrl[] = "ws://example.com";
23 
24 }  // namespace
25 
26 MATCHER_P2(Contain,
27            expected_name,
28            expected_value,
29            std::string("headers ") + (negation ? "don't " : "") + "contain '" +
30                expected_name + ": " + expected_value + "'") {
31   std::string value;
32   return arg.GetHeader(expected_name, &value) && value == expected_value;
33 }
34 
35 class NetworkServiceProxyDelegateTest : public testing::Test {
36  public:
NetworkServiceProxyDelegateTest()37   NetworkServiceProxyDelegateTest() {}
38 
SetUp()39   void SetUp() override {
40     context_ = std::make_unique<net::TestURLRequestContext>(true);
41     context_->Init();
42   }
43 
44  protected:
CreateDelegate(mojom::CustomProxyConfigPtr config)45   std::unique_ptr<NetworkServiceProxyDelegate> CreateDelegate(
46       mojom::CustomProxyConfigPtr config) {
47     auto delegate = std::make_unique<NetworkServiceProxyDelegate>(
48         network::mojom::CustomProxyConfig::New(),
49         client_.BindNewPipeAndPassReceiver());
50     SetConfig(std::move(config));
51     return delegate;
52   }
53 
CreateRequest(const GURL & url)54   std::unique_ptr<net::URLRequest> CreateRequest(const GURL& url) {
55     return context_->CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
56                                    TRAFFIC_ANNOTATION_FOR_TESTS);
57   }
58 
SetConfig(mojom::CustomProxyConfigPtr config)59   void SetConfig(mojom::CustomProxyConfigPtr config) {
60     client_->OnCustomProxyConfigUpdated(std::move(config));
61     task_environment_.RunUntilIdle();
62   }
63 
64  private:
65   mojo::Remote<mojom::CustomProxyConfigClient> client_;
66   std::unique_ptr<net::TestURLRequestContext> context_;
67   base::test::TaskEnvironment task_environment_;
68 };
69 
TEST_F(NetworkServiceProxyDelegateTest,NullConfigDoesNotCrash)70 TEST_F(NetworkServiceProxyDelegateTest, NullConfigDoesNotCrash) {
71   mojo::Remote<mojom::CustomProxyConfigClient> client;
72   auto delegate = std::make_unique<NetworkServiceProxyDelegate>(
73       nullptr, client.BindNewPipeAndPassReceiver());
74 
75   net::HttpRequestHeaders headers;
76   auto request = CreateRequest(GURL(kHttpUrl));
77 }
78 
TEST_F(NetworkServiceProxyDelegateTest,AddsHeadersToTunnelRequest)79 TEST_F(NetworkServiceProxyDelegateTest, AddsHeadersToTunnelRequest) {
80   auto config = mojom::CustomProxyConfig::New();
81   config->rules.ParseFromString("https://proxy");
82   config->connect_tunnel_headers.SetHeader("connect", "baz");
83   auto delegate = CreateDelegate(std::move(config));
84 
85   net::HttpRequestHeaders headers;
86   auto proxy_server = net::ProxyServer::FromPacString("HTTPS proxy");
87   delegate->OnBeforeTunnelRequest(proxy_server, &headers);
88 
89   EXPECT_THAT(headers, Contain("connect", "baz"));
90 }
91 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxySuccessHttpProxy)92 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxySuccessHttpProxy) {
93   auto config = mojom::CustomProxyConfig::New();
94   config->rules.ParseFromString("http=foo");
95   auto delegate = CreateDelegate(std::move(config));
96 
97   net::ProxyInfo result;
98   result.UseDirect();
99   delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
100                            &result);
101 
102   net::ProxyList expected_proxy_list;
103   expected_proxy_list.AddProxyServer(
104       net::ProxyServer::FromPacString("PROXY foo"));
105   EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
106   // HTTP proxies are not used as alternative QUIC proxies.
107   EXPECT_FALSE(result.alternative_proxy().is_valid());
108 }
109 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxySuccessHttpsUrl)110 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxySuccessHttpsUrl) {
111   auto config = mojom::CustomProxyConfig::New();
112   config->rules.ParseFromString("https://foo");
113   auto delegate = CreateDelegate(std::move(config));
114 
115   net::ProxyInfo result;
116   result.UseDirect();
117   delegate->OnResolveProxy(GURL(kHttpsUrl), "GET", net::ProxyRetryInfoMap(),
118                            &result);
119 
120   net::ProxyList expected_proxy_list;
121   expected_proxy_list.AddProxyServer(
122       net::ProxyServer::FromPacString("HTTPS foo"));
123   EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
124 }
125 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxySuccessWebSocketUrl)126 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxySuccessWebSocketUrl) {
127   auto config = mojom::CustomProxyConfig::New();
128   config->rules.ParseFromString("https://foo");
129   auto delegate = CreateDelegate(std::move(config));
130 
131   net::ProxyInfo result;
132   result.UseDirect();
133   delegate->OnResolveProxy(GURL(kWebsocketUrl), "GET", net::ProxyRetryInfoMap(),
134                            &result);
135 
136   net::ProxyList expected_proxy_list;
137   expected_proxy_list.AddProxyServer(
138       net::ProxyServer::FromPacString("HTTPS foo"));
139   EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
140 }
141 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyNoRuleForHttpsUrl)142 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyNoRuleForHttpsUrl) {
143   auto config = mojom::CustomProxyConfig::New();
144   config->rules.ParseFromString("http=foo");
145   auto delegate = CreateDelegate(std::move(config));
146 
147   net::ProxyInfo result;
148   result.UseDirect();
149   delegate->OnResolveProxy(GURL(kHttpsUrl), "GET", net::ProxyRetryInfoMap(),
150                            &result);
151 
152   EXPECT_TRUE(result.is_direct());
153   EXPECT_FALSE(result.alternative_proxy().is_valid());
154 }
155 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyLocalhost)156 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyLocalhost) {
157   auto config = mojom::CustomProxyConfig::New();
158   config->rules.ParseFromString("http=foo");
159   auto delegate = CreateDelegate(std::move(config));
160 
161   net::ProxyInfo result;
162   result.UseDirect();
163   delegate->OnResolveProxy(GURL(kLocalhost), "GET", net::ProxyRetryInfoMap(),
164                            &result);
165 
166   EXPECT_TRUE(result.is_direct());
167   EXPECT_FALSE(result.alternative_proxy().is_valid());
168 }
169 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyEmptyConfig)170 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyEmptyConfig) {
171   auto delegate = CreateDelegate(mojom::CustomProxyConfig::New());
172 
173   net::ProxyInfo result;
174   result.UseDirect();
175   delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
176                            &result);
177 
178   EXPECT_TRUE(result.is_direct());
179   EXPECT_FALSE(result.alternative_proxy().is_valid());
180 }
181 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyNonIdempotentMethod)182 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyNonIdempotentMethod) {
183   auto config = mojom::CustomProxyConfig::New();
184   config->rules.ParseFromString("http=foo");
185   auto delegate = CreateDelegate(std::move(config));
186 
187   net::ProxyInfo result;
188   result.UseDirect();
189   delegate->OnResolveProxy(GURL(kHttpUrl), "POST", net::ProxyRetryInfoMap(),
190                            &result);
191 
192   EXPECT_TRUE(result.is_direct());
193   EXPECT_FALSE(result.alternative_proxy().is_valid());
194 }
195 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyNonIdempotentMethodAllowed)196 TEST_F(NetworkServiceProxyDelegateTest,
197        OnResolveProxyNonIdempotentMethodAllowed) {
198   auto config = mojom::CustomProxyConfig::New();
199   config->rules.ParseFromString("http=foo");
200   config->allow_non_idempotent_methods = true;
201   auto delegate = CreateDelegate(std::move(config));
202 
203   net::ProxyInfo result;
204   result.UseDirect();
205   delegate->OnResolveProxy(GURL(kHttpUrl), "POST", net::ProxyRetryInfoMap(),
206                            &result);
207 
208   net::ProxyList expected_proxy_list;
209   expected_proxy_list.AddProxyServer(
210       net::ProxyServer::FromPacString("PROXY foo"));
211   EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
212 }
213 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyBypassForWebSocketScheme)214 TEST_F(NetworkServiceProxyDelegateTest,
215        OnResolveProxyBypassForWebSocketScheme) {
216   auto config = mojom::CustomProxyConfig::New();
217   config->rules.ParseFromString("http=foo");
218   config->rules.bypass_rules.AddRuleFromString(GURL(kWebsocketUrl).scheme() +
219                                                "://*");
220   auto delegate = CreateDelegate(std::move(config));
221 
222   net::ProxyInfo result;
223   result.UseDirect();
224   delegate->OnResolveProxy(GURL(kWebsocketUrl), "GET", net::ProxyRetryInfoMap(),
225                            &result);
226 
227   EXPECT_TRUE(result.is_direct());
228   EXPECT_FALSE(result.alternative_proxy().is_valid());
229 }
230 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyDoesNotOverrideExisting)231 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyDoesNotOverrideExisting) {
232   auto config = mojom::CustomProxyConfig::New();
233   config->rules.ParseFromString("http=foo");
234   config->should_override_existing_config = false;
235   auto delegate = CreateDelegate(std::move(config));
236 
237   net::ProxyInfo result;
238   result.UsePacString("PROXY bar");
239   delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
240                            &result);
241 
242   net::ProxyList expected_proxy_list;
243   expected_proxy_list.AddProxyServer(
244       net::ProxyServer::FromPacString("PROXY bar"));
245   EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
246   EXPECT_FALSE(result.alternative_proxy().is_valid());
247 }
248 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyOverridesExisting)249 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyOverridesExisting) {
250   auto config = mojom::CustomProxyConfig::New();
251   config->rules.ParseFromString("http=foo");
252   config->should_override_existing_config = true;
253   auto delegate = CreateDelegate(std::move(config));
254 
255   net::ProxyInfo result;
256   result.UsePacString("PROXY bar");
257   delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
258                            &result);
259 
260   net::ProxyList expected_proxy_list;
261   expected_proxy_list.AddProxyServer(
262       net::ProxyServer::FromPacString("PROXY foo"));
263   EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
264   EXPECT_FALSE(result.alternative_proxy().is_valid());
265 }
266 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyDeprioritizesBadProxies)267 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyDeprioritizesBadProxies) {
268   auto config = mojom::CustomProxyConfig::New();
269   config->rules.ParseFromString("http=foo,bar");
270   auto delegate = CreateDelegate(std::move(config));
271 
272   net::ProxyInfo result;
273   result.UseDirect();
274   net::ProxyRetryInfoMap retry_map;
275   net::ProxyRetryInfo& info = retry_map["foo:80"];
276   info.try_while_bad = false;
277   info.bad_until = base::TimeTicks::Now() + base::TimeDelta::FromDays(2);
278   delegate->OnResolveProxy(GURL(kHttpUrl), "GET", retry_map, &result);
279 
280   net::ProxyList expected_proxy_list;
281   expected_proxy_list.AddProxyServer(
282       net::ProxyServer::FromPacString("PROXY bar"));
283   EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
284 }
285 
TEST_F(NetworkServiceProxyDelegateTest,OnResolveProxyAllProxiesBad)286 TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyAllProxiesBad) {
287   auto config = mojom::CustomProxyConfig::New();
288   config->rules.ParseFromString("http=foo");
289   auto delegate = CreateDelegate(std::move(config));
290 
291   net::ProxyInfo result;
292   result.UseDirect();
293   net::ProxyRetryInfoMap retry_map;
294   net::ProxyRetryInfo& info = retry_map["foo:80"];
295   info.try_while_bad = false;
296   info.bad_until = base::TimeTicks::Now() + base::TimeDelta::FromDays(2);
297   delegate->OnResolveProxy(GURL(kHttpUrl), "GET", retry_map, &result);
298 
299   EXPECT_TRUE(result.is_direct());
300 }
301 
TEST_F(NetworkServiceProxyDelegateTest,InitialConfigUsedForProxy)302 TEST_F(NetworkServiceProxyDelegateTest, InitialConfigUsedForProxy) {
303   auto config = mojom::CustomProxyConfig::New();
304   config->rules.ParseFromString("http=foo");
305   mojo::Remote<mojom::CustomProxyConfigClient> client;
306   auto delegate = std::make_unique<NetworkServiceProxyDelegate>(
307       std::move(config), client.BindNewPipeAndPassReceiver());
308 
309   net::ProxyInfo result;
310   result.UseDirect();
311   delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
312                            &result);
313 
314   net::ProxyList expected_proxy_list;
315   expected_proxy_list.AddProxyServer(
316       net::ProxyServer::FromPacString("PROXY foo"));
317   EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
318 }
319 
320 }  // namespace network
321