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
21namespace {
22
23const char kDataReductionProxy[] = "https://foo.com:443/";
24const char kDataReductionProxyDev[] = "http://foo-dev.com:80";
25const char kDataReductionProxyFallback[] = "http://bar.com:80";
26const char kDataReductionProxyKey[] = "12345";
27const char kDataReductionProxyAlt[] = "https://alt.com:443/";
28const char kDataReductionProxyAltFallback[] = "http://alt2.com:80";
29const char kDataReductionProxySSL[] = "http://ssl.com:80";
30
31const char kProbeURLWithOKResponse[] = "http://ok.org/";
32const char kProbeURLWithBadResponse[] = "http://bad.org/";
33const char kProbeURLWithNoResponse[] = "http://no.org/";
34const char kWarmupURLWithNoContentResponse[] = "http://warm.org/";
35
36}  // namespace
37
38namespace data_reduction_proxy {
39
40class DataReductionProxySettingsTest
41    : public ConcreteDataReductionProxySettingsTest<
42          DataReductionProxySettings> {
43};
44
45
46TEST_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
71TEST_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
78TEST_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
88TEST_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
107TEST_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
116TEST_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
159TEST_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
180TEST_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
211TEST_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
235TEST_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
249TEST_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.
300TEST_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
353TEST_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
397TEST_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
409TEST_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
417TEST_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
427TEST_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
436TEST_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
450TEST_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