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_protocol.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/metrics/field_trial.h"
12 #include "base/run_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "components/data_reduction_proxy/browser/data_reduction_proxy_params_test_utils.h"
15 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
16 #include "net/base/completion_callback.h"
17 #include "net/base/host_port_pair.h"
18 #include "net/base/load_flags.h"
19 #include "net/base/network_delegate.h"
20 #include "net/http/http_response_headers.h"
21 #include "net/http/http_transaction_test_util.h"
22 #include "net/proxy/proxy_service.h"
23 #include "net/socket/socket_test_util.h"
24 #include "net/url_request/static_http_user_agent_settings.h"
25 #include "net/url_request/url_request.h"
26 #include "net/url_request/url_request_context.h"
27 #include "net/url_request/url_request_test_util.h"
28 #include "testing/gtest/include/gtest/gtest.h"
30 using net::HttpResponseHeaders;
31 using net::HostPortPair;
34 using net::ProxyRetryInfoMap;
35 using net::ProxyService;
36 using net::StaticSocketDataProvider;
37 using net::TestDelegate;
38 using net::URLRequest;
39 using net::TestURLRequestContext;
43 // Transform "normal"-looking headers (\n-separated) to the appropriate
44 // input format for ParseRawHeaders (\0-separated).
45 void HeadersToRaw(std::string* headers) {
46 std::replace(headers->begin(), headers->end(), '\n', '\0');
47 if (!headers->empty())
54 namespace data_reduction_proxy {
56 // A test network delegate that exercises the bypass logic of the data
58 class TestDataReductionProxyNetworkDelegate : public net::NetworkDelegate {
60 TestDataReductionProxyNetworkDelegate(
61 TestDataReductionProxyParams* test_params,
62 DataReductionProxyBypassType* bypass_type)
63 : net::NetworkDelegate(),
64 test_data_reduction_proxy_params_(test_params),
65 bypass_type_(bypass_type) {
68 virtual int OnHeadersReceived(
70 const net::CompletionCallback& callback,
71 const HttpResponseHeaders* original_response_headers,
72 scoped_refptr<HttpResponseHeaders>* override_response_headers,
73 GURL* allowed_unsafe_redirect_url) OVERRIDE {
74 data_reduction_proxy::MaybeBypassProxyAndPrepareToRetry(
75 test_data_reduction_proxy_params_,
77 original_response_headers,
78 override_response_headers,
83 TestDataReductionProxyParams* test_data_reduction_proxy_params_;
84 DataReductionProxyBypassType* bypass_type_;
87 // Constructs a |TestURLRequestContext| that uses a |MockSocketFactory| to
88 // simulate requests and responses.
89 class DataReductionProxyProtocolTest : public testing::Test {
91 DataReductionProxyProtocolTest() : http_user_agent_settings_("", "") {
93 new TestDataReductionProxyParams(
94 DataReductionProxyParams::kAllowed |
95 DataReductionProxyParams::kFallbackAllowed |
96 DataReductionProxyParams::kPromoAllowed,
97 TestDataReductionProxyParams::HAS_EVERYTHING &
98 ~TestDataReductionProxyParams::HAS_DEV_ORIGIN));
101 // Sets up the |TestURLRequestContext| with the provided |ProxyService| and
102 // |bypass_type| to store bypass reasons.
103 void ConfigureTestDependencies(ProxyService* proxy_service,
104 DataReductionProxyBypassType* bypass_type) {
105 // Create a context with delayed initialization.
106 context_.reset(new TestURLRequestContext(true));
108 proxy_service_.reset(proxy_service);
109 network_delegate_.reset(new TestDataReductionProxyNetworkDelegate(
110 proxy_params_.get(), bypass_type));
112 context_->set_client_socket_factory(&mock_socket_factory_);
113 context_->set_proxy_service(proxy_service_.get());
114 context_->set_network_delegate(network_delegate_.get());
115 // This is needed to prevent the test context from adding language headers
117 context_->set_http_user_agent_settings(&http_user_agent_settings_);
122 // Simulates a request to a data reduction proxy that may result in bypassing
123 // the proxy and retrying the the request.
124 // Runs a test with the given request |method| that expects the first response
125 // from the server to be |first_response|. If |expected_retry|, the test
126 // will expect a retry of the request. A response body will be expected
127 // if |expect_response_body|.
128 void TestProxyFallback(const char* method,
129 const char* first_response,
131 bool expect_response_body) {
132 std::string payload1 =
133 (expected_retry ? "Bypass message" : "content");
134 MockRead data_reads[] = {
135 MockRead(first_response),
136 MockRead(payload1.c_str()),
137 MockRead(net::SYNCHRONOUS, net::OK),
139 std::string m(method);
140 std::string trailer =
141 (m == "HEAD" || m == "PUT" || m == "POST") ?
142 "Content-Length: 0\r\n" : "";
144 std::string request1 =
145 base::StringPrintf("%s http://www.google.com/ HTTP/1.1\r\n"
146 "Host: www.google.com\r\n"
147 "Proxy-Connection: keep-alive\r\n%s"
149 "Accept-Encoding: gzip,deflate\r\n\r\n",
150 method, trailer.c_str());
151 MockWrite data_writes[] = {
152 MockWrite(request1.c_str()),
154 StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
155 data_writes, arraysize(data_writes));
156 mock_socket_factory_.AddSocketDataProvider(&data1);
158 MockRead data_reads2[] = {
159 MockRead("HTTP/1.0 200 OK\r\n"
160 "Server: not-proxy\r\n\r\n"),
162 MockRead(net::SYNCHRONOUS, net::OK),
164 std::string request2 =
165 base::StringPrintf("%s / HTTP/1.1\r\n"
166 "Host: www.google.com\r\n"
167 "Connection: keep-alive\r\n%s"
169 "Accept-Encoding: gzip,deflate\r\n\r\n",
170 method, trailer.c_str());
171 MockWrite data_writes2[] = {
172 MockWrite(request2.c_str()),
174 StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
175 data_writes2, arraysize(data_writes2));
176 if (expected_retry) {
177 mock_socket_factory_.AddSocketDataProvider(&data2);
180 // Expect that we get "content" and not "Bypass message", and that there's
181 // a "not-proxy" "Server:" header in the final response.
182 ExecuteRequestExpectingContentAndHeader(
184 (expect_response_body ? "content" : ""),
186 (expected_retry == 0 ? "proxy" : "not-proxy"),
190 // Starts a request with the given |method| and checks that the response
191 // contains |content| and the the header |header|: |value|, if |header| is
192 // non-empty. Verifies that the request's URL chain is the right length
193 // depending on whether or not a retry was expected (|expected_retry|).
194 void ExecuteRequestExpectingContentAndHeader(const std::string& method,
195 const std::string& content,
196 const std::string& header,
197 const std::string& value,
198 bool expected_retry) {
200 URLRequest r(GURL("http://www.google.com/"),
201 net::DEFAULT_PRIORITY,
204 r.set_method(method);
205 r.SetLoadFlags(net::LOAD_NORMAL);
208 base::RunLoop().Run();
210 EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status());
211 EXPECT_EQ(net::OK, r.status().error());
213 EXPECT_EQ(2U, r.url_chain().size());
215 EXPECT_EQ(1U, r.url_chain().size());
217 if (!header.empty()) {
218 // We also have a server header here that isn't set by the proxy.
219 EXPECT_TRUE(r.response_headers()->HasHeaderValue(header, value));
222 EXPECT_EQ(content, d.data_received());
225 // Returns the key to the |ProxyRetryInfoMap|.
226 std::string GetProxyKey(std::string proxy) {
228 std::string host_port = HostPortPair::FromURL(GURL(proxy)).ToString();
229 if (gurl.SchemeIs("https"))
230 return "https://" + host_port;
234 // Checks that |expected_num_bad_proxies| proxies are on the proxy retry list.
235 // If the list has one proxy, it should match |bad_proxy|. If it has two
236 // proxies, it should match |bad_proxy| and |bad_proxy2|. Checks also that
237 // the current delay associated with each bad proxy is |duration_seconds|.
238 void TestBadProxies(unsigned int expected_num_bad_proxies,
239 int duration_seconds,
240 const std::string& bad_proxy,
241 const std::string& bad_proxy2) {
242 const ProxyRetryInfoMap& retry_info = proxy_service_->proxy_retry_info();
243 ASSERT_EQ(expected_num_bad_proxies, retry_info.size());
245 base::TimeDelta expected_min_duration;
246 base::TimeDelta expected_max_duration;
247 if (duration_seconds == 0) {
248 expected_min_duration = base::TimeDelta::FromMinutes(1);
249 expected_max_duration = base::TimeDelta::FromMinutes(5);
252 expected_min_duration = base::TimeDelta::FromSeconds(duration_seconds);
253 expected_max_duration = base::TimeDelta::FromSeconds(duration_seconds);
256 if (expected_num_bad_proxies >= 1u) {
257 ProxyRetryInfoMap::const_iterator i =
258 retry_info.find(GetProxyKey(bad_proxy));
259 ASSERT_TRUE(i != retry_info.end());
260 EXPECT_TRUE(expected_min_duration <= (*i).second.current_delay);
261 EXPECT_TRUE((*i).second.current_delay <= expected_max_duration);
263 if (expected_num_bad_proxies == 2u) {
264 ProxyRetryInfoMap::const_iterator i =
265 retry_info.find(GetProxyKey(bad_proxy2));
266 ASSERT_TRUE(i != retry_info.end());
267 EXPECT_TRUE(expected_min_duration <= (*i).second.current_delay);
268 EXPECT_TRUE((*i).second.current_delay <= expected_max_duration);
273 base::MessageLoopForIO loop_;
275 net::MockClientSocketFactory mock_socket_factory_;
276 scoped_ptr<ProxyService> proxy_service_;
277 scoped_ptr<TestDataReductionProxyParams> proxy_params_;
278 scoped_ptr<TestDataReductionProxyNetworkDelegate> network_delegate_;
279 net::StaticHttpUserAgentSettings http_user_agent_settings_;
281 scoped_ptr<TestURLRequestContext> context_;
284 // Tests that request are deemed idempotent or not according to the method used.
285 TEST_F(DataReductionProxyProtocolTest, TestIdempotency) {
286 net::TestURLRequestContext context;
289 bool expected_result;
298 { "CONNECT", false },
300 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
301 net::TestURLRequest request(GURL("http://www.google.com/"),
302 net::DEFAULT_PRIORITY,
305 request.set_method(tests[i].method);
306 EXPECT_EQ(tests[i].expected_result, IsRequestIdempotent(&request));
310 // Tests that the response is correctly overwritten as a redirect.
311 TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirect) {
312 net::TestURLRequestContext context;
315 const char* expected_headers;
317 { "HTTP/1.1 200 0K\n"
318 "Chrome-Proxy: block=1\n"
319 "Via: 1.1 Chrome-Compression-Proxy\n",
321 "HTTP/1.1 302 Found\n"
322 "Chrome-Proxy: block=1\n"
323 "Via: 1.1 Chrome-Compression-Proxy\n"
324 "Location: http://www.google.com/\n"
326 { "HTTP/1.1 200 0K\n"
327 "Chrome-Proxy: block-once\n"
328 "Via: 1.1 Chrome-Compression-Proxy\n",
330 "HTTP/1.1 302 Found\n"
331 "Chrome-Proxy: block-once\n"
332 "Via: 1.1 Chrome-Compression-Proxy\n"
333 "Location: http://www.google.com/\n"
335 { "HTTP/1.1 302 Found\n"
336 "Location: http://foo.com/\n",
338 "HTTP/1.1 302 Found\n"
339 "Location: http://www.google.com/\n"
343 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
344 std::string headers(tests[i].headers);
345 HeadersToRaw(&headers);
346 scoped_refptr<HttpResponseHeaders> original_response_headers(
347 new HttpResponseHeaders(headers));
348 scoped_refptr<HttpResponseHeaders> override_response_headers;
349 TestDelegate test_delegate;
350 net::TestURLRequest request(GURL("http://www.google.com/"),
351 net::DEFAULT_PRIORITY,
354 OverrideResponseAsRedirect(&request,
355 original_response_headers,
356 &override_response_headers);
357 int expected_flags = net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY;
358 EXPECT_EQ(expected_flags, request.load_flags());
359 std::string override_headers;
360 override_response_headers->GetNormalizedHeaders(&override_headers);
361 EXPECT_EQ(std::string(tests[i].expected_headers), override_headers);
365 // Tests that the response is correctly overwritten as a redirect with CORS
366 // headers when an Origin header is provided in the initial request.
367 TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirectCORS) {
368 net::TestURLRequestContext context;
371 const char* expected_headers;
373 { "HTTP/1.1 200 0K\n"
374 "Chrome-Proxy: block=1\n"
375 "Via: 1.1 Chrome-Compression-Proxy\n",
377 "HTTP/1.1 302 Found\n"
378 "Chrome-Proxy: block=1\n"
379 "Via: 1.1 Chrome-Compression-Proxy\n"
380 "Location: http://www.google.com/\n"
381 "Access-Control-Allow-Origin: http://www.else.com\n"
382 "Access-Control-Allow-Credentials: true\n"
386 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
387 std::string headers(tests[i].headers);
388 HeadersToRaw(&headers);
389 scoped_refptr<HttpResponseHeaders> original_response_headers(
390 new HttpResponseHeaders(headers));
391 scoped_refptr<HttpResponseHeaders> override_response_headers;
392 TestDelegate test_delegate;
393 scoped_ptr<net::URLRequest> request(
394 context.CreateRequest(GURL("http://www.google.com/"),
395 net::DEFAULT_PRIORITY,
398 request->SetExtraRequestHeaderByName("Origin", "http://www.else.com", true);
399 OverrideResponseAsRedirect(request.get(), original_response_headers.get(),
400 &override_response_headers);
401 int expected_flags = net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY;
402 EXPECT_EQ(expected_flags, request->load_flags());
403 std::string override_headers;
404 override_response_headers->GetNormalizedHeaders(&override_headers);
405 EXPECT_EQ(std::string(tests[i].expected_headers), override_headers);
410 // After each test, the proxy retry info will contain zero, one, or two of the
411 // data reduction proxies depending on whether no bypass was indicated by the
412 // initial response, a single proxy bypass was indicated, or a double bypass
413 // was indicated. In both the single and double bypass cases, if the request
414 // was idempotent, it will be retried over a direct connection.
415 TEST_F(DataReductionProxyProtocolTest, BypassLogic) {
418 const char* first_response;
420 size_t expected_bad_proxy_count;
421 bool expect_response_body;
422 int expected_duration;
423 DataReductionProxyBypassType expected_bypass_type;
425 // Valid data reduction proxy response with no bypass message.
427 "HTTP/1.1 200 OK\r\n"
429 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
434 BYPASS_EVENT_TYPE_MAX,
436 // Valid data reduction proxy response with older, but still valid via
439 "HTTP/1.1 200 OK\r\n"
441 "Via: 1.1 Chrome Compression Proxy\r\n\r\n",
446 BYPASS_EVENT_TYPE_MAX
448 // Valid data reduction proxy response with chained via header,
449 // no bypass message.
451 "HTTP/1.1 200 OK\r\n"
453 "Via: 1.1 Chrome-Compression-Proxy, 1.0 some-other-proxy\r\n\r\n",
458 BYPASS_EVENT_TYPE_MAX
460 // Valid data reduction proxy response with a bypass message.
462 "HTTP/1.1 200 OK\r\n"
464 "Chrome-Proxy: bypass=0\r\n"
465 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
470 BYPASS_EVENT_TYPE_MEDIUM
472 // Valid data reduction proxy response with a bypass message.
474 "HTTP/1.1 200 OK\r\n"
476 "Chrome-Proxy: bypass=1\r\n"
477 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
482 BYPASS_EVENT_TYPE_SHORT
484 // Same as above with the OPTIONS method, which is idempotent.
486 "HTTP/1.1 200 OK\r\n"
488 "Chrome-Proxy: bypass=0\r\n"
489 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
494 BYPASS_EVENT_TYPE_MEDIUM
496 // Same as above with the HEAD method, which is idempotent.
498 "HTTP/1.1 200 OK\r\n"
500 "Chrome-Proxy: bypass=0\r\n"
501 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
506 BYPASS_EVENT_TYPE_MEDIUM
508 // Same as above with the PUT method, which is idempotent.
510 "HTTP/1.1 200 OK\r\n"
512 "Chrome-Proxy: bypass=0\r\n"
513 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
518 BYPASS_EVENT_TYPE_MEDIUM
520 // Same as above with the DELETE method, which is idempotent.
522 "HTTP/1.1 200 OK\r\n"
524 "Chrome-Proxy: bypass=0\r\n"
525 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
530 BYPASS_EVENT_TYPE_MEDIUM
532 // Same as above with the TRACE method, which is idempotent.
534 "HTTP/1.1 200 OK\r\n"
536 "Chrome-Proxy: bypass=0\r\n"
537 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
542 BYPASS_EVENT_TYPE_MEDIUM
544 // 500 responses should be bypassed.
546 "HTTP/1.1 500 Internal Server Error\r\n"
548 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
553 BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR
555 // 502 responses should be bypassed.
557 "HTTP/1.1 502 Internal Server Error\r\n"
559 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
564 BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY
566 // 503 responses should be bypassed.
568 "HTTP/1.1 503 Internal Server Error\r\n"
570 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
575 BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE
577 // Invalid data reduction proxy response. Missing Via header.
579 "HTTP/1.1 200 OK\r\n"
580 "Server: proxy\r\n\r\n",
585 BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
587 // Invalid data reduction proxy response. Wrong Via header.
589 "HTTP/1.1 200 OK\r\n"
591 "Via: 1.0 some-other-proxy\r\n\r\n",
596 BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
598 // Valid data reduction proxy response. 304 missing Via header.
600 "HTTP/1.1 304 Not Modified\r\n"
601 "Server: proxy\r\n\r\n",
606 BYPASS_EVENT_TYPE_MAX
608 // Valid data reduction proxy response with a bypass message. It will
609 // not be retried because the request is non-idempotent.
611 "HTTP/1.1 200 OK\r\n"
613 "Chrome-Proxy: bypass=0\r\n"
614 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
619 BYPASS_EVENT_TYPE_MEDIUM
621 // Valid data reduction proxy response with block message. Both proxies
622 // should be on the retry list when it completes.
624 "HTTP/1.1 200 OK\r\n"
626 "Chrome-Proxy: block=1\r\n"
627 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
632 BYPASS_EVENT_TYPE_SHORT
634 // Valid data reduction proxy response with a block-once message. It will be
635 // retried, and there will be no proxies on the retry list since block-once
636 // only affects the current request.
638 "HTTP/1.1 200 OK\r\n"
640 "Chrome-Proxy: block-once\r\n"
641 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
646 BYPASS_EVENT_TYPE_CURRENT
648 // Same as above with the OPTIONS method, which is idempotent.
650 "HTTP/1.1 200 OK\r\n"
652 "Chrome-Proxy: block-once\r\n"
653 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
658 BYPASS_EVENT_TYPE_CURRENT
660 // Same as above with the HEAD method, which is idempotent.
662 "HTTP/1.1 200 OK\r\n"
664 "Chrome-Proxy: block-once\r\n"
665 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
670 BYPASS_EVENT_TYPE_CURRENT
672 // Same as above with the PUT method, which is idempotent.
674 "HTTP/1.1 200 OK\r\n"
676 "Chrome-Proxy: block-once\r\n"
677 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
682 BYPASS_EVENT_TYPE_CURRENT
684 // Same as above with the DELETE method, which is idempotent.
686 "HTTP/1.1 200 OK\r\n"
688 "Chrome-Proxy: block-once\r\n"
689 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
694 BYPASS_EVENT_TYPE_CURRENT
696 // Same as above with the TRACE method, which is idempotent.
698 "HTTP/1.1 200 OK\r\n"
700 "Chrome-Proxy: block-once\r\n"
701 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
706 BYPASS_EVENT_TYPE_CURRENT
708 // Valid data reduction proxy response with a block-once message. It will
709 // not be retried because the request is non-idempotent, and there will be
710 // no proxies on the retry list since block-once only affects the current
713 "HTTP/1.1 200 OK\r\n"
715 "Chrome-Proxy: block-once\r\n"
716 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
721 BYPASS_EVENT_TYPE_CURRENT
723 // Valid data reduction proxy response with block and block-once messages.
724 // The block message will override the block-once message, so both proxies
725 // should be on the retry list when it completes.
727 "HTTP/1.1 200 OK\r\n"
729 "Chrome-Proxy: block=1, block-once\r\n"
730 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
735 BYPASS_EVENT_TYPE_SHORT
737 // Valid data reduction proxy response with bypass and block-once messages.
738 // The bypass message will override the block-once message, so one proxy
739 // should be on the retry list when it completes.
741 "HTTP/1.1 200 OK\r\n"
743 "Chrome-Proxy: bypass=1, block-once\r\n"
744 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
749 BYPASS_EVENT_TYPE_SHORT
752 std::string primary = proxy_params_->DefaultOrigin();
753 std::string fallback = proxy_params_->DefaultFallbackOrigin();
754 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
755 DataReductionProxyBypassType bypass_type;
756 ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
758 HostPortPair::FromURL(GURL(primary)).ToString() + "; PROXY " +
759 HostPortPair::FromURL(GURL(fallback)).ToString() + "; DIRECT"),
761 TestProxyFallback(tests[i].method,
762 tests[i].first_response,
763 tests[i].expected_retry,
764 tests[i].expect_response_body);
766 EXPECT_EQ(tests[i].expected_bypass_type, bypass_type);
768 // We should also observe the bad proxy in the retry list.
769 TestBadProxies(tests[i].expected_bad_proxy_count,
770 tests[i].expected_duration,
775 TEST_F(DataReductionProxyProtocolTest,
776 ProxyBypassIgnoredOnDirectConnection) {
777 // Verify that a Chrome-Proxy header is ignored when returned from a directly
778 // connected origin server.
779 ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult("DIRECT"),
782 MockRead data_reads[] = {
783 MockRead("HTTP/1.1 200 OK\r\n"
784 "Chrome-Proxy: bypass=0\r\n\r\n"),
785 MockRead("Bypass message"),
786 MockRead(net::SYNCHRONOUS, net::OK),
788 MockWrite data_writes[] = {
789 MockWrite("GET / HTTP/1.1\r\n"
790 "Host: www.google.com\r\n"
791 "Connection: keep-alive\r\n"
793 "Accept-Encoding: gzip,deflate\r\n\r\n"),
795 StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
796 data_writes, arraysize(data_writes));
797 mock_socket_factory_.AddSocketDataProvider(&data1);
800 URLRequest r(GURL("http://www.google.com/"),
801 net::DEFAULT_PRIORITY,
805 r.SetLoadFlags(net::LOAD_NORMAL);
808 base::RunLoop().Run();
810 EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status());
811 EXPECT_EQ(net::OK, r.status().error());
813 EXPECT_EQ("Bypass message", d.data_received());
815 // We should have no entries in our bad proxy list.
816 TestBadProxies(0, -1, "", "");
819 class BadEntropyProvider : public base::FieldTrial::EntropyProvider {
821 virtual ~BadEntropyProvider() {}
823 virtual double GetEntropyForTrial(const std::string& trial_name,
824 uint32 randomization_seed) const OVERRIDE {
829 TEST_F(DataReductionProxyProtocolTest, OnResolveProxyHandler) {
830 int load_flags = net::LOAD_NORMAL;
831 GURL url("http://www.google.com/");
833 TestDataReductionProxyParams test_params(
834 DataReductionProxyParams::kAllowed |
835 DataReductionProxyParams::kFallbackAllowed |
836 DataReductionProxyParams::kPromoAllowed,
837 TestDataReductionProxyParams::HAS_EVERYTHING &
838 ~TestDataReductionProxyParams::HAS_DEV_ORIGIN);
840 // Data reduction proxy info
841 net::ProxyInfo data_reduction_proxy_info;
842 std::string data_reduction_proxy;
843 base::TrimString(test_params.DefaultOrigin(), "/", &data_reduction_proxy);
844 data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
845 EXPECT_FALSE(data_reduction_proxy_info.is_empty());
847 // Data reduction proxy config
848 net::ProxyConfig data_reduction_proxy_config;
849 data_reduction_proxy_config.proxy_rules().ParseFromString(
850 "http=" + data_reduction_proxy + ",direct://;");
851 data_reduction_proxy_config.set_id(1);
854 net::ProxyInfo other_proxy_info;
855 other_proxy_info.UseNamedProxy("proxy.com");
856 EXPECT_FALSE(other_proxy_info.is_empty());
859 net::ProxyInfo direct_proxy_info;
860 direct_proxy_info.UseDirect();
861 EXPECT_TRUE(direct_proxy_info.is_direct());
863 // Empty retry info map
864 net::ProxyRetryInfoMap empty_proxy_retry_info;
866 // Retry info map with the data reduction proxy;
867 net::ProxyRetryInfoMap data_reduction_proxy_retry_info;
868 net::ProxyRetryInfo retry_info;
869 retry_info.current_delay = base::TimeDelta::FromSeconds(1000);
870 retry_info.bad_until = base::TimeTicks().Now() + retry_info.current_delay;
871 retry_info.try_while_bad = false;
872 data_reduction_proxy_retry_info[
873 data_reduction_proxy_info.proxy_server().ToURI()] = retry_info;
875 net::ProxyInfo result;
877 // The data reduction proxy is used. It should be used afterwards.
878 result.Use(data_reduction_proxy_info);
879 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
880 empty_proxy_retry_info, &test_params, &result);
881 EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server());
883 // Another proxy is used. It should be used afterwards.
884 result.Use(other_proxy_info);
885 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
886 empty_proxy_retry_info, &test_params, &result);
887 EXPECT_EQ(other_proxy_info.proxy_server(), result.proxy_server());
889 // A direct connection is used. The data reduction proxy should be used
891 // Another proxy is used. It should be used afterwards.
892 result.Use(direct_proxy_info);
893 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
894 empty_proxy_retry_info, &test_params, &result);
895 EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server());
897 // A direct connection is used, but the data reduction proxy is on the retry
898 // list. A direct connection should be used afterwards.
899 result.Use(direct_proxy_info);
900 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
901 data_reduction_proxy_retry_info, &test_params,
903 EXPECT_TRUE(result.proxy_server().is_direct());
906 // Without DataCompressionProxyCriticalBypass Finch trial set, should never
908 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
909 empty_proxy_retry_info, &test_params,
910 &data_reduction_proxy_info);
911 EXPECT_FALSE(data_reduction_proxy_info.is_direct());
913 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
914 empty_proxy_retry_info, &test_params,
916 EXPECT_FALSE(other_proxy_info.is_direct());
918 load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
920 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
921 empty_proxy_retry_info, &test_params,
922 &data_reduction_proxy_info);
923 EXPECT_FALSE(data_reduction_proxy_info.is_direct());
925 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
926 empty_proxy_retry_info, &test_params,
928 EXPECT_FALSE(other_proxy_info.is_direct());
930 // With Finch trial set, should only bypass if LOAD flag is set and the
931 // effective proxy is the data compression proxy.
932 base::FieldTrialList field_trial_list(new BadEntropyProvider());
933 base::FieldTrialList::CreateFieldTrial("DataCompressionProxyRollout",
935 base::FieldTrialList::CreateFieldTrial("DataCompressionProxyCriticalBypass",
938 DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial());
940 load_flags = net::LOAD_NORMAL;
942 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
943 empty_proxy_retry_info, &test_params,
944 &data_reduction_proxy_info);
945 EXPECT_FALSE(data_reduction_proxy_info.is_direct());
947 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
948 empty_proxy_retry_info, &test_params,
950 EXPECT_FALSE(other_proxy_info.is_direct());
952 load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
954 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
955 empty_proxy_retry_info, &test_params,
957 EXPECT_FALSE(other_proxy_info.is_direct());
959 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
960 empty_proxy_retry_info, &test_params,
961 &data_reduction_proxy_info);
962 EXPECT_TRUE(data_reduction_proxy_info.is_direct());
965 } // namespace data_reduction_proxy