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 "components/data_reduction_proxy/common/data_reduction_proxy_headers_test_utils.h"
17 #include "net/base/completion_callback.h"
18 #include "net/base/host_port_pair.h"
19 #include "net/base/load_flags.h"
20 #include "net/base/network_delegate.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/http/http_transaction_test_util.h"
23 #include "net/proxy/proxy_service.h"
24 #include "net/socket/socket_test_util.h"
25 #include "net/url_request/static_http_user_agent_settings.h"
26 #include "net/url_request/url_request.h"
27 #include "net/url_request/url_request_context.h"
28 #include "net/url_request/url_request_test_util.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 using net::HttpResponseHeaders;
32 using net::HostPortPair;
35 using net::ProxyRetryInfoMap;
36 using net::ProxyService;
37 using net::StaticSocketDataProvider;
38 using net::TestDelegate;
39 using net::URLRequest;
40 using net::TestURLRequestContext;
43 namespace data_reduction_proxy {
45 // A test network delegate that exercises the bypass logic of the data
47 class TestDataReductionProxyNetworkDelegate : public net::NetworkDelegate {
49 TestDataReductionProxyNetworkDelegate(
50 TestDataReductionProxyParams* test_params,
51 DataReductionProxyBypassType* bypass_type)
52 : net::NetworkDelegate(),
53 test_data_reduction_proxy_params_(test_params),
54 bypass_type_(bypass_type) {
57 virtual int OnHeadersReceived(
59 const net::CompletionCallback& callback,
60 const HttpResponseHeaders* original_response_headers,
61 scoped_refptr<HttpResponseHeaders>* override_response_headers,
62 GURL* allowed_unsafe_redirect_url) OVERRIDE {
63 data_reduction_proxy::MaybeBypassProxyAndPrepareToRetry(
64 test_data_reduction_proxy_params_,
66 original_response_headers,
67 override_response_headers,
72 TestDataReductionProxyParams* test_data_reduction_proxy_params_;
73 DataReductionProxyBypassType* bypass_type_;
76 // Constructs a |TestURLRequestContext| that uses a |MockSocketFactory| to
77 // simulate requests and responses.
78 class DataReductionProxyProtocolTest : public testing::Test {
80 DataReductionProxyProtocolTest() : http_user_agent_settings_("", "") {
82 new TestDataReductionProxyParams(
83 DataReductionProxyParams::kAllowed |
84 DataReductionProxyParams::kFallbackAllowed |
85 DataReductionProxyParams::kPromoAllowed,
86 TestDataReductionProxyParams::HAS_EVERYTHING &
87 ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
88 ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
91 // Sets up the |TestURLRequestContext| with the provided |ProxyService| and
92 // |bypass_type| to store bypass reasons.
93 void ConfigureTestDependencies(ProxyService* proxy_service,
94 DataReductionProxyBypassType* bypass_type) {
95 // Create a context with delayed initialization.
96 context_.reset(new TestURLRequestContext(true));
98 proxy_service_.reset(proxy_service);
99 network_delegate_.reset(new TestDataReductionProxyNetworkDelegate(
100 proxy_params_.get(), bypass_type));
102 context_->set_client_socket_factory(&mock_socket_factory_);
103 context_->set_proxy_service(proxy_service_.get());
104 context_->set_network_delegate(network_delegate_.get());
105 // This is needed to prevent the test context from adding language headers
107 context_->set_http_user_agent_settings(&http_user_agent_settings_);
112 // Simulates a request to a data reduction proxy that may result in bypassing
113 // the proxy and retrying the the request.
114 // Runs a test with the given request |method| that expects the first response
115 // from the server to be |first_response|. If |expected_retry|, the test
116 // will expect a retry of the request. A response body will be expected
117 // if |expect_response_body|.
118 void TestProxyFallback(const char* method,
119 const char* first_response,
121 bool expect_response_body) {
122 std::string payload1 =
123 (expected_retry ? "Bypass message" : "content");
124 MockRead data_reads[] = {
125 MockRead(first_response),
126 MockRead(payload1.c_str()),
127 MockRead(net::SYNCHRONOUS, net::OK),
129 std::string m(method);
130 std::string trailer =
131 (m == "HEAD" || m == "PUT" || m == "POST") ?
132 "Content-Length: 0\r\n" : "";
134 std::string request1 =
135 base::StringPrintf("%s http://www.google.com/ HTTP/1.1\r\n"
136 "Host: www.google.com\r\n"
137 "Proxy-Connection: keep-alive\r\n%s"
139 "Accept-Encoding: gzip, deflate\r\n\r\n",
140 method, trailer.c_str());
141 MockWrite data_writes[] = {
142 MockWrite(request1.c_str()),
144 StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
145 data_writes, arraysize(data_writes));
146 mock_socket_factory_.AddSocketDataProvider(&data1);
148 MockRead data_reads2[] = {
149 MockRead("HTTP/1.0 200 OK\r\n"
150 "Server: not-proxy\r\n\r\n"),
152 MockRead(net::SYNCHRONOUS, net::OK),
154 std::string request2 =
155 base::StringPrintf("%s / HTTP/1.1\r\n"
156 "Host: www.google.com\r\n"
157 "Connection: keep-alive\r\n%s"
159 "Accept-Encoding: gzip, deflate\r\n\r\n",
160 method, trailer.c_str());
161 MockWrite data_writes2[] = {
162 MockWrite(request2.c_str()),
164 StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
165 data_writes2, arraysize(data_writes2));
166 if (expected_retry) {
167 mock_socket_factory_.AddSocketDataProvider(&data2);
170 // Expect that we get "content" and not "Bypass message", and that there's
171 // a "not-proxy" "Server:" header in the final response.
172 ExecuteRequestExpectingContentAndHeader(
174 (expect_response_body ? "content" : ""),
176 (expected_retry == 0 ? "proxy" : "not-proxy"),
180 // Starts a request with the given |method| and checks that the response
181 // contains |content| and the the header |header|: |value|, if |header| is
182 // non-empty. Verifies that the request's URL chain is the right length
183 // depending on whether or not a retry was expected (|expected_retry|).
184 void ExecuteRequestExpectingContentAndHeader(const std::string& method,
185 const std::string& content,
186 const std::string& header,
187 const std::string& value,
188 bool expected_retry) {
190 scoped_ptr<URLRequest> r(context_->CreateRequest(
191 GURL("http://www.google.com/"),
192 net::DEFAULT_PRIORITY,
195 r->set_method(method);
196 r->SetLoadFlags(net::LOAD_NORMAL);
199 base::RunLoop().Run();
201 EXPECT_EQ(net::URLRequestStatus::SUCCESS, r->status().status());
202 EXPECT_EQ(net::OK, r->status().error());
204 EXPECT_EQ(2U, r->url_chain().size());
206 EXPECT_EQ(1U, r->url_chain().size());
208 if (!header.empty()) {
209 // We also have a server header here that isn't set by the proxy.
210 EXPECT_TRUE(r->response_headers()->HasHeaderValue(header, value));
213 EXPECT_EQ(content, d.data_received());
216 // Returns the key to the |ProxyRetryInfoMap|.
217 std::string GetProxyKey(std::string proxy) {
219 std::string host_port = HostPortPair::FromURL(GURL(proxy)).ToString();
220 if (gurl.SchemeIs("https"))
221 return "https://" + host_port;
225 // Checks that |expected_num_bad_proxies| proxies are on the proxy retry list.
226 // If the list has one proxy, it should match |bad_proxy|. If it has two
227 // proxies, it should match |bad_proxy| and |bad_proxy2|. Checks also that
228 // the current delay associated with each bad proxy is |duration_seconds|.
229 void TestBadProxies(unsigned int expected_num_bad_proxies,
230 int duration_seconds,
231 const std::string& bad_proxy,
232 const std::string& bad_proxy2) {
233 const ProxyRetryInfoMap& retry_info = proxy_service_->proxy_retry_info();
234 ASSERT_EQ(expected_num_bad_proxies, retry_info.size());
236 base::TimeDelta expected_min_duration;
237 base::TimeDelta expected_max_duration;
238 if (duration_seconds == 0) {
239 expected_min_duration = base::TimeDelta::FromMinutes(1);
240 expected_max_duration = base::TimeDelta::FromMinutes(5);
242 expected_min_duration = base::TimeDelta::FromSeconds(duration_seconds);
243 expected_max_duration = base::TimeDelta::FromSeconds(duration_seconds);
246 if (expected_num_bad_proxies >= 1u) {
247 ProxyRetryInfoMap::const_iterator i =
248 retry_info.find(GetProxyKey(bad_proxy));
249 ASSERT_TRUE(i != retry_info.end());
250 EXPECT_TRUE(expected_min_duration <= (*i).second.current_delay);
251 EXPECT_TRUE((*i).second.current_delay <= expected_max_duration);
253 if (expected_num_bad_proxies == 2u) {
254 ProxyRetryInfoMap::const_iterator i =
255 retry_info.find(GetProxyKey(bad_proxy2));
256 ASSERT_TRUE(i != retry_info.end());
257 EXPECT_TRUE(expected_min_duration <= (*i).second.current_delay);
258 EXPECT_TRUE((*i).second.current_delay <= expected_max_duration);
263 base::MessageLoopForIO loop_;
265 net::MockClientSocketFactory mock_socket_factory_;
266 scoped_ptr<ProxyService> proxy_service_;
267 scoped_ptr<TestDataReductionProxyParams> proxy_params_;
268 scoped_ptr<TestDataReductionProxyNetworkDelegate> network_delegate_;
269 net::StaticHttpUserAgentSettings http_user_agent_settings_;
271 scoped_ptr<TestURLRequestContext> context_;
274 // Tests that request are deemed idempotent or not according to the method used.
275 TEST_F(DataReductionProxyProtocolTest, TestIdempotency) {
276 net::TestURLRequestContext context;
279 bool expected_result;
288 { "CONNECT", false },
290 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
291 scoped_ptr<net::URLRequest> request(
292 context.CreateRequest(GURL("http://www.google.com/"),
293 net::DEFAULT_PRIORITY,
296 request->set_method(tests[i].method);
297 EXPECT_EQ(tests[i].expected_result, IsRequestIdempotent(request.get()));
301 // Tests that the response is correctly overwritten as a redirect.
302 TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirect) {
303 net::TestURLRequestContext context;
306 const char* expected_headers;
308 { "HTTP/1.1 200 0K\n"
309 "Chrome-Proxy: block=1\n"
310 "Via: 1.1 Chrome-Compression-Proxy\n",
312 "HTTP/1.1 302 Found\n"
313 "Chrome-Proxy: block=1\n"
314 "Via: 1.1 Chrome-Compression-Proxy\n"
315 "Location: http://www.google.com/\n"
317 { "HTTP/1.1 200 0K\n"
318 "Chrome-Proxy: block-once\n"
319 "Via: 1.1 Chrome-Compression-Proxy\n",
321 "HTTP/1.1 302 Found\n"
322 "Chrome-Proxy: block-once\n"
323 "Via: 1.1 Chrome-Compression-Proxy\n"
324 "Location: http://www.google.com/\n"
326 { "HTTP/1.1 302 Found\n"
327 "Location: http://foo.com/\n",
329 "HTTP/1.1 302 Found\n"
330 "Location: http://www.google.com/\n"
334 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
335 std::string headers(tests[i].headers);
336 HeadersToRaw(&headers);
337 scoped_refptr<HttpResponseHeaders> original_response_headers(
338 new HttpResponseHeaders(headers));
339 scoped_refptr<HttpResponseHeaders> override_response_headers;
340 TestDelegate test_delegate;
341 scoped_ptr<net::URLRequest> request(
342 context.CreateRequest(GURL("http://www.google.com/"),
343 net::DEFAULT_PRIORITY,
346 OverrideResponseAsRedirect(request.get(), original_response_headers.get(),
347 &override_response_headers);
348 int expected_flags = net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY;
349 EXPECT_EQ(expected_flags, request->load_flags());
350 std::string override_headers;
351 override_response_headers->GetNormalizedHeaders(&override_headers);
352 EXPECT_EQ(std::string(tests[i].expected_headers), override_headers);
356 // Tests that the response is correctly overwritten as a redirect with CORS
357 // headers when an Origin header is provided in the initial request.
358 TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirectCORS) {
359 net::TestURLRequestContext context;
362 const char* expected_headers;
364 { "HTTP/1.1 200 0K\n"
365 "Chrome-Proxy: block=1\n"
366 "Via: 1.1 Chrome-Compression-Proxy\n",
368 "HTTP/1.1 302 Found\n"
369 "Chrome-Proxy: block=1\n"
370 "Via: 1.1 Chrome-Compression-Proxy\n"
371 "Location: http://www.google.com/\n"
372 "Access-Control-Allow-Origin: http://www.else.com\n"
373 "Access-Control-Allow-Credentials: true\n"
377 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
378 std::string headers(tests[i].headers);
379 HeadersToRaw(&headers);
380 scoped_refptr<HttpResponseHeaders> original_response_headers(
381 new HttpResponseHeaders(headers));
382 scoped_refptr<HttpResponseHeaders> override_response_headers;
383 TestDelegate test_delegate;
384 scoped_ptr<net::URLRequest> request(
385 context.CreateRequest(GURL("http://www.google.com/"),
386 net::DEFAULT_PRIORITY,
389 request->SetExtraRequestHeaderByName("Origin", "http://www.else.com", true);
390 OverrideResponseAsRedirect(request.get(), original_response_headers.get(),
391 &override_response_headers);
392 int expected_flags = net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY;
393 EXPECT_EQ(expected_flags, request->load_flags());
394 std::string override_headers;
395 override_response_headers->GetNormalizedHeaders(&override_headers);
396 EXPECT_EQ(std::string(tests[i].expected_headers), override_headers);
401 // After each test, the proxy retry info will contain zero, one, or two of the
402 // data reduction proxies depending on whether no bypass was indicated by the
403 // initial response, a single proxy bypass was indicated, or a double bypass
404 // was indicated. In both the single and double bypass cases, if the request
405 // was idempotent, it will be retried over a direct connection.
406 TEST_F(DataReductionProxyProtocolTest, BypassLogic) {
409 const char* first_response;
411 size_t expected_bad_proxy_count;
412 bool expect_response_body;
413 int expected_duration;
414 DataReductionProxyBypassType expected_bypass_type;
416 // Valid data reduction proxy response with no bypass message.
418 "HTTP/1.1 200 OK\r\n"
420 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
425 BYPASS_EVENT_TYPE_MAX,
427 // Valid data reduction proxy response with older, but still valid via
430 "HTTP/1.1 200 OK\r\n"
432 "Via: 1.1 Chrome Compression Proxy\r\n\r\n",
437 BYPASS_EVENT_TYPE_MAX
439 // Valid data reduction proxy response with chained via header,
440 // no bypass message.
442 "HTTP/1.1 200 OK\r\n"
444 "Via: 1.1 Chrome-Compression-Proxy, 1.0 some-other-proxy\r\n\r\n",
449 BYPASS_EVENT_TYPE_MAX
451 // Valid data reduction proxy response with a bypass message.
453 "HTTP/1.1 200 OK\r\n"
455 "Chrome-Proxy: bypass=0\r\n"
456 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
461 BYPASS_EVENT_TYPE_MEDIUM
463 // Valid data reduction proxy response with a bypass message.
465 "HTTP/1.1 200 OK\r\n"
467 "Chrome-Proxy: bypass=1\r\n"
468 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
473 BYPASS_EVENT_TYPE_SHORT
475 // Same as above with the OPTIONS method, which is idempotent.
477 "HTTP/1.1 200 OK\r\n"
479 "Chrome-Proxy: bypass=0\r\n"
480 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
485 BYPASS_EVENT_TYPE_MEDIUM
487 // Same as above with the HEAD method, which is idempotent.
489 "HTTP/1.1 200 OK\r\n"
491 "Chrome-Proxy: bypass=0\r\n"
492 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
497 BYPASS_EVENT_TYPE_MEDIUM
499 // Same as above with the PUT method, which is idempotent.
501 "HTTP/1.1 200 OK\r\n"
503 "Chrome-Proxy: bypass=0\r\n"
504 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
509 BYPASS_EVENT_TYPE_MEDIUM
511 // Same as above with the DELETE method, which is idempotent.
513 "HTTP/1.1 200 OK\r\n"
515 "Chrome-Proxy: bypass=0\r\n"
516 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
521 BYPASS_EVENT_TYPE_MEDIUM
523 // Same as above with the TRACE method, which is idempotent.
525 "HTTP/1.1 200 OK\r\n"
527 "Chrome-Proxy: bypass=0\r\n"
528 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
533 BYPASS_EVENT_TYPE_MEDIUM
535 // 500 responses should be bypassed.
537 "HTTP/1.1 500 Internal Server Error\r\n"
539 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
544 BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR
546 // 502 responses should be bypassed.
548 "HTTP/1.1 502 Internal Server Error\r\n"
550 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
555 BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY
557 // 503 responses should be bypassed.
559 "HTTP/1.1 503 Internal Server Error\r\n"
561 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
566 BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE
568 // Invalid data reduction proxy response. Missing Via header.
570 "HTTP/1.1 200 OK\r\n"
571 "Server: proxy\r\n\r\n",
576 BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
578 // Invalid data reduction proxy response. Wrong Via header.
580 "HTTP/1.1 200 OK\r\n"
582 "Via: 1.0 some-other-proxy\r\n\r\n",
587 BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
589 // Valid data reduction proxy response. 304 missing Via header.
591 "HTTP/1.1 304 Not Modified\r\n"
592 "Server: proxy\r\n\r\n",
597 BYPASS_EVENT_TYPE_MAX
599 // Valid data reduction proxy response with a bypass message. It will
600 // not be retried because the request is non-idempotent.
602 "HTTP/1.1 200 OK\r\n"
604 "Chrome-Proxy: bypass=0\r\n"
605 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
610 BYPASS_EVENT_TYPE_MEDIUM
612 // Valid data reduction proxy response with block message. Both proxies
613 // should be on the retry list when it completes.
615 "HTTP/1.1 200 OK\r\n"
617 "Chrome-Proxy: block=1\r\n"
618 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
623 BYPASS_EVENT_TYPE_SHORT
625 // Valid data reduction proxy response with a block-once message. It will be
626 // retried, and there will be no proxies on the retry list since block-once
627 // only affects the current request.
629 "HTTP/1.1 200 OK\r\n"
631 "Chrome-Proxy: block-once\r\n"
632 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
637 BYPASS_EVENT_TYPE_CURRENT
639 // Same as above with the OPTIONS method, which is idempotent.
641 "HTTP/1.1 200 OK\r\n"
643 "Chrome-Proxy: block-once\r\n"
644 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
649 BYPASS_EVENT_TYPE_CURRENT
651 // Same as above with the HEAD method, which is idempotent.
653 "HTTP/1.1 200 OK\r\n"
655 "Chrome-Proxy: block-once\r\n"
656 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
661 BYPASS_EVENT_TYPE_CURRENT
663 // Same as above with the PUT method, which is idempotent.
665 "HTTP/1.1 200 OK\r\n"
667 "Chrome-Proxy: block-once\r\n"
668 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
673 BYPASS_EVENT_TYPE_CURRENT
675 // Same as above with the DELETE method, which is idempotent.
677 "HTTP/1.1 200 OK\r\n"
679 "Chrome-Proxy: block-once\r\n"
680 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
685 BYPASS_EVENT_TYPE_CURRENT
687 // Same as above with the TRACE method, which is idempotent.
689 "HTTP/1.1 200 OK\r\n"
691 "Chrome-Proxy: block-once\r\n"
692 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
697 BYPASS_EVENT_TYPE_CURRENT
699 // Valid data reduction proxy response with a block-once message. It will
700 // not be retried because the request is non-idempotent, and there will be
701 // no proxies on the retry list since block-once only affects the current
704 "HTTP/1.1 200 OK\r\n"
706 "Chrome-Proxy: block-once\r\n"
707 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
712 BYPASS_EVENT_TYPE_CURRENT
714 // Valid data reduction proxy response with block and block-once messages.
715 // The block message will override the block-once message, so both proxies
716 // should be on the retry list when it completes.
718 "HTTP/1.1 200 OK\r\n"
720 "Chrome-Proxy: block=1, block-once\r\n"
721 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
726 BYPASS_EVENT_TYPE_SHORT
728 // Valid data reduction proxy response with bypass and block-once messages.
729 // The bypass message will override the block-once message, so one proxy
730 // should be on the retry list when it completes.
732 "HTTP/1.1 200 OK\r\n"
734 "Chrome-Proxy: bypass=1, block-once\r\n"
735 "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
740 BYPASS_EVENT_TYPE_SHORT
743 std::string primary = proxy_params_->DefaultOrigin();
744 std::string fallback = proxy_params_->DefaultFallbackOrigin();
745 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
746 DataReductionProxyBypassType bypass_type;
747 ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
749 HostPortPair::FromURL(GURL(primary)).ToString() + "; PROXY " +
750 HostPortPair::FromURL(GURL(fallback)).ToString() + "; DIRECT"),
752 TestProxyFallback(tests[i].method,
753 tests[i].first_response,
754 tests[i].expected_retry,
755 tests[i].expect_response_body);
757 EXPECT_EQ(tests[i].expected_bypass_type, bypass_type);
759 // We should also observe the bad proxy in the retry list.
760 TestBadProxies(tests[i].expected_bad_proxy_count,
761 tests[i].expected_duration,
766 TEST_F(DataReductionProxyProtocolTest,
767 ProxyBypassIgnoredOnDirectConnection) {
768 // Verify that a Chrome-Proxy header is ignored when returned from a directly
769 // connected origin server.
770 ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult("DIRECT"),
773 MockRead data_reads[] = {
774 MockRead("HTTP/1.1 200 OK\r\n"
775 "Chrome-Proxy: bypass=0\r\n\r\n"),
776 MockRead("Bypass message"),
777 MockRead(net::SYNCHRONOUS, net::OK),
779 MockWrite data_writes[] = {
780 MockWrite("GET / HTTP/1.1\r\n"
781 "Host: www.google.com\r\n"
782 "Connection: keep-alive\r\n"
784 "Accept-Encoding: gzip, deflate\r\n\r\n"),
786 StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
787 data_writes, arraysize(data_writes));
788 mock_socket_factory_.AddSocketDataProvider(&data1);
791 scoped_ptr<URLRequest> r(context_->CreateRequest(
792 GURL("http://www.google.com/"),
793 net::DEFAULT_PRIORITY,
796 r->set_method("GET");
797 r->SetLoadFlags(net::LOAD_NORMAL);
800 base::RunLoop().Run();
802 EXPECT_EQ(net::URLRequestStatus::SUCCESS, r->status().status());
803 EXPECT_EQ(net::OK, r->status().error());
805 EXPECT_EQ("Bypass message", d.data_received());
807 // We should have no entries in our bad proxy list.
808 TestBadProxies(0, -1, "", "");
811 class BadEntropyProvider : public base::FieldTrial::EntropyProvider {
813 virtual ~BadEntropyProvider() {}
815 virtual double GetEntropyForTrial(const std::string& trial_name,
816 uint32 randomization_seed) const OVERRIDE {
821 TEST_F(DataReductionProxyProtocolTest, OnResolveProxyHandler) {
822 int load_flags = net::LOAD_NORMAL;
823 GURL url("http://www.google.com/");
825 TestDataReductionProxyParams test_params(
826 DataReductionProxyParams::kAllowed |
827 DataReductionProxyParams::kFallbackAllowed |
828 DataReductionProxyParams::kPromoAllowed,
829 TestDataReductionProxyParams::HAS_EVERYTHING &
830 ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
831 ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN);
833 // Data reduction proxy info
834 net::ProxyInfo data_reduction_proxy_info;
835 std::string data_reduction_proxy;
836 base::TrimString(test_params.DefaultOrigin(), "/", &data_reduction_proxy);
837 data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
838 EXPECT_FALSE(data_reduction_proxy_info.is_empty());
840 // Data reduction proxy config
841 net::ProxyConfig data_reduction_proxy_config;
842 data_reduction_proxy_config.proxy_rules().ParseFromString(
843 "http=" + data_reduction_proxy + ",direct://;");
844 data_reduction_proxy_config.set_id(1);
847 net::ProxyInfo other_proxy_info;
848 other_proxy_info.UseNamedProxy("proxy.com");
849 EXPECT_FALSE(other_proxy_info.is_empty());
852 net::ProxyInfo direct_proxy_info;
853 direct_proxy_info.UseDirect();
854 EXPECT_TRUE(direct_proxy_info.is_direct());
856 // Empty retry info map
857 net::ProxyRetryInfoMap empty_proxy_retry_info;
859 // Retry info map with the data reduction proxy;
860 net::ProxyRetryInfoMap data_reduction_proxy_retry_info;
861 net::ProxyRetryInfo retry_info;
862 retry_info.current_delay = base::TimeDelta::FromSeconds(1000);
863 retry_info.bad_until = base::TimeTicks().Now() + retry_info.current_delay;
864 retry_info.try_while_bad = false;
865 data_reduction_proxy_retry_info[
866 data_reduction_proxy_info.proxy_server().ToURI()] = retry_info;
868 net::ProxyInfo result;
870 // The data reduction proxy is used. It should be used afterwards.
871 result.Use(data_reduction_proxy_info);
872 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
873 empty_proxy_retry_info, &test_params, &result);
874 EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server());
876 // Another proxy is used. It should be used afterwards.
877 result.Use(other_proxy_info);
878 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
879 empty_proxy_retry_info, &test_params, &result);
880 EXPECT_EQ(other_proxy_info.proxy_server(), result.proxy_server());
882 // A direct connection is used. The data reduction proxy should be used
884 // Another proxy is used. It should be used afterwards.
885 result.Use(direct_proxy_info);
886 net::ProxyConfig::ID prev_id = result.config_id();
887 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
888 empty_proxy_retry_info, &test_params, &result);
889 EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server());
890 // Only the proxy list should be updated, not he proxy info.
891 EXPECT_EQ(result.config_id(), prev_id);
893 // A direct connection is used, but the data reduction proxy is on the retry
894 // list. A direct connection should be used afterwards.
895 result.Use(direct_proxy_info);
896 prev_id = result.config_id();
897 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
898 data_reduction_proxy_retry_info, &test_params,
900 EXPECT_TRUE(result.proxy_server().is_direct());
901 EXPECT_EQ(result.config_id(), prev_id);
904 // Without DataCompressionProxyCriticalBypass Finch trial set, should never
906 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
907 empty_proxy_retry_info, &test_params,
908 &data_reduction_proxy_info);
909 EXPECT_FALSE(data_reduction_proxy_info.is_direct());
911 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
912 empty_proxy_retry_info, &test_params,
914 EXPECT_FALSE(other_proxy_info.is_direct());
916 load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
918 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
919 empty_proxy_retry_info, &test_params,
920 &data_reduction_proxy_info);
921 EXPECT_FALSE(data_reduction_proxy_info.is_direct());
923 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
924 empty_proxy_retry_info, &test_params,
926 EXPECT_FALSE(other_proxy_info.is_direct());
928 // With Finch trial set, should only bypass if LOAD flag is set and the
929 // effective proxy is the data compression proxy.
930 base::FieldTrialList field_trial_list(new BadEntropyProvider());
931 base::FieldTrialList::CreateFieldTrial("DataCompressionProxyCriticalBypass",
934 DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial());
936 load_flags = net::LOAD_NORMAL;
938 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
939 empty_proxy_retry_info, &test_params,
940 &data_reduction_proxy_info);
941 EXPECT_FALSE(data_reduction_proxy_info.is_direct());
943 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
944 empty_proxy_retry_info, &test_params,
946 EXPECT_FALSE(other_proxy_info.is_direct());
948 load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
950 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
951 empty_proxy_retry_info, &test_params,
953 EXPECT_FALSE(other_proxy_info.is_direct());
955 OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
956 empty_proxy_retry_info, &test_params,
957 &data_reduction_proxy_info);
958 EXPECT_TRUE(data_reduction_proxy_info.is_direct());
961 } // namespace data_reduction_proxy