1 // Copyright 2014 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 "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h"
6
7 #include "base/command_line.h"
8 #include "base/md5.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h"
12 #include "components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.h"
13 #include "components/data_reduction_proxy/common/data_reduction_proxy_pref_names.h"
14 #include "components/data_reduction_proxy/common/data_reduction_proxy_switches.h"
15 #include "net/http/http_auth.h"
16 #include "net/http/http_auth_cache.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "url/gurl.h"
20
21 namespace {
22
23 const char kDataReductionProxy[] = "https://foo.com:443/";
24 const char kDataReductionProxyDev[] = "http://foo-dev.com:80";
25 const char kDataReductionProxyFallback[] = "http://bar.com:80";
26 const char kDataReductionProxyKey[] = "12345";
27 const char kDataReductionProxyAlt[] = "https://alt.com:443/";
28 const char kDataReductionProxyAltFallback[] = "http://alt2.com:80";
29 const char kDataReductionProxySSL[] = "http://ssl.com:80";
30
31 const char kProbeURLWithOKResponse[] = "http://ok.org/";
32 const char kProbeURLWithBadResponse[] = "http://bad.org/";
33 const char kProbeURLWithNoResponse[] = "http://no.org/";
34 const char kWarmupURLWithNoContentResponse[] = "http://warm.org/";
35
36 } // namespace
37
38 namespace data_reduction_proxy {
39
40 class DataReductionProxySettingsTest
41 : public ConcreteDataReductionProxySettingsTest<
42 DataReductionProxySettings> {
43 };
44
45
TEST_F(DataReductionProxySettingsTest,TestAuthenticationInit)46 TEST_F(DataReductionProxySettingsTest, TestAuthenticationInit) {
47 net::HttpAuthCache cache;
48 DataReductionProxyParams drp_params(
49 DataReductionProxyParams::kAllowed |
50 DataReductionProxyParams::kFallbackAllowed |
51 DataReductionProxyParams::kPromoAllowed);
52 drp_params.set_key(kDataReductionProxyKey);
53 DataReductionProxySettings::InitDataReductionAuthentication(
54 &cache, &drp_params);
55 DataReductionProxyParams::DataReductionProxyList proxies =
56 drp_params.GetAllowedProxies();
57 for (DataReductionProxyParams::DataReductionProxyList::iterator it =
58 proxies.begin(); it != proxies.end(); ++it) {
59 net::HttpAuthCache::Entry* entry = cache.LookupByPath(*it,
60 std::string("/"));
61 EXPECT_TRUE(entry != NULL);
62 EXPECT_EQ(net::HttpAuth::AUTH_SCHEME_SPDYPROXY, entry->scheme());
63 EXPECT_EQ("SpdyProxy", entry->auth_challenge().substr(0,9));
64 }
65 GURL bad_server = GURL("https://bad.proxy.com/");
66 net::HttpAuthCache::Entry* entry =
67 cache.LookupByPath(bad_server, std::string());
68 EXPECT_TRUE(entry == NULL);
69 }
70
TEST_F(DataReductionProxySettingsTest,TestGetDataReductionProxyOrigin)71 TEST_F(DataReductionProxySettingsTest, TestGetDataReductionProxyOrigin) {
72 // SetUp() adds the origin to the command line, which should be returned here.
73 std::string result =
74 settings_->params()->origin().spec();
75 EXPECT_EQ(GURL(kDataReductionProxy), GURL(result));
76 }
77
TEST_F(DataReductionProxySettingsTest,TestGetDataReductionProxyDevOrigin)78 TEST_F(DataReductionProxySettingsTest, TestGetDataReductionProxyDevOrigin) {
79 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
80 switches::kDataReductionProxyDev, kDataReductionProxyDev);
81 ResetSettings(true, true, false, true);
82 std::string result =
83 settings_->params()->origin().spec();
84 EXPECT_EQ(GURL(kDataReductionProxyDev), GURL(result));
85 }
86
87
TEST_F(DataReductionProxySettingsTest,TestGetDataReductionProxies)88 TEST_F(DataReductionProxySettingsTest, TestGetDataReductionProxies) {
89 DataReductionProxyParams drp_params(
90 DataReductionProxyParams::kAllowed |
91 DataReductionProxyParams::kFallbackAllowed |
92 DataReductionProxyParams::kPromoAllowed);
93 DataReductionProxyParams::DataReductionProxyList proxies =
94 drp_params.GetAllowedProxies();
95
96 unsigned int expected_proxy_size = 2u;
97 EXPECT_EQ(expected_proxy_size, proxies.size());
98
99 // Command line proxies have precedence, so even if there were other values
100 // compiled in, these should be the ones in the list.
101 EXPECT_EQ("foo.com", proxies[0].host());
102 EXPECT_EQ(443 ,proxies[0].EffectiveIntPort());
103 EXPECT_EQ("bar.com", proxies[1].host());
104 EXPECT_EQ(80, proxies[1].EffectiveIntPort());
105 }
106
TEST_F(DataReductionProxySettingsTest,TestAuthHashGeneration)107 TEST_F(DataReductionProxySettingsTest, TestAuthHashGeneration) {
108 std::string salt = "8675309"; // Jenny's number to test the hash generator.
109 std::string salted_key = salt + kDataReductionProxyKey + salt;
110 base::string16 expected_hash = base::UTF8ToUTF16(base::MD5String(salted_key));
111 EXPECT_EQ(expected_hash,
112 DataReductionProxySettings::AuthHashForSalt(
113 8675309, kDataReductionProxyKey));
114 }
115
TEST_F(DataReductionProxySettingsTest,TestSetProxyConfigs)116 TEST_F(DataReductionProxySettingsTest, TestSetProxyConfigs) {
117 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
118 switches::kDataReductionProxyAlt, kDataReductionProxyAlt);
119 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
120 switches::kDataReductionProxyAltFallback, kDataReductionProxyAltFallback);
121 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
122 switches::kDataReductionSSLProxy, kDataReductionProxySSL);
123 ResetSettings(true, true, true, true);
124 TestDataReductionProxyConfig* config =
125 static_cast<TestDataReductionProxyConfig*>(
126 settings_->configurator());
127
128 settings_->SetProxyConfigs(true, true, false, false);
129 EXPECT_TRUE(config->enabled_);
130 EXPECT_TRUE(net::HostPortPair::FromString(kDataReductionProxyAlt).Equals(
131 net::HostPortPair::FromString(config->origin_)));
132 EXPECT_TRUE(
133 net::HostPortPair::FromString(kDataReductionProxyAltFallback).Equals(
134 net::HostPortPair::FromString(config->fallback_origin_)));
135 EXPECT_TRUE(net::HostPortPair::FromString(kDataReductionProxySSL).Equals(
136 net::HostPortPair::FromString(config->ssl_origin_)));
137
138 settings_->SetProxyConfigs(true, false, false, false);
139 EXPECT_TRUE(config->enabled_);
140 EXPECT_TRUE(net::HostPortPair::FromString(kDataReductionProxy).Equals(
141 net::HostPortPair::FromString(config->origin_)));
142 EXPECT_TRUE(net::HostPortPair::FromString(kDataReductionProxyFallback).Equals(
143 net::HostPortPair::FromString(config->fallback_origin_)));
144 EXPECT_EQ("", config->ssl_origin_);
145
146 settings_->SetProxyConfigs(false, true, false, false);
147 EXPECT_FALSE(config->enabled_);
148 EXPECT_EQ("", config->origin_);
149 EXPECT_EQ("", config->fallback_origin_);
150 EXPECT_EQ("", config->ssl_origin_);
151
152 settings_->SetProxyConfigs(false, false, false, false);
153 EXPECT_FALSE(config->enabled_);
154 EXPECT_EQ("", config->origin_);
155 EXPECT_EQ("", config->fallback_origin_);
156 EXPECT_EQ("", config->ssl_origin_);
157 }
158
TEST_F(DataReductionProxySettingsTest,TestIsProxyEnabledOrManaged)159 TEST_F(DataReductionProxySettingsTest, TestIsProxyEnabledOrManaged) {
160 settings_->InitPrefMembers();
161 base::MessageLoopForUI loop;
162 // The proxy is disabled initially.
163 settings_->enabled_by_user_ = false;
164 settings_->SetProxyConfigs(false, false, false, false);
165
166 EXPECT_FALSE(settings_->IsDataReductionProxyEnabled());
167 EXPECT_FALSE(settings_->IsDataReductionProxyManaged());
168
169 CheckOnPrefChange(true, true, false);
170 EXPECT_TRUE(settings_->IsDataReductionProxyEnabled());
171 EXPECT_FALSE(settings_->IsDataReductionProxyManaged());
172
173 CheckOnPrefChange(true, true, true);
174 EXPECT_TRUE(settings_->IsDataReductionProxyEnabled());
175 EXPECT_TRUE(settings_->IsDataReductionProxyManaged());
176
177 base::MessageLoop::current()->RunUntilIdle();
178 }
179
TEST_F(DataReductionProxySettingsTest,TestAcceptableChallenges)180 TEST_F(DataReductionProxySettingsTest, TestAcceptableChallenges) {
181 typedef struct {
182 std::string host;
183 std::string realm;
184 bool expected_to_succeed;
185 } challenge_test;
186
187 challenge_test tests[] = {
188 {"foo.com:443", "", false}, // 0. No realm.
189 {"foo.com:443", "xxx", false}, // 1. Wrong realm.
190 {"foo.com:443", "spdyproxy", false}, // 2. Case matters.
191 {"foo.com:443", "SpdyProxy", true}, // 3. OK.
192 {"foo.com:443", "SpdyProxy1234567", true}, // 4. OK
193 {"bar.com:80", "SpdyProxy1234567", true}, // 5. OK.
194 {"foo.com:443", "SpdyProxyxxx", true}, // 6. OK
195 {"", "SpdyProxy1234567", false}, // 7. No challenger.
196 {"xxx.net:443", "SpdyProxy1234567", false}, // 8. Wrong host.
197 {"foo.com", "SpdyProxy1234567", false}, // 9. No port.
198 {"foo.com:80", "SpdyProxy1234567", false}, // 10.Wrong port.
199 {"bar.com:81", "SpdyProxy1234567", false}, // 11.Wrong port.
200 };
201
202 for (int i = 0; i <= 11; ++i) {
203 scoped_refptr<net::AuthChallengeInfo> auth_info(new net::AuthChallengeInfo);
204 auth_info->challenger = net::HostPortPair::FromString(tests[i].host);
205 auth_info->realm = tests[i].realm;
206 EXPECT_EQ(tests[i].expected_to_succeed,
207 settings_->IsAcceptableAuthChallenge(auth_info.get()));
208 }
209 }
210
TEST_F(DataReductionProxySettingsTest,TestChallengeTokens)211 TEST_F(DataReductionProxySettingsTest, TestChallengeTokens) {
212 typedef struct {
213 std::string realm;
214 bool expected_empty_token;
215 } token_test;
216
217 token_test tests[] = {
218 {"", true}, // 0. No realm.
219 {"xxx", true}, // 1. realm too short.
220 {"spdyproxy", true}, // 2. no salt.
221 {"SpdyProxyxxx", true}, // 3. Salt not an int.
222 {"SpdyProxy1234567", false}, // 4. OK
223 };
224
225 for (int i = 0; i <= 4; ++i) {
226 scoped_refptr<net::AuthChallengeInfo> auth_info(new net::AuthChallengeInfo);
227 auth_info->challenger =
228 net::HostPortPair::FromString(kDataReductionProxy);
229 auth_info->realm = tests[i].realm;
230 base::string16 token = settings_->GetTokenForAuthChallenge(auth_info.get());
231 EXPECT_EQ(tests[i].expected_empty_token, token.empty());
232 }
233 }
234
TEST_F(DataReductionProxySettingsTest,TestResetDataReductionStatistics)235 TEST_F(DataReductionProxySettingsTest, TestResetDataReductionStatistics) {
236 int64 original_content_length;
237 int64 received_content_length;
238 int64 last_update_time;
239 settings_->ResetDataReductionStatistics();
240 settings_->GetContentLengths(kNumDaysInHistory,
241 &original_content_length,
242 &received_content_length,
243 &last_update_time);
244 EXPECT_EQ(0L, original_content_length);
245 EXPECT_EQ(0L, received_content_length);
246 EXPECT_EQ(last_update_time_.ToInternalValue(), last_update_time);
247 }
248
TEST_F(DataReductionProxySettingsTest,TestContentLengths)249 TEST_F(DataReductionProxySettingsTest, TestContentLengths) {
250 int64 original_content_length;
251 int64 received_content_length;
252 int64 last_update_time;
253
254 // Request |kNumDaysInHistory| days.
255 settings_->GetContentLengths(kNumDaysInHistory,
256 &original_content_length,
257 &received_content_length,
258 &last_update_time);
259 const unsigned int days = kNumDaysInHistory;
260 // Received content length history values are 0 to |kNumDaysInHistory - 1|.
261 int64 expected_total_received_content_length = (days - 1L) * days / 2;
262 // Original content length history values are 0 to
263 // |2 * (kNumDaysInHistory - 1)|.
264 long expected_total_original_content_length = (days - 1L) * days;
265 EXPECT_EQ(expected_total_original_content_length, original_content_length);
266 EXPECT_EQ(expected_total_received_content_length, received_content_length);
267 EXPECT_EQ(last_update_time_.ToInternalValue(), last_update_time);
268
269 // Request |kNumDaysInHistory - 1| days.
270 settings_->GetContentLengths(kNumDaysInHistory - 1,
271 &original_content_length,
272 &received_content_length,
273 &last_update_time);
274 expected_total_received_content_length -= (days - 1);
275 expected_total_original_content_length -= 2 * (days - 1);
276 EXPECT_EQ(expected_total_original_content_length, original_content_length);
277 EXPECT_EQ(expected_total_received_content_length, received_content_length);
278
279 // Request 0 days.
280 settings_->GetContentLengths(0,
281 &original_content_length,
282 &received_content_length,
283 &last_update_time);
284 expected_total_received_content_length = 0;
285 expected_total_original_content_length = 0;
286 EXPECT_EQ(expected_total_original_content_length, original_content_length);
287 EXPECT_EQ(expected_total_received_content_length, received_content_length);
288
289 // Request 1 day. First day had 0 bytes so should be same as 0 days.
290 settings_->GetContentLengths(1,
291 &original_content_length,
292 &received_content_length,
293 &last_update_time);
294 EXPECT_EQ(expected_total_original_content_length, original_content_length);
295 EXPECT_EQ(expected_total_received_content_length, received_content_length);
296 }
297
298 // TODO(marq): Add a test to verify that MaybeActivateDataReductionProxy
299 // is called when the pref in |settings_| is enabled.
TEST_F(DataReductionProxySettingsTest,TestMaybeActivateDataReductionProxy)300 TEST_F(DataReductionProxySettingsTest, TestMaybeActivateDataReductionProxy) {
301 // Initialize the pref member in |settings_| without the usual callback
302 // so it won't trigger MaybeActivateDataReductionProxy when the pref value
303 // is set.
304 settings_->spdy_proxy_auth_enabled_.Init(
305 prefs::kDataReductionProxyEnabled,
306 settings_->GetOriginalProfilePrefs());
307 settings_->data_reduction_proxy_alternative_enabled_.Init(
308 prefs::kDataReductionProxyAltEnabled,
309 settings_->GetOriginalProfilePrefs());
310
311 // TODO(bengr): Test enabling/disabling while a probe is outstanding.
312 base::MessageLoopForUI loop;
313 // The proxy is enabled and unrestructed initially.
314 // Request succeeded but with bad response, expect proxy to be restricted.
315 CheckProbe(true,
316 kProbeURLWithBadResponse,
317 kWarmupURLWithNoContentResponse,
318 "Bad",
319 true,
320 true,
321 true,
322 false);
323 // Request succeeded with valid response, expect proxy to be unrestricted.
324 CheckProbe(true,
325 kProbeURLWithOKResponse,
326 kWarmupURLWithNoContentResponse,
327 "OK",
328 true,
329 true,
330 false,
331 false);
332 // Request failed, expect proxy to be enabled but restricted.
333 CheckProbe(true,
334 kProbeURLWithNoResponse,
335 kWarmupURLWithNoContentResponse,
336 "",
337 false,
338 true,
339 true,
340 false);
341 // The proxy is disabled initially. Probes should not be emitted to change
342 // state.
343 CheckProbe(false,
344 kProbeURLWithOKResponse,
345 kWarmupURLWithNoContentResponse,
346 "OK",
347 true,
348 false,
349 false,
350 false);
351 }
352
TEST_F(DataReductionProxySettingsTest,TestOnIPAddressChanged)353 TEST_F(DataReductionProxySettingsTest, TestOnIPAddressChanged) {
354 base::MessageLoopForUI loop;
355 // The proxy is enabled initially.
356 pref_service_.SetBoolean(prefs::kDataReductionProxyEnabled, true);
357 settings_->spdy_proxy_auth_enabled_.Init(
358 prefs::kDataReductionProxyEnabled,
359 settings_->GetOriginalProfilePrefs());
360 settings_->data_reduction_proxy_alternative_enabled_.Init(
361 prefs::kDataReductionProxyAltEnabled,
362 settings_->GetOriginalProfilePrefs());
363 settings_->enabled_by_user_ = true;
364 settings_->restricted_by_carrier_ = false;
365 settings_->SetProxyConfigs(true, false, false, true);
366 // IP address change triggers a probe that succeeds. Proxy remains
367 // unrestricted.
368 CheckProbeOnIPChange(kProbeURLWithOKResponse,
369 kWarmupURLWithNoContentResponse,
370 "OK",
371 true,
372 false,
373 false);
374 // IP address change triggers a probe that fails. Proxy is restricted.
375 CheckProbeOnIPChange(kProbeURLWithBadResponse,
376 kWarmupURLWithNoContentResponse,
377 "Bad",
378 true,
379 true,
380 false);
381 // IP address change triggers a probe that fails. Proxy remains restricted.
382 CheckProbeOnIPChange(kProbeURLWithBadResponse,
383 kWarmupURLWithNoContentResponse,
384 "Bad",
385 true,
386 true,
387 false);
388 // IP address change triggers a probe that succeed. Proxy is unrestricted.
389 CheckProbeOnIPChange(kProbeURLWithBadResponse,
390 kWarmupURLWithNoContentResponse,
391 "OK",
392 true,
393 false,
394 false);
395 }
396
TEST_F(DataReductionProxySettingsTest,TestOnProxyEnabledPrefChange)397 TEST_F(DataReductionProxySettingsTest, TestOnProxyEnabledPrefChange) {
398 settings_->InitPrefMembers();
399 base::MessageLoopForUI loop;
400 // The proxy is enabled initially.
401 settings_->enabled_by_user_ = true;
402 settings_->SetProxyConfigs(true, false, false, true);
403 // The pref is disabled, so correspondingly should be the proxy.
404 CheckOnPrefChange(false, false, false);
405 // The pref is enabled, so correspondingly should be the proxy.
406 CheckOnPrefChange(true, true, false);
407 }
408
TEST_F(DataReductionProxySettingsTest,TestInitDataReductionProxyOn)409 TEST_F(DataReductionProxySettingsTest, TestInitDataReductionProxyOn) {
410 MockSettings* settings = static_cast<MockSettings*>(settings_.get());
411 EXPECT_CALL(*settings, RecordStartupState(PROXY_ENABLED));
412
413 pref_service_.SetBoolean(prefs::kDataReductionProxyEnabled, true);
414 CheckInitDataReductionProxy(true);
415 }
416
TEST_F(DataReductionProxySettingsTest,TestInitDataReductionProxyOff)417 TEST_F(DataReductionProxySettingsTest, TestInitDataReductionProxyOff) {
418 // InitDataReductionProxySettings with the preference off will directly call
419 // LogProxyState.
420 MockSettings* settings = static_cast<MockSettings*>(settings_.get());
421 EXPECT_CALL(*settings, RecordStartupState(PROXY_DISABLED));
422
423 pref_service_.SetBoolean(prefs::kDataReductionProxyEnabled, false);
424 CheckInitDataReductionProxy(false);
425 }
426
TEST_F(DataReductionProxySettingsTest,TestSetProxyFromCommandLine)427 TEST_F(DataReductionProxySettingsTest, TestSetProxyFromCommandLine) {
428 MockSettings* settings = static_cast<MockSettings*>(settings_.get());
429 EXPECT_CALL(*settings, RecordStartupState(PROXY_ENABLED));
430
431 CommandLine::ForCurrentProcess()->AppendSwitch(
432 switches::kEnableDataReductionProxy);
433 CheckInitDataReductionProxy(true);
434 }
435
TEST_F(DataReductionProxySettingsTest,TestGetDailyContentLengths)436 TEST_F(DataReductionProxySettingsTest, TestGetDailyContentLengths) {
437 DataReductionProxySettings::ContentLengthList result =
438 settings_->GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength);
439
440 ASSERT_FALSE(result.empty());
441 ASSERT_EQ(kNumDaysInHistory, result.size());
442
443 for (size_t i = 0; i < kNumDaysInHistory; ++i) {
444 long expected_length =
445 static_cast<long>((kNumDaysInHistory - 1 - i) * 2);
446 ASSERT_EQ(expected_length, result[i]);
447 }
448 }
449
TEST_F(DataReductionProxySettingsTest,CheckInitMetricsWhenNotAllowed)450 TEST_F(DataReductionProxySettingsTest, CheckInitMetricsWhenNotAllowed) {
451 // No call to |AddProxyToCommandLine()| was made, so the proxy feature
452 // should be unavailable.
453 base::MessageLoopForUI loop;
454 // Clear the command line. Setting flags can force the proxy to be allowed.
455 CommandLine::ForCurrentProcess()->InitFromArgv(0, NULL);
456
457 ResetSettings(false, false, false, false);
458 MockSettings* settings = static_cast<MockSettings*>(settings_.get());
459 EXPECT_FALSE(settings->params()->allowed());
460 EXPECT_CALL(*settings, RecordStartupState(PROXY_NOT_AVAILABLE));
461
462 scoped_ptr<DataReductionProxyConfigurator> configurator(
463 new TestDataReductionProxyConfig());
464 settings_->SetProxyConfigurator(configurator.Pass());
465 scoped_refptr<net::TestURLRequestContextGetter> request_context =
466 new net::TestURLRequestContextGetter(base::MessageLoopProxy::current());
467 settings_->InitDataReductionProxySettings(&pref_service_,
468 &pref_service_,
469 request_context.get());
470
471 base::MessageLoop::current()->RunUntilIdle();
472 }
473
474 } // namespace data_reduction_proxy
475