Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / components / data_reduction_proxy / core / browser / data_reduction_proxy_protocol_unittest.cc
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_protocol.h"
6
7 #include <utility>
8
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/core/browser/data_reduction_proxy_interceptor.h"
15 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_usage_stats.h"
16 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
17 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
18 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
19 #include "net/base/completion_callback.h"
20 #include "net/base/host_port_pair.h"
21 #include "net/base/load_flags.h"
22 #include "net/base/network_delegate.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/http/http_transaction_test_util.h"
25 #include "net/proxy/proxy_service.h"
26 #include "net/socket/socket_test_util.h"
27 #include "net/url_request/static_http_user_agent_settings.h"
28 #include "net/url_request/url_request.h"
29 #include "net/url_request/url_request_context.h"
30 #include "net/url_request/url_request_filter.h"
31 #include "net/url_request/url_request_http_job.h"
32 #include "net/url_request/url_request_intercepting_job_factory.h"
33 #include "net/url_request/url_request_interceptor.h"
34 #include "net/url_request/url_request_job_factory.h"
35 #include "net/url_request/url_request_job_factory_impl.h"
36 #include "net/url_request/url_request_test_util.h"
37 #include "testing/gtest/include/gtest/gtest.h"
38 #include "url/gurl.h"
39
40 using net::HttpResponseHeaders;
41 using net::HostPortPair;
42 using net::MockRead;
43 using net::MockWrite;
44 using net::ProxyRetryInfoMap;
45 using net::ProxyService;
46 using net::StaticSocketDataProvider;
47 using net::TestDelegate;
48 using net::URLRequest;
49 using net::TestURLRequestContext;
50
51
52 namespace data_reduction_proxy {
53
54 class SimpleURLRequestInterceptor : public net::URLRequestInterceptor {
55  public:
56   net::URLRequestJob* MaybeInterceptRequest(
57       net::URLRequest* request,
58       net::NetworkDelegate* network_delegate) const override {
59     return net::URLRequestHttpJob::Factory(request, network_delegate, "http");
60   }
61 };
62
63 // Constructs a |TestURLRequestContext| that uses a |MockSocketFactory| to
64 // simulate requests and responses.
65 class DataReductionProxyProtocolTest : public testing::Test {
66  public:
67   DataReductionProxyProtocolTest() : http_user_agent_settings_("", "") {
68     proxy_params_.reset(
69         new TestDataReductionProxyParams(
70             DataReductionProxyParams::kAllowed |
71             DataReductionProxyParams::kFallbackAllowed |
72             DataReductionProxyParams::kPromoAllowed,
73             TestDataReductionProxyParams::HAS_EVERYTHING &
74             ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
75             ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
76     simple_interceptor_.reset(new SimpleURLRequestInterceptor());
77     net::URLRequestFilter::GetInstance()->AddHostnameInterceptor(
78         "http", "www.google.com", simple_interceptor_.Pass());
79   }
80   ~DataReductionProxyProtocolTest() override {
81     // URLRequestJobs may post clean-up tasks on destruction.
82     net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(
83             "http", "www.google.com");
84     base::RunLoop().RunUntilIdle();
85   }
86
87   // Sets up the |TestURLRequestContext| with the provided |ProxyService| and
88   // |bypass_type| to store bypass reasons.
89   void ConfigureTestDependencies(ProxyService* proxy_service,
90                                  DataReductionProxyBypassType* bypass_type) {
91     // Create a context with delayed initialization.
92     context_.reset(new TestURLRequestContext(true));
93
94     proxy_service_.reset(proxy_service);
95     context_->set_client_socket_factory(&mock_socket_factory_);
96     context_->set_proxy_service(proxy_service_.get());
97     network_delegate_.reset(new net::TestNetworkDelegate());
98     context_->set_network_delegate(network_delegate_.get());
99     // This is needed to prevent the test context from adding language headers
100     // to requests.
101     context_->set_http_user_agent_settings(&http_user_agent_settings_);
102     usage_stats_.reset(new DataReductionProxyUsageStats(
103         proxy_params_.get(), base::MessageLoopProxy::current()));
104     DataReductionProxyInterceptor* interceptor =
105         new DataReductionProxyInterceptor(proxy_params_.get(),
106                                           usage_stats_.get());
107
108     scoped_ptr<net::URLRequestJobFactoryImpl> job_factory_impl(
109         new net::URLRequestJobFactoryImpl());
110     job_factory_.reset(
111         new net::URLRequestInterceptingJobFactory(
112             job_factory_impl.Pass(),
113             make_scoped_ptr(interceptor)));
114     context_->set_job_factory(job_factory_.get());
115     context_->Init();
116   }
117
118   // Simulates a request to a data reduction proxy that may result in bypassing
119   // the proxy and retrying the the request.
120   // Runs a test with the given request |method| that expects the first response
121   // from the server to be |first_response|. If |expected_retry|, the test
122   // will expect a retry of the request. A response body will be expected
123   // if |expect_response_body|.
124   void TestProxyFallback(const char* method,
125                          const char* first_response,
126                          bool has_origin,
127                          bool expected_retry,
128                          size_t expected_bad_proxy_count,
129                          bool expect_response_body) {
130     std::string payload1 =
131         (expected_retry ? "Bypass message" : "content");
132     MockRead data_reads[] = {
133       MockRead(first_response),
134       MockRead(payload1.c_str()),
135       MockRead(net::SYNCHRONOUS, net::OK),
136     };
137     std::string origin = has_origin ? "Origin: foo.com\r\n" : "";
138
139     std::string m(method);
140     std::string trailer =
141         (m == "HEAD" || m == "PUT" || m == "POST") ?
142             "Content-Length: 0\r\n" : "";
143
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%s"
148                            "User-Agent:\r\n"
149                            "Accept-Encoding: gzip, deflate\r\n\r\n",
150                            method, origin.c_str(), trailer.c_str());
151     MockWrite data_writes[] = {
152       MockWrite(request1.c_str()),
153     };
154     StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
155                                   data_writes, arraysize(data_writes));
156     mock_socket_factory_.AddSocketDataProvider(&data1);
157
158       std::string response2;
159       std::string request2;
160       if (expected_bad_proxy_count >= 2u ||
161           (m != "POST" && expected_retry && expected_bad_proxy_count == 0u)) {
162         response2 =
163             "HTTP/1.0 200 OK\r\n"
164             "Server: not-proxy\r\n\r\n";
165         request2 = base::StringPrintf(
166             "%s / HTTP/1.1\r\n"
167             "Host: www.google.com\r\n"
168             "Connection: keep-alive\r\n%s%s"
169             "User-Agent:\r\n"
170             "Accept-Encoding: gzip, deflate\r\n\r\n",
171             method, origin.c_str(), trailer.c_str());
172       } else if (expected_bad_proxy_count <= 1u) {
173         response2 =
174             "HTTP/1.0 200 OK\r\n"
175             "Server: not-proxy\r\n"
176             "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n";
177         request2 = base::StringPrintf(
178             "%s http://www.google.com/ HTTP/1.1\r\n"
179             "Host: www.google.com\r\n"
180             "Proxy-Connection: keep-alive\r\n%s%s"
181             "User-Agent:\r\n"
182             "Accept-Encoding: gzip, deflate\r\n\r\n",
183             method, origin.c_str(), trailer.c_str());
184       }
185       MockRead data_reads2[] = {
186           MockRead(response2.c_str()),
187           MockRead("content"),
188           MockRead(net::SYNCHRONOUS, net::OK),
189       };
190       MockWrite data_writes2[] = {
191           MockWrite(request2.c_str()),
192       };
193       StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
194                                      data_writes2, arraysize(data_writes2));
195       if (expected_retry) {
196         mock_socket_factory_.AddSocketDataProvider(&data2);
197     }
198
199     // Expect that we get "content" and not "Bypass message", and that there's
200     // a "not-proxy" "Server:" header in the final response.
201     ExecuteRequestExpectingContentAndHeader(
202         method,
203         (expect_response_body ? "content" : ""),
204         "server",
205         (expected_retry == 0 ? "proxy" : "not-proxy"),
206         has_origin,
207         expected_retry);
208   }
209
210   // Starts a request with the given |method| and checks that the response
211   // contains |content| and the the header |header|: |value|, if |header| is
212   // non-empty. Verifies that the request's URL chain is the right length
213   // depending on whether or not a retry was expected (|expected_retry|).
214   void ExecuteRequestExpectingContentAndHeader(const std::string& method,
215                                                const std::string& content,
216                                                const std::string& header,
217                                                const std::string& value,
218                                                bool has_origin,
219                                                bool expected_retry) {
220     TestDelegate d;
221     scoped_ptr<URLRequest> r(context_->CreateRequest(
222         GURL("http://www.google.com/"),
223         net::DEFAULT_PRIORITY,
224         &d,
225         NULL));
226     r->set_method(method);
227     r->SetLoadFlags(net::LOAD_NORMAL);
228     if (has_origin)
229       r->SetExtraRequestHeaderByName("Origin", "foo.com", true);
230
231     r->Start();
232     base::RunLoop().Run();
233
234     EXPECT_EQ(net::URLRequestStatus::SUCCESS, r->status().status());
235     EXPECT_EQ(net::OK, r->status().error());
236     if (expected_retry)
237       EXPECT_EQ(2, network_delegate_->headers_received_count());
238     else
239       EXPECT_EQ(1, network_delegate_->headers_received_count());
240
241     if (!header.empty()) {
242       // We also have a server header here that isn't set by the proxy.
243       EXPECT_TRUE(r->response_headers()->HasHeaderValue(header, value));
244     }
245
246     EXPECT_EQ(content, d.data_received());
247   }
248
249   // Returns the key to the |ProxyRetryInfoMap|.
250   std::string GetProxyKey(std::string proxy) {
251     GURL gurl(proxy);
252     std::string host_port = HostPortPair::FromURL(GURL(proxy)).ToString();
253     if (gurl.SchemeIs("https"))
254       return "https://" + host_port;
255     return host_port;
256   }
257
258   // Checks that |expected_num_bad_proxies| proxies are on the proxy retry list.
259   // If the list has one proxy, it should match |bad_proxy|. If it has two
260   // proxies, it should match |bad_proxy| and |bad_proxy2|. Checks also that
261   // the current delay associated with each bad proxy is |duration_seconds|.
262   void TestBadProxies(unsigned int expected_num_bad_proxies,
263                       int duration_seconds,
264                       const std::string& bad_proxy,
265                       const std::string& bad_proxy2) {
266     const ProxyRetryInfoMap& retry_info = proxy_service_->proxy_retry_info();
267     ASSERT_EQ(expected_num_bad_proxies, retry_info.size());
268
269     base::TimeDelta expected_min_duration;
270     base::TimeDelta expected_max_duration;
271     if (duration_seconds == 0) {
272       expected_min_duration = base::TimeDelta::FromMinutes(1);
273       expected_max_duration = base::TimeDelta::FromMinutes(5);
274     } else {
275       expected_min_duration = base::TimeDelta::FromSeconds(duration_seconds);
276       expected_max_duration = base::TimeDelta::FromSeconds(duration_seconds);
277     }
278
279     if (expected_num_bad_proxies >= 1u) {
280       ProxyRetryInfoMap::const_iterator i =
281           retry_info.find(GetProxyKey(bad_proxy));
282       ASSERT_TRUE(i != retry_info.end());
283       EXPECT_TRUE(expected_min_duration <= (*i).second.current_delay);
284       EXPECT_TRUE((*i).second.current_delay <= expected_max_duration);
285     }
286     if (expected_num_bad_proxies == 2u) {
287       ProxyRetryInfoMap::const_iterator i =
288           retry_info.find(GetProxyKey(bad_proxy2));
289       ASSERT_TRUE(i != retry_info.end());
290       EXPECT_TRUE(expected_min_duration <= (*i).second.current_delay);
291       EXPECT_TRUE((*i).second.current_delay <= expected_max_duration);
292     }
293   }
294
295  protected:
296   base::MessageLoopForIO loop_;
297
298   scoped_ptr<net::URLRequestInterceptor> simple_interceptor_;
299   net::MockClientSocketFactory mock_socket_factory_;
300   scoped_ptr<net::TestNetworkDelegate> network_delegate_;
301   scoped_ptr<ProxyService> proxy_service_;
302   scoped_ptr<TestDataReductionProxyParams> proxy_params_;
303   scoped_ptr<DataReductionProxyUsageStats> usage_stats_;
304   net::StaticHttpUserAgentSettings http_user_agent_settings_;
305
306   scoped_ptr<net::URLRequestInterceptingJobFactory> job_factory_;
307   scoped_ptr<TestURLRequestContext> context_;
308 };
309
310 // Tests that request are deemed idempotent or not according to the method used.
311 TEST_F(DataReductionProxyProtocolTest, TestIdempotency) {
312   net::TestURLRequestContext context;
313   const struct {
314     const char* method;
315     bool expected_result;
316   } tests[] = {
317       { "GET", true },
318       { "OPTIONS", true },
319       { "HEAD", true },
320       { "PUT", true },
321       { "DELETE", true },
322       { "TRACE", true },
323       { "POST", false },
324       { "CONNECT", false },
325   };
326   for (size_t i = 0; i < arraysize(tests); ++i) {
327     scoped_ptr<net::URLRequest> request(
328         context.CreateRequest(GURL("http://www.google.com/"),
329                               net::DEFAULT_PRIORITY,
330                               NULL,
331                               NULL));
332     request->set_method(tests[i].method);
333     EXPECT_EQ(tests[i].expected_result, IsRequestIdempotent(request.get()));
334   }
335 }
336
337 // After each test, the proxy retry info will contain zero, one, or two of the
338 // data reduction proxies depending on whether no bypass was indicated by the
339 // initial response, a single proxy bypass was indicated, or a double bypass
340 // was indicated. In both the single and double bypass cases, if the request
341 // was idempotent, it will be retried over a direct connection.
342 TEST_F(DataReductionProxyProtocolTest, BypassLogic) {
343   const struct {
344     const char* method;
345     const char* first_response;
346     bool has_origin;
347     bool expected_retry;
348     size_t expected_bad_proxy_count;
349     bool expect_response_body;
350     int expected_duration;
351     DataReductionProxyBypassType expected_bypass_type;
352   } tests[] = {
353     // Valid data reduction proxy response with no bypass message.
354     { "GET",
355       "HTTP/1.1 200 OK\r\n"
356       "Server: proxy\r\n"
357       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
358       false,
359       false,
360       0u,
361       true,
362       -1,
363       BYPASS_EVENT_TYPE_MAX,
364     },
365     // Valid data reduction proxy response with older, but still valid via
366     // header.
367     { "GET",
368       "HTTP/1.1 200 OK\r\n"
369       "Server: proxy\r\n"
370       "Via: 1.1 Chrome Compression Proxy\r\n\r\n",
371       false,
372       false,
373       0u,
374       true,
375       -1,
376       BYPASS_EVENT_TYPE_MAX
377     },
378     // Valid data reduction proxy response with chained via header,
379     // no bypass message.
380     { "GET",
381       "HTTP/1.1 200 OK\r\n"
382       "Server: proxy\r\n"
383       "Via: 1.1 Chrome-Compression-Proxy, 1.0 some-other-proxy\r\n\r\n",
384       false,
385       false,
386       0u,
387       true,
388       -1,
389       BYPASS_EVENT_TYPE_MAX
390     },
391     // Valid data reduction proxy response with a bypass message.
392     { "GET",
393       "HTTP/1.1 200 OK\r\n"
394       "Server: proxy\r\n"
395       "Chrome-Proxy: bypass=0\r\n"
396       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
397       false,
398       true,
399       1u,
400       true,
401       0,
402       BYPASS_EVENT_TYPE_MEDIUM
403     },
404     // Valid data reduction proxy response with a bypass message.
405     { "GET",
406       "HTTP/1.1 200 OK\r\n"
407       "Server: proxy\r\n"
408       "Chrome-Proxy: bypass=1\r\n"
409       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
410       false,
411       true,
412       1u,
413       true,
414       1,
415       BYPASS_EVENT_TYPE_SHORT
416     },
417     // Same as above with the OPTIONS method, which is idempotent.
418     { "OPTIONS",
419       "HTTP/1.1 200 OK\r\n"
420       "Server: proxy\r\n"
421       "Chrome-Proxy: bypass=0\r\n"
422       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
423       false,
424       true,
425       1u,
426       true,
427       0,
428       BYPASS_EVENT_TYPE_MEDIUM
429     },
430     // Same as above with the HEAD method, which is idempotent.
431     { "HEAD",
432       "HTTP/1.1 200 OK\r\n"
433       "Server: proxy\r\n"
434       "Chrome-Proxy: bypass=0\r\n"
435       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
436       false,
437       true,
438       1u,
439       false,
440       0,
441       BYPASS_EVENT_TYPE_MEDIUM
442     },
443     // Same as above with the PUT method, which is idempotent.
444     { "PUT",
445       "HTTP/1.1 200 OK\r\n"
446       "Server: proxy\r\n"
447       "Chrome-Proxy: bypass=0\r\n"
448       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
449       false,
450       true,
451       1u,
452       true,
453       0,
454       BYPASS_EVENT_TYPE_MEDIUM
455     },
456     // Same as above with the DELETE method, which is idempotent.
457     { "DELETE",
458       "HTTP/1.1 200 OK\r\n"
459       "Server: proxy\r\n"
460       "Chrome-Proxy: bypass=0\r\n"
461       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
462       false,
463       true,
464       1u,
465       true,
466       0,
467       BYPASS_EVENT_TYPE_MEDIUM
468     },
469     // Same as above with the TRACE method, which is idempotent.
470     { "TRACE",
471       "HTTP/1.1 200 OK\r\n"
472       "Server: proxy\r\n"
473       "Chrome-Proxy: bypass=0\r\n"
474       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
475       false,
476       true,
477       1u,
478       true,
479       0,
480       BYPASS_EVENT_TYPE_MEDIUM
481     },
482     // 500 responses should be bypassed.
483     { "GET",
484       "HTTP/1.1 500 Internal Server Error\r\n"
485       "Server: proxy\r\n"
486       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
487       false,
488       true,
489       1u,
490       true,
491       0,
492       BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR
493     },
494     // 502 responses should be bypassed.
495     { "GET",
496       "HTTP/1.1 502 Internal Server Error\r\n"
497       "Server: proxy\r\n"
498       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
499       false,
500       true,
501       1u,
502       true,
503       0,
504       BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY
505     },
506     // 503 responses should be bypassed.
507     { "GET",
508       "HTTP/1.1 503 Internal Server Error\r\n"
509       "Server: proxy\r\n"
510       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
511       false,
512       true,
513       1u,
514       true,
515       0,
516       BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE
517     },
518     // Invalid data reduction proxy 4xx response. Missing Via header.
519     { "GET",
520       "HTTP/1.1 404 Not Found\r\n"
521       "Server: proxy\r\n\r\n",
522       false,
523       true,
524       1u,
525       true,
526       1,
527       BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX
528     },
529     // Invalid data reduction proxy response. Missing Via header.
530     { "GET",
531       "HTTP/1.1 200 OK\r\n"
532       "Server: proxy\r\n\r\n",
533       false,
534       true,
535       1u,
536       true,
537       0,
538       BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
539     },
540     // Invalid data reduction proxy response. Wrong Via header.
541     { "GET",
542       "HTTP/1.1 200 OK\r\n"
543       "Server: proxy\r\n"
544       "Via: 1.0 some-other-proxy\r\n\r\n",
545       false,
546       true,
547       1u,
548       true,
549       0,
550       BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
551     },
552     // Valid data reduction proxy response. 304 missing Via header.
553     { "GET",
554       "HTTP/1.1 304 Not Modified\r\n"
555       "Server: proxy\r\n\r\n",
556       false,
557       false,
558       0u,
559       false,
560       0,
561       BYPASS_EVENT_TYPE_MAX
562     },
563     // Valid data reduction proxy response with a bypass message. It will
564     // not be retried because the request is non-idempotent.
565     { "POST",
566       "HTTP/1.1 200 OK\r\n"
567       "Server: proxy\r\n"
568       "Chrome-Proxy: bypass=0\r\n"
569       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
570       false,
571       false,
572       1u,
573       true,
574       0,
575       BYPASS_EVENT_TYPE_MEDIUM
576     },
577     // Valid data reduction proxy response with block message. Both proxies
578     // should be on the retry list when it completes.
579     { "GET",
580       "HTTP/1.1 200 OK\r\n"
581       "Server: proxy\r\n"
582       "Chrome-Proxy: block=1\r\n"
583       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
584       false,
585       true,
586       2u,
587       true,
588       1,
589       BYPASS_EVENT_TYPE_SHORT
590     },
591     // Valid data reduction proxy response with a block-once message. It will be
592     // retried, and there will be no proxies on the retry list since block-once
593     // only affects the current request.
594     { "GET",
595       "HTTP/1.1 200 OK\r\n"
596       "Server: proxy\r\n"
597       "Chrome-Proxy: block-once\r\n"
598       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
599       false,
600       true,
601       0u,
602       true,
603       0,
604       BYPASS_EVENT_TYPE_CURRENT
605     },
606     // Same as above with the OPTIONS method, which is idempotent.
607     { "OPTIONS",
608       "HTTP/1.1 200 OK\r\n"
609       "Server: proxy\r\n"
610       "Chrome-Proxy: block-once\r\n"
611       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
612       false,
613       true,
614       0u,
615       true,
616       0,
617       BYPASS_EVENT_TYPE_CURRENT
618     },
619     // Same as above with the HEAD method, which is idempotent.
620     { "HEAD",
621       "HTTP/1.1 200 OK\r\n"
622       "Server: proxy\r\n"
623       "Chrome-Proxy: block-once\r\n"
624       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
625       false,
626       true,
627       0u,
628       false,
629       0,
630       BYPASS_EVENT_TYPE_CURRENT
631     },
632     // Same as above with the PUT method, which is idempotent.
633     { "PUT",
634       "HTTP/1.1 200 OK\r\n"
635       "Server: proxy\r\n"
636       "Chrome-Proxy: block-once\r\n"
637       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
638       false,
639       true,
640       0u,
641       true,
642       0,
643       BYPASS_EVENT_TYPE_CURRENT
644     },
645     // Same as above with the DELETE method, which is idempotent.
646     { "DELETE",
647       "HTTP/1.1 200 OK\r\n"
648       "Server: proxy\r\n"
649       "Chrome-Proxy: block-once\r\n"
650       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
651       false,
652       true,
653       0u,
654       true,
655       0,
656       BYPASS_EVENT_TYPE_CURRENT
657     },
658     // Same as above with the TRACE method, which is idempotent.
659     { "TRACE",
660       "HTTP/1.1 200 OK\r\n"
661       "Server: proxy\r\n"
662       "Chrome-Proxy: block-once\r\n"
663       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
664       false,
665       true,
666       0u,
667       true,
668       0,
669       BYPASS_EVENT_TYPE_CURRENT
670     },
671     // Valid data reduction proxy response with a block-once message. It will
672     // not be retried because the request is non-idempotent, and there will be
673     // no proxies on the retry list since block-once only affects the current
674     // request.
675     { "POST",
676       "HTTP/1.1 200 OK\r\n"
677       "Server: proxy\r\n"
678       "Chrome-Proxy: block-once\r\n"
679       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
680       false,
681       false,
682       0u,
683       true,
684       0,
685       BYPASS_EVENT_TYPE_CURRENT
686     },
687     // Valid data reduction proxy response with block and block-once messages.
688     // The block message will override the block-once message, so both proxies
689     // should be on the retry list when it completes.
690     { "GET",
691       "HTTP/1.1 200 OK\r\n"
692       "Server: proxy\r\n"
693       "Chrome-Proxy: block=1, block-once\r\n"
694       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
695       false,
696       true,
697       2u,
698       true,
699       1,
700       BYPASS_EVENT_TYPE_SHORT
701     },
702     // Valid data reduction proxy response with bypass and block-once messages.
703     // The bypass message will override the block-once message, so one proxy
704     // should be on the retry list when it completes.
705     { "GET",
706       "HTTP/1.1 200 OK\r\n"
707       "Server: proxy\r\n"
708       "Chrome-Proxy: bypass=1, block-once\r\n"
709       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
710       false,
711       true,
712       1u,
713       true,
714       1,
715       BYPASS_EVENT_TYPE_SHORT
716     },
717   };
718   std::string primary = proxy_params_->DefaultOrigin();
719   std::string fallback = proxy_params_->DefaultFallbackOrigin();
720   for (size_t i = 0; i < arraysize(tests); ++i) {
721     DataReductionProxyBypassType bypass_type;
722     ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult(
723         "PROXY " +
724         HostPortPair::FromURL(GURL(primary)).ToString() + "; PROXY " +
725         HostPortPair::FromURL(GURL(fallback)).ToString() + "; DIRECT"),
726         &bypass_type);
727     TestProxyFallback(tests[i].method,
728                       tests[i].first_response,
729                       tests[i].has_origin,
730                       tests[i].expected_retry,
731                       tests[i].expected_bad_proxy_count,
732                       tests[i].expect_response_body);
733     EXPECT_EQ(tests[i].expected_bypass_type, usage_stats_->GetBypassType());
734     // We should also observe the bad proxy in the retry list.
735     TestBadProxies(tests[i].expected_bad_proxy_count,
736                    tests[i].expected_duration,
737                    primary, fallback);
738   }
739 }
740
741 TEST_F(DataReductionProxyProtocolTest,
742        ProxyBypassIgnoredOnDirectConnection) {
743   // Verify that a Chrome-Proxy header is ignored when returned from a directly
744   // connected origin server.
745   ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult("DIRECT"),
746                             NULL);
747
748   MockRead data_reads[] = {
749     MockRead("HTTP/1.1 200 OK\r\n"
750              "Chrome-Proxy: bypass=0\r\n\r\n"),
751     MockRead("Bypass message"),
752     MockRead(net::SYNCHRONOUS, net::OK),
753   };
754   MockWrite data_writes[] = {
755     MockWrite("GET / HTTP/1.1\r\n"
756               "Host: www.google.com\r\n"
757               "Connection: keep-alive\r\n"
758               "User-Agent:\r\n"
759               "Accept-Encoding: gzip, deflate\r\n\r\n"),
760   };
761   StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
762                                  data_writes, arraysize(data_writes));
763   mock_socket_factory_.AddSocketDataProvider(&data1);
764
765   TestDelegate d;
766   scoped_ptr<URLRequest> r(context_->CreateRequest(
767       GURL("http://www.google.com/"),
768       net::DEFAULT_PRIORITY,
769       &d,
770       NULL));
771   r->set_method("GET");
772   r->SetLoadFlags(net::LOAD_NORMAL);
773
774   r->Start();
775   base::RunLoop().Run();
776
777   EXPECT_EQ(net::URLRequestStatus::SUCCESS, r->status().status());
778   EXPECT_EQ(net::OK, r->status().error());
779
780   EXPECT_EQ("Bypass message", d.data_received());
781
782   // We should have no entries in our bad proxy list.
783   TestBadProxies(0, -1, "", "");
784 }
785
786 class BadEntropyProvider : public base::FieldTrial::EntropyProvider {
787  public:
788   ~BadEntropyProvider() override {}
789
790   double GetEntropyForTrial(const std::string& trial_name,
791                             uint32 randomization_seed) const override {
792     return 0.5;
793   }
794 };
795
796 TEST_F(DataReductionProxyProtocolTest, OnResolveProxyHandler) {
797   int load_flags = net::LOAD_NORMAL;
798   GURL url("http://www.google.com/");
799   net::ProxyConfig proxy_config_direct = net::ProxyConfig::CreateDirect();
800
801   TestDataReductionProxyParams test_params(
802             DataReductionProxyParams::kAllowed |
803             DataReductionProxyParams::kFallbackAllowed |
804             DataReductionProxyParams::kPromoAllowed,
805             TestDataReductionProxyParams::HAS_EVERYTHING &
806             ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
807             ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN);
808
809   // Data reduction proxy info
810   net::ProxyInfo data_reduction_proxy_info;
811   std::string data_reduction_proxy;
812   base::TrimString(test_params.DefaultOrigin(), "/", &data_reduction_proxy);
813   data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy);
814   EXPECT_FALSE(data_reduction_proxy_info.is_empty());
815
816   // Data reduction proxy config
817   net::ProxyConfig data_reduction_proxy_config;
818   data_reduction_proxy_config.proxy_rules().ParseFromString(
819       "http=" + data_reduction_proxy + ",direct://;");
820   data_reduction_proxy_config.set_id(1);
821
822   // Other proxy info
823   net::ProxyInfo other_proxy_info;
824   other_proxy_info.UseNamedProxy("proxy.com");
825   EXPECT_FALSE(other_proxy_info.is_empty());
826
827   // Direct
828    net::ProxyInfo direct_proxy_info;
829    direct_proxy_info.UseDirect();
830    EXPECT_TRUE(direct_proxy_info.is_direct());
831
832    // Empty retry info map
833    net::ProxyRetryInfoMap empty_proxy_retry_info;
834
835    // Retry info map with the data reduction proxy;
836    net::ProxyRetryInfoMap data_reduction_proxy_retry_info;
837    net::ProxyRetryInfo retry_info;
838    retry_info.current_delay = base::TimeDelta::FromSeconds(1000);
839    retry_info.bad_until = base::TimeTicks().Now() + retry_info.current_delay;
840    retry_info.try_while_bad = false;
841    data_reduction_proxy_retry_info[
842        data_reduction_proxy_info.proxy_server().ToURI()] = retry_info;
843
844    net::ProxyInfo result;
845
846    // The data reduction proxy is used. It should be used afterwards.
847    result.Use(data_reduction_proxy_info);
848    OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
849                          proxy_config_direct, empty_proxy_retry_info,
850                          &test_params, &result);
851    EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server());
852
853    // Another proxy is used. It should be used afterwards.
854    result.Use(other_proxy_info);
855    OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
856                          proxy_config_direct, empty_proxy_retry_info,
857                          &test_params, &result);
858    EXPECT_EQ(other_proxy_info.proxy_server(), result.proxy_server());
859
860    // A direct connection is used. The data reduction proxy should be used
861    // afterwards.
862    // Another proxy is used. It should be used afterwards.
863    result.Use(direct_proxy_info);
864    net::ProxyConfig::ID prev_id = result.config_id();
865    OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
866                          proxy_config_direct, empty_proxy_retry_info,
867                          &test_params, &result);
868    EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server());
869    // Only the proxy list should be updated, not he proxy info.
870    EXPECT_EQ(result.config_id(), prev_id);
871
872    // A direct connection is used, but the data reduction proxy is on the retry
873    // list. A direct connection should be used afterwards.
874    result.Use(direct_proxy_info);
875    prev_id = result.config_id();
876    OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
877                          proxy_config_direct, data_reduction_proxy_retry_info,
878                          &test_params, &result);
879    EXPECT_TRUE(result.proxy_server().is_direct());
880    EXPECT_EQ(result.config_id(), prev_id);
881
882
883   // Without DataCompressionProxyCriticalBypass Finch trial set, should never
884   // bypass.
885   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
886                         proxy_config_direct, empty_proxy_retry_info,
887                         &test_params, &data_reduction_proxy_info);
888   EXPECT_FALSE(data_reduction_proxy_info.is_direct());
889
890   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
891                         proxy_config_direct, empty_proxy_retry_info,
892                         &test_params, &other_proxy_info);
893   EXPECT_FALSE(other_proxy_info.is_direct());
894
895   load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
896
897   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
898                         proxy_config_direct, empty_proxy_retry_info,
899                         &test_params, &data_reduction_proxy_info);
900   EXPECT_FALSE(data_reduction_proxy_info.is_direct());
901
902   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
903                         proxy_config_direct, empty_proxy_retry_info,
904                         &test_params, &other_proxy_info);
905   EXPECT_FALSE(other_proxy_info.is_direct());
906
907   // With Finch trial set, should only bypass if LOAD flag is set and the
908   // effective proxy is the data compression proxy.
909   base::FieldTrialList field_trial_list(new BadEntropyProvider());
910   base::FieldTrialList::CreateFieldTrial("DataCompressionProxyCriticalBypass",
911                                          "Enabled");
912   EXPECT_TRUE(
913       DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial());
914
915   load_flags = net::LOAD_NORMAL;
916
917   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
918                         proxy_config_direct, empty_proxy_retry_info,
919                         &test_params, &data_reduction_proxy_info);
920   EXPECT_FALSE(data_reduction_proxy_info.is_direct());
921
922   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
923                         proxy_config_direct, empty_proxy_retry_info,
924                         &test_params, &other_proxy_info);
925   EXPECT_FALSE(other_proxy_info.is_direct());
926
927   load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
928
929   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
930                         proxy_config_direct, empty_proxy_retry_info,
931                         &test_params, &other_proxy_info);
932   EXPECT_FALSE(other_proxy_info.is_direct());
933
934   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
935                         proxy_config_direct, empty_proxy_retry_info,
936                         &test_params, &data_reduction_proxy_info);
937   EXPECT_TRUE(data_reduction_proxy_info.is_direct());
938 }
939
940 }  // namespace data_reduction_proxy