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.
5 #include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/metrics/histogram.h"
12 #include "base/test/histogram_tester.h"
13 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers_test_utils.h"
14 #include "net/base/host_port_pair.h"
15 #include "net/base/request_priority.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/http/http_util.h"
18 #include "net/url_request/url_request.h"
19 #include "net/url_request/url_request_job_factory_impl.h"
20 #include "net/url_request/url_request_status.h"
21 #include "net/url_request/url_request_test_job.h"
22 #include "net/url_request/url_request_test_util.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 using testing::Return;
30 class DataReductionProxyParamsMock :
31 public data_reduction_proxy::DataReductionProxyParams {
33 DataReductionProxyParamsMock() :
34 data_reduction_proxy::DataReductionProxyParams(0) {}
35 virtual ~DataReductionProxyParamsMock() {}
39 bool(const net::HostPortPair& host_port_pair,
40 data_reduction_proxy::DataReductionProxyTypeInfo* proxy_info));
42 WasDataReductionProxyUsed,
43 bool(const net::URLRequest*,
44 data_reduction_proxy::DataReductionProxyTypeInfo* proxy_info));
47 DISALLOW_COPY_AND_ASSIGN(DataReductionProxyParamsMock);
52 namespace data_reduction_proxy {
54 class DataReductionProxyUsageStatsTest : public testing::Test {
56 DataReductionProxyUsageStatsTest()
57 : loop_proxy_(base::MessageLoopProxy::current().get()),
62 // The |test_job_factory_| takes ownership of the interceptor.
63 test_job_interceptor_ = new net::TestJobInterceptor();
64 EXPECT_TRUE(test_job_factory_.SetProtocolHandler(url::kHttpScheme,
65 test_job_interceptor_));
67 context_.set_job_factory(&test_job_factory_);
69 mock_url_request_ = context_.CreateRequest(GURL(), net::IDLE, &delegate_,
73 void NotifyUnavailable(bool unavailable) {
74 unavailable_ = unavailable;
77 scoped_ptr<net::URLRequest> CreateURLRequestWithResponseHeaders(
79 const std::string& raw_response_headers) {
80 scoped_ptr<net::URLRequest> fake_request = context_.CreateRequest(
81 url, net::IDLE, &delegate_, NULL);
83 // Create a test job that will fill in the given response headers for the
85 scoped_refptr<net::URLRequestTestJob> test_job(
86 new net::URLRequestTestJob(fake_request.get(),
87 context_.network_delegate(),
88 raw_response_headers, std::string(), true));
90 // Configure the interceptor to use the test job to handle the next request.
91 test_job_interceptor_->set_main_intercept_job(test_job.get());
92 fake_request->Start();
93 base::MessageLoop::current()->RunUntilIdle();
95 EXPECT_TRUE(fake_request->response_headers() != NULL);
96 return fake_request.Pass();
99 // Required for base::MessageLoopProxy::current().
100 base::MessageLoopForUI loop_;
101 base::MessageLoopProxy* loop_proxy_;
104 net::TestURLRequestContext context_;
105 net::TestDelegate delegate_;
106 DataReductionProxyParamsMock mock_params_;
107 scoped_ptr<net::URLRequest> mock_url_request_;
108 // |test_job_interceptor_| is owned by |test_job_factory_|.
109 net::TestJobInterceptor* test_job_interceptor_;
110 net::URLRequestJobFactoryImpl test_job_factory_;
114 TEST_F(DataReductionProxyUsageStatsTest, IsDataReductionProxyUnreachable) {
115 net::ProxyServer fallback_proxy_server =
116 net::ProxyServer::FromURI("foo.com", net::ProxyServer::SCHEME_HTTP);
117 data_reduction_proxy::DataReductionProxyTypeInfo proxy_info;
119 bool fallback_proxy_server_is_data_reduction_proxy;
123 const TestCase test_cases[] = {
145 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
146 TestCase test_case = test_cases[i];
148 EXPECT_CALL(mock_params_, IsDataReductionProxy(testing::_, testing::_))
149 .WillRepeatedly(testing::Return(
150 test_case.fallback_proxy_server_is_data_reduction_proxy));
151 EXPECT_CALL(mock_params_,
152 WasDataReductionProxyUsed(mock_url_request_.get(), NULL))
153 .WillRepeatedly(testing::Return(test_case.was_proxy_used));
155 scoped_ptr<DataReductionProxyUsageStats> usage_stats(
156 new DataReductionProxyUsageStats(
157 &mock_params_, loop_proxy_));
158 usage_stats->set_unavailable_callback(
159 base::Bind(&DataReductionProxyUsageStatsTest::NotifyUnavailable,
160 base::Unretained(this)));
162 usage_stats->OnProxyFallback(fallback_proxy_server,
163 net::ERR_PROXY_CONNECTION_FAILED);
164 usage_stats->OnUrlRequestCompleted(mock_url_request_.get(), false);
165 base::MessageLoop::current()->RunUntilIdle();
167 EXPECT_EQ(test_case.is_unreachable, unavailable_);
171 TEST_F(DataReductionProxyUsageStatsTest, ProxyUnreachableThenReachable) {
172 net::ProxyServer fallback_proxy_server =
173 net::ProxyServer::FromURI("foo.com", net::ProxyServer::SCHEME_HTTP);
174 scoped_ptr<DataReductionProxyUsageStats> usage_stats(
175 new DataReductionProxyUsageStats(
176 &mock_params_, loop_proxy_));
177 usage_stats->set_unavailable_callback(
178 base::Bind(&DataReductionProxyUsageStatsTest::NotifyUnavailable,
179 base::Unretained(this)));
181 EXPECT_CALL(mock_params_, IsDataReductionProxy(testing::_, testing::_))
182 .WillOnce(testing::Return(true));
183 EXPECT_CALL(mock_params_,
184 WasDataReductionProxyUsed(mock_url_request_.get(), NULL))
185 .WillOnce(testing::Return(true));
188 usage_stats->OnProxyFallback(fallback_proxy_server,
189 net::ERR_PROXY_CONNECTION_FAILED);
190 base::MessageLoop::current()->RunUntilIdle();
191 EXPECT_TRUE(unavailable_);
194 usage_stats->OnUrlRequestCompleted(mock_url_request_.get(), false);
195 base::MessageLoop::current()->RunUntilIdle();
196 EXPECT_FALSE(unavailable_);
199 TEST_F(DataReductionProxyUsageStatsTest, ProxyReachableThenUnreachable) {
200 net::ProxyServer fallback_proxy_server =
201 net::ProxyServer::FromURI("foo.com", net::ProxyServer::SCHEME_HTTP);
202 scoped_ptr<DataReductionProxyUsageStats> usage_stats(
203 new DataReductionProxyUsageStats(
204 &mock_params_, loop_proxy_));
205 usage_stats->set_unavailable_callback(
206 base::Bind(&DataReductionProxyUsageStatsTest::NotifyUnavailable,
207 base::Unretained(this)));
208 EXPECT_CALL(mock_params_,
209 WasDataReductionProxyUsed(mock_url_request_.get(), NULL))
210 .WillOnce(testing::Return(true));
211 EXPECT_CALL(mock_params_, IsDataReductionProxy(testing::_, testing::_))
212 .WillRepeatedly(testing::Return(true));
215 usage_stats->OnUrlRequestCompleted(mock_url_request_.get(), false);
216 base::MessageLoop::current()->RunUntilIdle();
217 EXPECT_FALSE(unavailable_);
219 // Then proxy falls back indefinitely.
220 usage_stats->OnProxyFallback(fallback_proxy_server,
221 net::ERR_PROXY_CONNECTION_FAILED);
222 usage_stats->OnProxyFallback(fallback_proxy_server,
223 net::ERR_PROXY_CONNECTION_FAILED);
224 usage_stats->OnProxyFallback(fallback_proxy_server,
225 net::ERR_PROXY_CONNECTION_FAILED);
226 usage_stats->OnProxyFallback(fallback_proxy_server,
227 net::ERR_PROXY_CONNECTION_FAILED);
228 base::MessageLoop::current()->RunUntilIdle();
229 EXPECT_TRUE(unavailable_);
232 TEST_F(DataReductionProxyUsageStatsTest,
233 DetectAndRecordMissingViaHeaderResponseCode) {
234 const std::string kPrimaryHistogramName =
235 "DataReductionProxy.MissingViaHeader.ResponseCode.Primary";
236 const std::string kFallbackHistogramName =
237 "DataReductionProxy.MissingViaHeader.ResponseCode.Fallback";
242 int expected_primary_sample; // -1 indicates no expected sample.
243 int expected_fallback_sample; // -1 indicates no expected sample.
245 const TestCase test_cases[] = {
249 "Via: 1.1 Chrome-Compression-Proxy\n",
256 "Via: 1.1 Chrome-Compression-Proxy\n",
274 "HTTP/1.1 304 Not Modified\n",
280 "HTTP/1.1 304 Not Modified\n",
286 "HTTP/1.1 404 Not Found\n",
292 "HTTP/1.1 404 Not Found\n",
298 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
299 base::HistogramTester histogram_tester;
300 std::string raw_headers(test_cases[i].headers);
301 HeadersToRaw(&raw_headers);
302 scoped_refptr<net::HttpResponseHeaders> headers(
303 new net::HttpResponseHeaders(raw_headers));
305 DataReductionProxyUsageStats::DetectAndRecordMissingViaHeaderResponseCode(
306 test_cases[i].is_primary, headers.get());
308 if (test_cases[i].expected_primary_sample == -1) {
309 histogram_tester.ExpectTotalCount(kPrimaryHistogramName, 0);
311 histogram_tester.ExpectUniqueSample(
312 kPrimaryHistogramName, test_cases[i].expected_primary_sample, 1);
315 if (test_cases[i].expected_fallback_sample == -1) {
316 histogram_tester.ExpectTotalCount(kFallbackHistogramName, 0);
318 histogram_tester.ExpectUniqueSample(
319 kFallbackHistogramName, test_cases[i].expected_fallback_sample, 1);
324 TEST_F(DataReductionProxyUsageStatsTest, RecordMissingViaHeaderBytes) {
325 const std::string k4xxHistogramName =
326 "DataReductionProxy.MissingViaHeader.Bytes.4xx";
327 const std::string kOtherHistogramName =
328 "DataReductionProxy.MissingViaHeader.Bytes.Other";
329 const int64 kResponseContentLength = 100;
334 bool is_4xx_sample_expected;
335 bool is_other_sample_expected;
337 const TestCase test_cases[] = {
338 // Nothing should be recorded for requests that don't use the proxy.
341 "HTTP/1.1 404 Not Found\n",
351 // Nothing should be recorded for responses that have the via header.
354 "HTTP/1.1 404 Not Found\n"
355 "Via: 1.1 Chrome-Compression-Proxy\n",
362 "Via: 1.1 Chrome-Compression-Proxy\n",
366 // 4xx responses that used the proxy and don't have the via header should be
370 "HTTP/1.1 404 Not Found\n",
376 "HTTP/1.1 400 Bad Request\n",
382 "HTTP/1.1 499 Big Client Error Response Code\n",
386 // Non-4xx responses that used the proxy and don't have the via header
387 // should be recorded.
396 "HTTP/1.1 399 Big Redirection Response Code\n",
402 "HTTP/1.1 500 Internal Server Error\n",
408 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
409 base::HistogramTester histogram_tester;
410 scoped_ptr<DataReductionProxyUsageStats> usage_stats(
411 new DataReductionProxyUsageStats(&mock_params_, loop_proxy_));
413 std::string raw_headers(test_cases[i].headers);
414 HeadersToRaw(&raw_headers);
416 scoped_ptr<net::URLRequest> fake_request(
417 CreateURLRequestWithResponseHeaders(GURL("http://www.google.com/"),
419 fake_request->set_received_response_content_length(kResponseContentLength);
421 EXPECT_CALL(mock_params_,
422 WasDataReductionProxyUsed(fake_request.get(), NULL))
423 .WillRepeatedly(Return(test_cases[i].was_proxy_used));
425 usage_stats->RecordMissingViaHeaderBytes(fake_request.get());
427 if (test_cases[i].is_4xx_sample_expected) {
428 histogram_tester.ExpectUniqueSample(k4xxHistogramName,
429 kResponseContentLength, 1);
431 histogram_tester.ExpectTotalCount(k4xxHistogramName, 0);
434 if (test_cases[i].is_other_sample_expected) {
435 histogram_tester.ExpectUniqueSample(kOtherHistogramName,
436 kResponseContentLength, 1);
438 histogram_tester.ExpectTotalCount(kOtherHistogramName, 0);
443 } // namespace data_reduction_proxy