OLD | NEW |
| (Empty) |
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/core/browser/data_reduction_proxy_usag
e_stats.h" | |
6 | |
7 #include <set> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "base/prefs/testing_pref_service.h" | |
15 #include "base/test/histogram_tester.h" | |
16 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_conf
ig_test_utils.h" | |
17 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_conf
igurator.h" | |
18 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_d
ata.h" | |
19 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_netw
ork_delegate.h" | |
20 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_pref
s.h" | |
21 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test
_utils.h" | |
22 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_heade
rs_test_utils.h" | |
23 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_param
s_test_utils.h" | |
24 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_
names.h" | |
25 #include "net/base/host_port_pair.h" | |
26 #include "net/base/load_flags.h" | |
27 #include "net/base/net_errors.h" | |
28 #include "net/base/net_log.h" | |
29 #include "net/base/request_priority.h" | |
30 #include "net/dns/mock_host_resolver.h" | |
31 #include "net/http/http_response_headers.h" | |
32 #include "net/http/http_util.h" | |
33 #include "net/socket/socket_test_util.h" | |
34 #include "net/url_request/url_request.h" | |
35 #include "net/url_request/url_request_context_getter.h" | |
36 #include "net/url_request/url_request_context_storage.h" | |
37 #include "net/url_request/url_request_intercepting_job_factory.h" | |
38 #include "net/url_request/url_request_interceptor.h" | |
39 #include "net/url_request/url_request_job_factory_impl.h" | |
40 #include "net/url_request/url_request_status.h" | |
41 #include "net/url_request/url_request_test_job.h" | |
42 #include "net/url_request/url_request_test_util.h" | |
43 #include "testing/gmock/include/gmock/gmock.h" | |
44 #include "testing/gtest/include/gtest/gtest.h" | |
45 | |
46 using net::MockRead; | |
47 using net::MockWrite; | |
48 using testing::Return; | |
49 | |
50 namespace data_reduction_proxy { | |
51 | |
52 namespace { | |
53 | |
54 const std::string kBody = "hello"; | |
55 const std::string kNextBody = "hello again"; | |
56 const std::string kErrorBody = "bad"; | |
57 | |
58 } // namespace | |
59 | |
60 class DataReductionProxyUsageStatsTest : public testing::Test { | |
61 public: | |
62 DataReductionProxyUsageStatsTest() | |
63 : context_(true) { | |
64 context_.Init(); | |
65 | |
66 // The |test_job_factory_| takes ownership of the interceptor. | |
67 test_job_interceptor_ = new net::TestJobInterceptor(); | |
68 EXPECT_TRUE(test_job_factory_.SetProtocolHandler(url::kHttpScheme, | |
69 test_job_interceptor_)); | |
70 | |
71 context_.set_job_factory(&test_job_factory_); | |
72 | |
73 test_context_ = | |
74 DataReductionProxyTestContext::Builder() | |
75 .WithParamsFlags(DataReductionProxyParams::kAllowed | | |
76 DataReductionProxyParams::kFallbackAllowed | | |
77 DataReductionProxyParams::kPromoAllowed) | |
78 .WithParamsDefinitions( | |
79 TestDataReductionProxyParams::HAS_EVERYTHING & | |
80 ~TestDataReductionProxyParams::HAS_DEV_ORIGIN & | |
81 ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN) | |
82 .WithMockConfig() | |
83 .Build(); | |
84 mock_url_request_ = context_.CreateRequest(GURL(), net::IDLE, &delegate_); | |
85 } | |
86 | |
87 scoped_ptr<net::URLRequest> CreateURLRequestWithResponseHeaders( | |
88 const GURL& url, | |
89 const std::string& raw_response_headers) { | |
90 scoped_ptr<net::URLRequest> fake_request = context_.CreateRequest( | |
91 url, net::IDLE, &delegate_); | |
92 | |
93 // Create a test job that will fill in the given response headers for the | |
94 // |fake_request|. | |
95 scoped_refptr<net::URLRequestTestJob> test_job( | |
96 new net::URLRequestTestJob(fake_request.get(), | |
97 context_.network_delegate(), | |
98 raw_response_headers, std::string(), true)); | |
99 | |
100 // Configure the interceptor to use the test job to handle the next request. | |
101 test_job_interceptor_->set_main_intercept_job(test_job.get()); | |
102 fake_request->Start(); | |
103 test_context_->RunUntilIdle(); | |
104 | |
105 EXPECT_TRUE(fake_request->response_headers() != NULL); | |
106 return fake_request.Pass(); | |
107 } | |
108 | |
109 bool IsUnreachable() const { | |
110 return test_context_->settings()->IsDataReductionProxyUnreachable(); | |
111 } | |
112 | |
113 protected: | |
114 scoped_ptr<DataReductionProxyUsageStats> BuildUsageStats() { | |
115 return make_scoped_ptr( | |
116 new DataReductionProxyUsageStats( | |
117 test_context_->config(), | |
118 test_context_->unreachable_callback(), | |
119 test_context_->task_runner())).Pass(); | |
120 } | |
121 | |
122 net::URLRequest* url_request() { | |
123 return mock_url_request_.get(); | |
124 } | |
125 | |
126 MockDataReductionProxyConfig* config() const { | |
127 return test_context_->mock_config(); | |
128 } | |
129 | |
130 void RunUntilIdle() { | |
131 test_context_->RunUntilIdle(); | |
132 } | |
133 | |
134 private: | |
135 net::TestURLRequestContext context_; | |
136 net::TestDelegate delegate_; | |
137 scoped_ptr<net::URLRequest> mock_url_request_; | |
138 // |test_job_interceptor_| is owned by |test_job_factory_|. | |
139 net::TestJobInterceptor* test_job_interceptor_; | |
140 net::URLRequestJobFactoryImpl test_job_factory_; | |
141 scoped_ptr<DataReductionProxyTestContext> test_context_; | |
142 }; | |
143 | |
144 TEST_F(DataReductionProxyUsageStatsTest, IsDataReductionProxyUnreachable) { | |
145 net::ProxyServer fallback_proxy_server = | |
146 net::ProxyServer::FromURI("foo.com", net::ProxyServer::SCHEME_HTTP); | |
147 data_reduction_proxy::DataReductionProxyTypeInfo proxy_info; | |
148 struct TestCase { | |
149 bool fallback_proxy_server_is_data_reduction_proxy; | |
150 bool was_proxy_used; | |
151 bool is_unreachable; | |
152 }; | |
153 const TestCase test_cases[] = { | |
154 { | |
155 false, | |
156 false, | |
157 false | |
158 }, | |
159 { | |
160 false, | |
161 true, | |
162 false | |
163 }, | |
164 { | |
165 true, | |
166 true, | |
167 false | |
168 }, | |
169 { | |
170 true, | |
171 false, | |
172 true | |
173 } | |
174 }; | |
175 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
176 TestCase test_case = test_cases[i]; | |
177 | |
178 EXPECT_CALL(*config(), IsDataReductionProxy(testing::_, testing::_)) | |
179 .WillRepeatedly(testing::Return( | |
180 test_case.fallback_proxy_server_is_data_reduction_proxy)); | |
181 EXPECT_CALL(*config(), | |
182 WasDataReductionProxyUsed(url_request(), testing::_)) | |
183 .WillRepeatedly(testing::Return(test_case.was_proxy_used)); | |
184 | |
185 scoped_ptr<DataReductionProxyUsageStats> usage_stats = BuildUsageStats(); | |
186 | |
187 usage_stats->OnProxyFallback(fallback_proxy_server, | |
188 net::ERR_PROXY_CONNECTION_FAILED); | |
189 usage_stats->OnUrlRequestCompleted(url_request(), false); | |
190 RunUntilIdle(); | |
191 | |
192 EXPECT_EQ(test_case.is_unreachable, IsUnreachable()); | |
193 } | |
194 } | |
195 | |
196 TEST_F(DataReductionProxyUsageStatsTest, ProxyUnreachableThenReachable) { | |
197 net::ProxyServer fallback_proxy_server = | |
198 net::ProxyServer::FromURI("foo.com", net::ProxyServer::SCHEME_HTTP); | |
199 scoped_ptr<DataReductionProxyUsageStats> usage_stats = BuildUsageStats(); | |
200 EXPECT_CALL(*config(), IsDataReductionProxy(testing::_, testing::_)) | |
201 .WillOnce(testing::Return(true)); | |
202 EXPECT_CALL(*config(), | |
203 WasDataReductionProxyUsed(url_request(), testing::_)) | |
204 .WillOnce(testing::Return(true)); | |
205 | |
206 // proxy falls back | |
207 usage_stats->OnProxyFallback(fallback_proxy_server, | |
208 net::ERR_PROXY_CONNECTION_FAILED); | |
209 RunUntilIdle(); | |
210 EXPECT_TRUE(IsUnreachable()); | |
211 | |
212 // proxy succeeds | |
213 usage_stats->OnUrlRequestCompleted(url_request(), false); | |
214 RunUntilIdle(); | |
215 EXPECT_FALSE(IsUnreachable()); | |
216 } | |
217 | |
218 TEST_F(DataReductionProxyUsageStatsTest, ProxyReachableThenUnreachable) { | |
219 net::ProxyServer fallback_proxy_server = | |
220 net::ProxyServer::FromURI("foo.com", net::ProxyServer::SCHEME_HTTP); | |
221 scoped_ptr<DataReductionProxyUsageStats> usage_stats = BuildUsageStats(); | |
222 EXPECT_CALL(*config(), | |
223 WasDataReductionProxyUsed(url_request(), testing::_)) | |
224 .WillOnce(testing::Return(true)); | |
225 EXPECT_CALL(*config(), IsDataReductionProxy(testing::_, testing::_)) | |
226 .WillRepeatedly(testing::Return(true)); | |
227 | |
228 // Proxy succeeds. | |
229 usage_stats->OnUrlRequestCompleted(url_request(), false); | |
230 RunUntilIdle(); | |
231 EXPECT_FALSE(IsUnreachable()); | |
232 | |
233 // Then proxy falls back indefinitely. | |
234 usage_stats->OnProxyFallback(fallback_proxy_server, | |
235 net::ERR_PROXY_CONNECTION_FAILED); | |
236 usage_stats->OnProxyFallback(fallback_proxy_server, | |
237 net::ERR_PROXY_CONNECTION_FAILED); | |
238 usage_stats->OnProxyFallback(fallback_proxy_server, | |
239 net::ERR_PROXY_CONNECTION_FAILED); | |
240 usage_stats->OnProxyFallback(fallback_proxy_server, | |
241 net::ERR_PROXY_CONNECTION_FAILED); | |
242 RunUntilIdle(); | |
243 EXPECT_TRUE(IsUnreachable()); | |
244 } | |
245 | |
246 TEST_F(DataReductionProxyUsageStatsTest, | |
247 DetectAndRecordMissingViaHeaderResponseCode) { | |
248 const std::string kPrimaryHistogramName = | |
249 "DataReductionProxy.MissingViaHeader.ResponseCode.Primary"; | |
250 const std::string kFallbackHistogramName = | |
251 "DataReductionProxy.MissingViaHeader.ResponseCode.Fallback"; | |
252 | |
253 struct TestCase { | |
254 bool is_primary; | |
255 const char* headers; | |
256 int expected_primary_sample; // -1 indicates no expected sample. | |
257 int expected_fallback_sample; // -1 indicates no expected sample. | |
258 }; | |
259 const TestCase test_cases[] = { | |
260 { | |
261 true, | |
262 "HTTP/1.1 200 OK\n" | |
263 "Via: 1.1 Chrome-Compression-Proxy\n", | |
264 -1, | |
265 -1 | |
266 }, | |
267 { | |
268 false, | |
269 "HTTP/1.1 200 OK\n" | |
270 "Via: 1.1 Chrome-Compression-Proxy\n", | |
271 -1, | |
272 -1 | |
273 }, | |
274 { | |
275 true, | |
276 "HTTP/1.1 200 OK\n", | |
277 200, | |
278 -1 | |
279 }, | |
280 { | |
281 false, | |
282 "HTTP/1.1 200 OK\n", | |
283 -1, | |
284 200 | |
285 }, | |
286 { | |
287 true, | |
288 "HTTP/1.1 304 Not Modified\n", | |
289 304, | |
290 -1 | |
291 }, | |
292 { | |
293 false, | |
294 "HTTP/1.1 304 Not Modified\n", | |
295 -1, | |
296 304 | |
297 }, | |
298 { | |
299 true, | |
300 "HTTP/1.1 404 Not Found\n", | |
301 404, | |
302 -1 | |
303 }, | |
304 { | |
305 false, | |
306 "HTTP/1.1 404 Not Found\n", | |
307 -1, | |
308 404 | |
309 } | |
310 }; | |
311 | |
312 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
313 base::HistogramTester histogram_tester; | |
314 std::string raw_headers(test_cases[i].headers); | |
315 HeadersToRaw(&raw_headers); | |
316 scoped_refptr<net::HttpResponseHeaders> headers( | |
317 new net::HttpResponseHeaders(raw_headers)); | |
318 | |
319 DataReductionProxyUsageStats::DetectAndRecordMissingViaHeaderResponseCode( | |
320 test_cases[i].is_primary, headers.get()); | |
321 | |
322 if (test_cases[i].expected_primary_sample == -1) { | |
323 histogram_tester.ExpectTotalCount(kPrimaryHistogramName, 0); | |
324 } else { | |
325 histogram_tester.ExpectUniqueSample( | |
326 kPrimaryHistogramName, test_cases[i].expected_primary_sample, 1); | |
327 } | |
328 | |
329 if (test_cases[i].expected_fallback_sample == -1) { | |
330 histogram_tester.ExpectTotalCount(kFallbackHistogramName, 0); | |
331 } else { | |
332 histogram_tester.ExpectUniqueSample( | |
333 kFallbackHistogramName, test_cases[i].expected_fallback_sample, 1); | |
334 } | |
335 } | |
336 } | |
337 | |
338 TEST_F(DataReductionProxyUsageStatsTest, RecordMissingViaHeaderBytes) { | |
339 const std::string k4xxHistogramName = | |
340 "DataReductionProxy.MissingViaHeader.Bytes.4xx"; | |
341 const std::string kOtherHistogramName = | |
342 "DataReductionProxy.MissingViaHeader.Bytes.Other"; | |
343 const int64 kResponseContentLength = 100; | |
344 | |
345 struct TestCase { | |
346 bool was_proxy_used; | |
347 const char* headers; | |
348 bool is_4xx_sample_expected; | |
349 bool is_other_sample_expected; | |
350 }; | |
351 const TestCase test_cases[] = { | |
352 // Nothing should be recorded for requests that don't use the proxy. | |
353 { | |
354 false, | |
355 "HTTP/1.1 404 Not Found\n", | |
356 false, | |
357 false | |
358 }, | |
359 { | |
360 false, | |
361 "HTTP/1.1 200 OK\n", | |
362 false, | |
363 false | |
364 }, | |
365 // Nothing should be recorded for responses that have the via header. | |
366 { | |
367 true, | |
368 "HTTP/1.1 404 Not Found\n" | |
369 "Via: 1.1 Chrome-Compression-Proxy\n", | |
370 false, | |
371 false | |
372 }, | |
373 { | |
374 true, | |
375 "HTTP/1.1 200 OK\n" | |
376 "Via: 1.1 Chrome-Compression-Proxy\n", | |
377 false, | |
378 false | |
379 }, | |
380 // 4xx responses that used the proxy and don't have the via header should be | |
381 // recorded. | |
382 { | |
383 true, | |
384 "HTTP/1.1 404 Not Found\n", | |
385 true, | |
386 false | |
387 }, | |
388 { | |
389 true, | |
390 "HTTP/1.1 400 Bad Request\n", | |
391 true, | |
392 false | |
393 }, | |
394 { | |
395 true, | |
396 "HTTP/1.1 499 Big Client Error Response Code\n", | |
397 true, | |
398 false | |
399 }, | |
400 // Non-4xx responses that used the proxy and don't have the via header | |
401 // should be recorded. | |
402 { | |
403 true, | |
404 "HTTP/1.1 200 OK\n", | |
405 false, | |
406 true | |
407 }, | |
408 { | |
409 true, | |
410 "HTTP/1.1 399 Big Redirection Response Code\n", | |
411 false, | |
412 true | |
413 }, | |
414 { | |
415 true, | |
416 "HTTP/1.1 500 Internal Server Error\n", | |
417 false, | |
418 true | |
419 } | |
420 }; | |
421 | |
422 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
423 base::HistogramTester histogram_tester; | |
424 scoped_ptr<DataReductionProxyUsageStats> usage_stats = BuildUsageStats(); | |
425 | |
426 std::string raw_headers(test_cases[i].headers); | |
427 HeadersToRaw(&raw_headers); | |
428 | |
429 scoped_ptr<net::URLRequest> fake_request( | |
430 CreateURLRequestWithResponseHeaders(GURL("http://www.google.com/"), | |
431 raw_headers)); | |
432 fake_request->set_received_response_content_length(kResponseContentLength); | |
433 | |
434 EXPECT_CALL(*config(), | |
435 WasDataReductionProxyUsed(fake_request.get(), testing::_)) | |
436 .WillRepeatedly(Return(test_cases[i].was_proxy_used)); | |
437 | |
438 usage_stats->RecordMissingViaHeaderBytes(*fake_request); | |
439 | |
440 if (test_cases[i].is_4xx_sample_expected) { | |
441 histogram_tester.ExpectUniqueSample(k4xxHistogramName, | |
442 kResponseContentLength, 1); | |
443 } else { | |
444 histogram_tester.ExpectTotalCount(k4xxHistogramName, 0); | |
445 } | |
446 | |
447 if (test_cases[i].is_other_sample_expected) { | |
448 histogram_tester.ExpectUniqueSample(kOtherHistogramName, | |
449 kResponseContentLength, 1); | |
450 } else { | |
451 histogram_tester.ExpectTotalCount(kOtherHistogramName, 0); | |
452 } | |
453 } | |
454 } | |
455 | |
456 TEST_F(DataReductionProxyUsageStatsTest, RequestCompletionErrorCodes) { | |
457 const std::string kPrimaryHistogramName = | |
458 "DataReductionProxy.RequestCompletionErrorCodes.Primary"; | |
459 const std::string kFallbackHistogramName = | |
460 "DataReductionProxy.RequestCompletionErrorCodes.Fallback"; | |
461 const std::string kPrimaryMainFrameHistogramName = | |
462 "DataReductionProxy.RequestCompletionErrorCodes.MainFrame.Primary"; | |
463 const std::string kFallbackMainFrameHistogramName = | |
464 "DataReductionProxy.RequestCompletionErrorCodes.MainFrame.Fallback"; | |
465 | |
466 struct TestCase { | |
467 bool was_proxy_used; | |
468 bool is_load_bypass_proxy; | |
469 bool is_fallback; | |
470 bool is_main_frame; | |
471 net::Error net_error; | |
472 }; | |
473 | |
474 const TestCase test_cases[] = { | |
475 {false, true, false, true, net::OK}, | |
476 {false, true, false, false, net::ERR_TOO_MANY_REDIRECTS}, | |
477 {false, false, false, true, net::OK}, | |
478 {false, false, false, false, net::ERR_TOO_MANY_REDIRECTS}, | |
479 {true, false, false, true, net::OK}, | |
480 {true, false, false, true, net::ERR_TOO_MANY_REDIRECTS}, | |
481 {true, false, false, false, net::OK}, | |
482 {true, false, false, false, net::ERR_TOO_MANY_REDIRECTS}, | |
483 {true, false, true, true, net::OK}, | |
484 {true, false, true, true, net::ERR_TOO_MANY_REDIRECTS}, | |
485 {true, false, true, false, net::OK}, | |
486 {true, false, true, false, net::ERR_TOO_MANY_REDIRECTS} | |
487 }; | |
488 | |
489 for (size_t i = 0; i < arraysize(test_cases); ++i) { | |
490 base::HistogramTester histogram_tester; | |
491 scoped_ptr<DataReductionProxyUsageStats> usage_stats = BuildUsageStats(); | |
492 | |
493 std::string raw_headers("HTTP/1.1 200 OK\n" | |
494 "Via: 1.1 Chrome-Compression-Proxy\n"); | |
495 HeadersToRaw(&raw_headers); | |
496 scoped_ptr<net::URLRequest> fake_request( | |
497 CreateURLRequestWithResponseHeaders(GURL("http://www.google.com/"), | |
498 raw_headers)); | |
499 if (test_cases[i].is_load_bypass_proxy) { | |
500 fake_request->SetLoadFlags(fake_request->load_flags() | | |
501 net::LOAD_BYPASS_PROXY); | |
502 } | |
503 if (test_cases[i].is_main_frame) { | |
504 fake_request->SetLoadFlags(fake_request->load_flags() | | |
505 net::LOAD_MAIN_FRAME); | |
506 } | |
507 | |
508 int net_error_int = static_cast<int>(test_cases[i].net_error); | |
509 if (test_cases[i].net_error != net::OK) { | |
510 fake_request->CancelWithError(net_error_int); | |
511 } | |
512 | |
513 DataReductionProxyTypeInfo proxy_info; | |
514 proxy_info.is_fallback = test_cases[i].is_fallback; | |
515 EXPECT_CALL(*config(), WasDataReductionProxyUsed(fake_request.get(), | |
516 testing::NotNull())) | |
517 .WillRepeatedly(testing::DoAll(testing::SetArgPointee<1>(proxy_info), | |
518 Return(test_cases[i].was_proxy_used))); | |
519 | |
520 usage_stats->OnUrlRequestCompleted(fake_request.get(), false); | |
521 | |
522 if (test_cases[i].was_proxy_used && !test_cases[i].is_load_bypass_proxy && | |
523 !test_cases[i].is_fallback) { | |
524 histogram_tester.ExpectUniqueSample( | |
525 kPrimaryHistogramName, -net_error_int, 1); | |
526 } else { | |
527 histogram_tester.ExpectTotalCount(kPrimaryHistogramName, 0); | |
528 } | |
529 if (test_cases[i].was_proxy_used && !test_cases[i].is_load_bypass_proxy && | |
530 test_cases[i].is_fallback) { | |
531 histogram_tester.ExpectUniqueSample( | |
532 kFallbackHistogramName, -net_error_int, 1); | |
533 } else { | |
534 histogram_tester.ExpectTotalCount(kFallbackHistogramName, 0); | |
535 } | |
536 if (test_cases[i].was_proxy_used && !test_cases[i].is_load_bypass_proxy && | |
537 !test_cases[i].is_fallback && test_cases[i].is_main_frame) { | |
538 histogram_tester.ExpectUniqueSample( | |
539 kPrimaryMainFrameHistogramName, -net_error_int, 1); | |
540 } else { | |
541 histogram_tester.ExpectTotalCount(kPrimaryMainFrameHistogramName, 0); | |
542 } | |
543 if (test_cases[i].was_proxy_used && !test_cases[i].is_load_bypass_proxy && | |
544 test_cases[i].is_fallback && test_cases[i].is_main_frame) { | |
545 histogram_tester.ExpectUniqueSample( | |
546 kFallbackMainFrameHistogramName, -net_error_int, 1); | |
547 } else { | |
548 histogram_tester.ExpectTotalCount(kFallbackMainFrameHistogramName, 0); | |
549 } | |
550 } | |
551 } | |
552 | |
553 // End-to-end tests for the DataReductionProxy.BypassedBytes histograms. | |
554 class DataReductionProxyUsageStatsEndToEndTest : public testing::Test { | |
555 public: | |
556 DataReductionProxyUsageStatsEndToEndTest() | |
557 : context_(true), context_storage_(&context_) {} | |
558 | |
559 ~DataReductionProxyUsageStatsEndToEndTest() override { | |
560 drp_test_context_->io_data()->ShutdownOnUIThread(); | |
561 drp_test_context_->RunUntilIdle(); | |
562 } | |
563 | |
564 void SetUp() override { | |
565 // Only use the primary data reduction proxy in order to make it easier to | |
566 // test bypassed bytes due to proxy fallbacks. This way, a test just needs | |
567 // to cause one proxy fallback in order for the data reduction proxy to be | |
568 // fully bypassed. | |
569 drp_test_context_ = | |
570 DataReductionProxyTestContext::Builder() | |
571 .WithParamsFlags(DataReductionProxyParams::kAllowed) | |
572 .WithParamsDefinitions( | |
573 TestDataReductionProxyParams::HAS_EVERYTHING & | |
574 ~TestDataReductionProxyParams::HAS_DEV_ORIGIN & | |
575 ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN) | |
576 .WithURLRequestContext(&context_) | |
577 .WithMockClientSocketFactory(&mock_socket_factory_) | |
578 .Build(); | |
579 drp_test_context_->AttachToURLRequestContext(&context_storage_); | |
580 context_.set_client_socket_factory(&mock_socket_factory_); | |
581 } | |
582 | |
583 // Create and execute a fake request using the data reduction proxy stack. | |
584 // Passing in nullptr for |retry_response_headers| indicates that the request | |
585 // is not expected to be retried. | |
586 void CreateAndExecuteRequest(const GURL& url, | |
587 const char* initial_response_headers, | |
588 const char* initial_response_body, | |
589 const char* retry_response_headers, | |
590 const char* retry_response_body) { | |
591 // Support HTTPS URLs. | |
592 net::SSLSocketDataProvider ssl_socket_data_provider(net::ASYNC, net::OK); | |
593 if (url.SchemeIsSecure()) { | |
594 mock_socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data_provider); | |
595 } | |
596 | |
597 // Prepare for the initial response. | |
598 MockRead initial_data_reads[] = { | |
599 MockRead(initial_response_headers), | |
600 MockRead(initial_response_body), | |
601 MockRead(net::SYNCHRONOUS, net::OK), | |
602 }; | |
603 net::StaticSocketDataProvider initial_socket_data_provider( | |
604 initial_data_reads, arraysize(initial_data_reads), nullptr, 0); | |
605 mock_socket_factory_.AddSocketDataProvider(&initial_socket_data_provider); | |
606 | |
607 // Prepare for the response from retrying the request, if applicable. | |
608 // |retry_data_reads| and |retry_socket_data_provider| are out here so that | |
609 // they stay in scope for when the request is executed. | |
610 std::vector<MockRead> retry_data_reads; | |
611 scoped_ptr<net::StaticSocketDataProvider> retry_socket_data_provider; | |
612 if (retry_response_headers) { | |
613 retry_data_reads.push_back(MockRead(retry_response_headers)); | |
614 retry_data_reads.push_back(MockRead(retry_response_body)); | |
615 retry_data_reads.push_back(MockRead(net::SYNCHRONOUS, net::OK)); | |
616 | |
617 retry_socket_data_provider.reset(new net::StaticSocketDataProvider( | |
618 &retry_data_reads.front(), retry_data_reads.size(), nullptr, 0)); | |
619 mock_socket_factory_.AddSocketDataProvider( | |
620 retry_socket_data_provider.get()); | |
621 } | |
622 | |
623 scoped_ptr<net::URLRequest> request( | |
624 context_.CreateRequest(url, net::IDLE, &delegate_)); | |
625 request->set_method("GET"); | |
626 request->SetLoadFlags(net::LOAD_NORMAL); | |
627 request->Start(); | |
628 drp_test_context_->RunUntilIdle(); | |
629 } | |
630 | |
631 void set_proxy_service(net::ProxyService* proxy_service) { | |
632 context_.set_proxy_service(proxy_service); | |
633 } | |
634 | |
635 void set_host_resolver(net::HostResolver* host_resolver) { | |
636 context_.set_host_resolver(host_resolver); | |
637 } | |
638 | |
639 const DataReductionProxySettings* settings() const { | |
640 return drp_test_context_->settings(); | |
641 } | |
642 | |
643 TestDataReductionProxyConfig* config() const { | |
644 return drp_test_context_->config(); | |
645 } | |
646 | |
647 void ClearBadProxies() { | |
648 context_.proxy_service()->ClearBadProxiesCache(); | |
649 } | |
650 | |
651 void InitializeContext() { | |
652 context_.Init(); | |
653 drp_test_context_->EnableDataReductionProxyWithSecureProxyCheckSuccess(); | |
654 } | |
655 | |
656 void ExpectOtherBypassedBytesHistogramsEmpty( | |
657 const base::HistogramTester& histogram_tester, | |
658 const std::set<std::string>& excluded_histograms) const { | |
659 const std::string kHistograms[] = { | |
660 "DataReductionProxy.BypassedBytes.NotBypassed", | |
661 "DataReductionProxy.BypassedBytes.SSL", | |
662 "DataReductionProxy.BypassedBytes.LocalBypassRules", | |
663 "DataReductionProxy.BypassedBytes.ProxyOverridden", | |
664 "DataReductionProxy.BypassedBytes.Current", | |
665 "DataReductionProxy.BypassedBytes.ShortAll", | |
666 "DataReductionProxy.BypassedBytes.ShortTriggeringRequest", | |
667 "DataReductionProxy.BypassedBytes.ShortAudioVideo", | |
668 "DataReductionProxy.BypassedBytes.MediumAll", | |
669 "DataReductionProxy.BypassedBytes.MediumTriggeringRequest", | |
670 "DataReductionProxy.BypassedBytes.LongAll", | |
671 "DataReductionProxy.BypassedBytes.LongTriggeringRequest", | |
672 "DataReductionProxy.BypassedBytes.MissingViaHeader4xx", | |
673 "DataReductionProxy.BypassedBytes.MissingViaHeaderOther", | |
674 "DataReductionProxy.BypassedBytes.Malformed407", | |
675 "DataReductionProxy.BypassedBytes.Status500HttpInternalServerError", | |
676 "DataReductionProxy.BypassedBytes.Status502HttpBadGateway", | |
677 "DataReductionProxy.BypassedBytes.Status503HttpServiceUnavailable", | |
678 "DataReductionProxy.BypassedBytes.NetworkErrorOther", | |
679 }; | |
680 | |
681 for (const std::string& histogram : kHistograms) { | |
682 if (excluded_histograms.find(histogram) == | |
683 excluded_histograms.end()) { | |
684 histogram_tester.ExpectTotalCount(histogram, 0); | |
685 } | |
686 } | |
687 } | |
688 | |
689 void ExpectOtherBypassedBytesHistogramsEmpty( | |
690 const base::HistogramTester& histogram_tester, | |
691 const std::string& excluded_histogram) const { | |
692 std::set<std::string> excluded_histograms; | |
693 excluded_histograms.insert(excluded_histogram); | |
694 ExpectOtherBypassedBytesHistogramsEmpty(histogram_tester, | |
695 excluded_histograms); | |
696 } | |
697 | |
698 void ExpectOtherBypassedBytesHistogramsEmpty( | |
699 const base::HistogramTester& histogram_tester, | |
700 const std::string& first_excluded_histogram, | |
701 const std::string& second_excluded_histogram) const { | |
702 std::set<std::string> excluded_histograms; | |
703 excluded_histograms.insert(first_excluded_histogram); | |
704 excluded_histograms.insert(second_excluded_histogram); | |
705 ExpectOtherBypassedBytesHistogramsEmpty(histogram_tester, | |
706 excluded_histograms); | |
707 } | |
708 | |
709 private: | |
710 net::TestDelegate delegate_; | |
711 net::MockClientSocketFactory mock_socket_factory_; | |
712 net::TestURLRequestContext context_; | |
713 net::URLRequestContextStorage context_storage_; | |
714 scoped_ptr<DataReductionProxyTestContext> drp_test_context_; | |
715 }; | |
716 | |
717 TEST_F(DataReductionProxyUsageStatsEndToEndTest, BypassedBytesNoRetry) { | |
718 struct TestCase { | |
719 GURL url; | |
720 const char* histogram_name; | |
721 const char* initial_response_headers; | |
722 }; | |
723 const TestCase test_cases[] = { | |
724 { GURL("http://foo.com"), | |
725 "DataReductionProxy.BypassedBytes.NotBypassed", | |
726 "HTTP/1.1 200 OK\r\n" | |
727 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", | |
728 }, | |
729 { GURL("https://foo.com"), | |
730 "DataReductionProxy.BypassedBytes.SSL", | |
731 "HTTP/1.1 200 OK\r\n\r\n", | |
732 }, | |
733 { GURL("http://localhost"), | |
734 "DataReductionProxy.BypassedBytes.LocalBypassRules", | |
735 "HTTP/1.1 200 OK\r\n\r\n", | |
736 }, | |
737 }; | |
738 | |
739 InitializeContext(); | |
740 for (const TestCase& test_case : test_cases) { | |
741 ClearBadProxies(); | |
742 base::HistogramTester histogram_tester; | |
743 CreateAndExecuteRequest(test_case.url, test_case.initial_response_headers, | |
744 kBody.c_str(), nullptr, nullptr); | |
745 | |
746 histogram_tester.ExpectUniqueSample(test_case.histogram_name, kBody.size(), | |
747 1); | |
748 ExpectOtherBypassedBytesHistogramsEmpty(histogram_tester, | |
749 test_case.histogram_name); | |
750 } | |
751 } | |
752 | |
753 TEST_F(DataReductionProxyUsageStatsEndToEndTest, BypassedBytesProxyOverridden) { | |
754 scoped_ptr<net::ProxyService> proxy_service( | |
755 net::ProxyService::CreateFixed("http://test.com:80")); | |
756 set_proxy_service(proxy_service.get()); | |
757 InitializeContext(); | |
758 | |
759 base::HistogramTester histogram_tester; | |
760 CreateAndExecuteRequest(GURL("http://foo.com"), "HTTP/1.1 200 OK\r\n\r\n", | |
761 kBody.c_str(), nullptr, nullptr); | |
762 | |
763 histogram_tester.ExpectUniqueSample( | |
764 "DataReductionProxy.BypassedBytes.ProxyOverridden", kBody.size(), 1); | |
765 ExpectOtherBypassedBytesHistogramsEmpty( | |
766 histogram_tester, "DataReductionProxy.BypassedBytes.ProxyOverridden"); | |
767 } | |
768 | |
769 TEST_F(DataReductionProxyUsageStatsEndToEndTest, BypassedBytesCurrent) { | |
770 InitializeContext(); | |
771 base::HistogramTester histogram_tester; | |
772 CreateAndExecuteRequest(GURL("http://foo.com"), | |
773 "HTTP/1.1 502 Bad Gateway\r\n" | |
774 "Via: 1.1 Chrome-Compression-Proxy\r\n" | |
775 "Chrome-Proxy: block-once\r\n\r\n", | |
776 kErrorBody.c_str(), "HTTP/1.1 200 OK\r\n\r\n", | |
777 kBody.c_str()); | |
778 | |
779 histogram_tester.ExpectUniqueSample( | |
780 "DataReductionProxy.BypassedBytes.Current", kBody.size(), 1); | |
781 ExpectOtherBypassedBytesHistogramsEmpty( | |
782 histogram_tester, "DataReductionProxy.BypassedBytes.Current"); | |
783 } | |
784 | |
785 TEST_F(DataReductionProxyUsageStatsEndToEndTest, BypassedBytesShortAudioVideo) { | |
786 InitializeContext(); | |
787 base::HistogramTester histogram_tester; | |
788 CreateAndExecuteRequest(GURL("http://foo.com"), | |
789 "HTTP/1.1 502 Bad Gateway\r\n" | |
790 "Via: 1.1 Chrome-Compression-Proxy\r\n" | |
791 "Chrome-Proxy: block=1\r\n\r\n", | |
792 kErrorBody.c_str(), | |
793 "HTTP/1.1 200 OK\r\n" | |
794 "Content-Type: video/mp4\r\n\r\n", | |
795 kBody.c_str()); | |
796 | |
797 histogram_tester.ExpectUniqueSample( | |
798 "DataReductionProxy.BypassedBytes.ShortAudioVideo", kBody.size(), 1); | |
799 ExpectOtherBypassedBytesHistogramsEmpty( | |
800 histogram_tester, "DataReductionProxy.BypassedBytes.ShortAudioVideo"); | |
801 } | |
802 | |
803 TEST_F(DataReductionProxyUsageStatsEndToEndTest, BypassedBytesExplicitBypass) { | |
804 struct TestCase { | |
805 const char* triggering_histogram_name; | |
806 const char* all_histogram_name; | |
807 const char* initial_response_headers; | |
808 }; | |
809 const TestCase test_cases[] = { | |
810 { "DataReductionProxy.BypassedBytes.ShortTriggeringRequest", | |
811 "DataReductionProxy.BypassedBytes.ShortAll", | |
812 "HTTP/1.1 502 Bad Gateway\r\n" | |
813 "Via: 1.1 Chrome-Compression-Proxy\r\n" | |
814 "Chrome-Proxy: block=1\r\n\r\n", | |
815 }, | |
816 { "DataReductionProxy.BypassedBytes.MediumTriggeringRequest", | |
817 "DataReductionProxy.BypassedBytes.MediumAll", | |
818 "HTTP/1.1 502 Bad Gateway\r\n" | |
819 "Via: 1.1 Chrome-Compression-Proxy\r\n" | |
820 "Chrome-Proxy: block=0\r\n\r\n", | |
821 }, | |
822 { "DataReductionProxy.BypassedBytes.LongTriggeringRequest", | |
823 "DataReductionProxy.BypassedBytes.LongAll", | |
824 "HTTP/1.1 502 Bad Gateway\r\n" | |
825 "Via: 1.1 Chrome-Compression-Proxy\r\n" | |
826 "Chrome-Proxy: block=3600\r\n\r\n", | |
827 }, | |
828 }; | |
829 | |
830 InitializeContext(); | |
831 for (const TestCase& test_case : test_cases) { | |
832 ClearBadProxies(); | |
833 base::HistogramTester histogram_tester; | |
834 | |
835 CreateAndExecuteRequest( | |
836 GURL("http://foo.com"), test_case.initial_response_headers, | |
837 kErrorBody.c_str(), "HTTP/1.1 200 OK\r\n\r\n", kBody.c_str()); | |
838 // The first request caused the proxy to be marked as bad, so this second | |
839 // request should not come through the proxy. | |
840 CreateAndExecuteRequest(GURL("http://bar.com"), "HTTP/1.1 200 OK\r\n\r\n", | |
841 kNextBody.c_str(), nullptr, nullptr); | |
842 | |
843 histogram_tester.ExpectUniqueSample(test_case.triggering_histogram_name, | |
844 kBody.size(), 1); | |
845 histogram_tester.ExpectUniqueSample(test_case.all_histogram_name, | |
846 kNextBody.size(), 1); | |
847 ExpectOtherBypassedBytesHistogramsEmpty(histogram_tester, | |
848 test_case.triggering_histogram_name, | |
849 test_case.all_histogram_name); | |
850 } | |
851 } | |
852 | |
853 TEST_F(DataReductionProxyUsageStatsEndToEndTest, | |
854 BypassedBytesClientSideFallback) { | |
855 struct TestCase { | |
856 const char* histogram_name; | |
857 const char* initial_response_headers; | |
858 }; | |
859 const TestCase test_cases[] = { | |
860 { "DataReductionProxy.BypassedBytes.MissingViaHeader4xx", | |
861 "HTTP/1.1 414 Request-URI Too Long\r\n\r\n", | |
862 }, | |
863 { "DataReductionProxy.BypassedBytes.MissingViaHeaderOther", | |
864 "HTTP/1.1 200 OK\r\n\r\n", | |
865 }, | |
866 { "DataReductionProxy.BypassedBytes.Malformed407", | |
867 "HTTP/1.1 407 Proxy Authentication Required\r\n\r\n", | |
868 }, | |
869 { "DataReductionProxy.BypassedBytes.Status500HttpInternalServerError", | |
870 "HTTP/1.1 500 Internal Server Error\r\n\r\n", | |
871 }, | |
872 { "DataReductionProxy.BypassedBytes.Status502HttpBadGateway", | |
873 "HTTP/1.1 502 Bad Gateway\r\n\r\n", | |
874 }, | |
875 { "DataReductionProxy.BypassedBytes.Status503HttpServiceUnavailable", | |
876 "HTTP/1.1 503 Service Unavailable\r\n\r\n", | |
877 }, | |
878 }; | |
879 | |
880 InitializeContext(); | |
881 for (const TestCase& test_case : test_cases) { | |
882 ClearBadProxies(); | |
883 base::HistogramTester histogram_tester; | |
884 | |
885 CreateAndExecuteRequest( | |
886 GURL("http://foo.com"), test_case.initial_response_headers, | |
887 kErrorBody.c_str(), "HTTP/1.1 200 OK\r\n\r\n", kBody.c_str()); | |
888 // The first request caused the proxy to be marked as bad, so this second | |
889 // request should not come through the proxy. | |
890 CreateAndExecuteRequest(GURL("http://bar.com"), "HTTP/1.1 200 OK\r\n\r\n", | |
891 kNextBody.c_str(), nullptr, nullptr); | |
892 | |
893 histogram_tester.ExpectTotalCount(test_case.histogram_name, 2); | |
894 histogram_tester.ExpectBucketCount(test_case.histogram_name, kBody.size(), | |
895 1); | |
896 histogram_tester.ExpectBucketCount(test_case.histogram_name, | |
897 kNextBody.size(), 1); | |
898 ExpectOtherBypassedBytesHistogramsEmpty(histogram_tester, | |
899 test_case.histogram_name); | |
900 } | |
901 } | |
902 | |
903 TEST_F(DataReductionProxyUsageStatsEndToEndTest, BypassedBytesNetErrorOther) { | |
904 // Make the data reduction proxy host fail to resolve. | |
905 scoped_ptr<net::MockHostResolver> host_resolver(new net::MockHostResolver()); | |
906 host_resolver->rules()->AddSimulatedFailure( | |
907 config()->test_params()->origin().host_port_pair().host()); | |
908 set_host_resolver(host_resolver.get()); | |
909 InitializeContext(); | |
910 | |
911 base::HistogramTester histogram_tester; | |
912 CreateAndExecuteRequest(GURL("http://foo.com"), "HTTP/1.1 200 OK\r\n\r\n", | |
913 kBody.c_str(), nullptr, nullptr); | |
914 | |
915 histogram_tester.ExpectUniqueSample( | |
916 "DataReductionProxy.BypassedBytes.NetworkErrorOther", kBody.size(), 1); | |
917 ExpectOtherBypassedBytesHistogramsEmpty( | |
918 histogram_tester, "DataReductionProxy.BypassedBytes.NetworkErrorOther"); | |
919 histogram_tester.ExpectUniqueSample( | |
920 "DataReductionProxy.BypassOnNetworkErrorPrimary", | |
921 -net::ERR_PROXY_CONNECTION_FAILED, 1); | |
922 } | |
923 | |
924 } // namespace data_reduction_proxy | |
OLD | NEW |