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