Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / components / data_reduction_proxy / 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/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/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"
29
30 using net::HttpResponseHeaders;
31 using net::HostPortPair;
32 using net::MockRead;
33 using net::MockWrite;
34 using net::ProxyRetryInfoMap;
35 using net::ProxyService;
36 using net::StaticSocketDataProvider;
37 using net::TestDelegate;
38 using net::URLRequest;
39 using net::TestURLRequestContext;
40
41
42 namespace {
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())
48     *headers += '\0';
49 }
50
51 } // namespace
52
53
54 namespace data_reduction_proxy {
55
56 // A test network delegate that exercises the bypass logic of the data
57 // reduction proxy.
58 class TestDataReductionProxyNetworkDelegate : public net::NetworkDelegate {
59  public:
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) {
66   }
67
68   virtual int OnHeadersReceived(
69       URLRequest* request,
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_,
76         request,
77         original_response_headers,
78         override_response_headers,
79         bypass_type_);
80     return net::OK;
81   }
82
83   TestDataReductionProxyParams* test_data_reduction_proxy_params_;
84   DataReductionProxyBypassType* bypass_type_;
85 };
86
87 // Constructs a |TestURLRequestContext| that uses a |MockSocketFactory| to
88 // simulate requests and responses.
89 class DataReductionProxyProtocolTest : public testing::Test {
90  public:
91   DataReductionProxyProtocolTest() : http_user_agent_settings_("", "") {
92     proxy_params_.reset(
93         new TestDataReductionProxyParams(
94             DataReductionProxyParams::kAllowed |
95             DataReductionProxyParams::kFallbackAllowed |
96             DataReductionProxyParams::kPromoAllowed,
97             TestDataReductionProxyParams::HAS_EVERYTHING &
98             ~TestDataReductionProxyParams::HAS_DEV_ORIGIN));
99   }
100
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));
107
108     proxy_service_.reset(proxy_service);
109     network_delegate_.reset(new TestDataReductionProxyNetworkDelegate(
110         proxy_params_.get(), bypass_type));
111
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
116     // to requests.
117     context_->set_http_user_agent_settings(&http_user_agent_settings_);
118
119     context_->Init();
120   }
121
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,
130                          bool expected_retry,
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),
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"
148                            "User-Agent:\r\n"
149                            "Accept-Encoding: gzip,deflate\r\n\r\n",
150                            method, 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       MockRead data_reads2[] = {
159         MockRead("HTTP/1.0 200 OK\r\n"
160                  "Server: not-proxy\r\n\r\n"),
161         MockRead("content"),
162         MockRead(net::SYNCHRONOUS, net::OK),
163       };
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"
168                              "User-Agent:\r\n"
169                              "Accept-Encoding: gzip,deflate\r\n\r\n",
170                              method, trailer.c_str());
171       MockWrite data_writes2[] = {
172           MockWrite(request2.c_str()),
173       };
174       StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
175                                      data_writes2, arraysize(data_writes2));
176       if (expected_retry) {
177         mock_socket_factory_.AddSocketDataProvider(&data2);
178     }
179
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(
183         method,
184         (expect_response_body ? "content" : ""),
185         "server",
186         (expected_retry == 0 ? "proxy" : "not-proxy"),
187         expected_retry);
188   }
189
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) {
199     TestDelegate d;
200     URLRequest r(GURL("http://www.google.com/"),
201                  net::DEFAULT_PRIORITY,
202                  &d,
203                  context_.get());
204     r.set_method(method);
205     r.SetLoadFlags(net::LOAD_NORMAL);
206
207     r.Start();
208     base::RunLoop().Run();
209
210     EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status());
211     EXPECT_EQ(net::OK, r.status().error());
212     if (expected_retry)
213       EXPECT_EQ(2U, r.url_chain().size());
214     else
215       EXPECT_EQ(1U, r.url_chain().size());
216
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));
220     }
221
222     EXPECT_EQ(content, d.data_received());
223   }
224
225   // Returns the key to the |ProxyRetryInfoMap|.
226   std::string GetProxyKey(std::string proxy) {
227     GURL gurl(proxy);
228     std::string host_port = HostPortPair::FromURL(GURL(proxy)).ToString();
229     if (gurl.SchemeIs("https"))
230       return "https://" + host_port;
231     return host_port;
232   }
233
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());
244
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);
250     }
251     else {
252       expected_min_duration = base::TimeDelta::FromSeconds(duration_seconds);
253       expected_max_duration = base::TimeDelta::FromSeconds(duration_seconds);
254     }
255
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);
262     }
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);
269     }
270   }
271
272  protected:
273   base::MessageLoopForIO loop_;
274
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_;
280
281   scoped_ptr<TestURLRequestContext> context_;
282 };
283
284 // Tests that request are deemed idempotent or not according to the method used.
285 TEST_F(DataReductionProxyProtocolTest, TestIdempotency) {
286   net::TestURLRequestContext context;
287   const struct {
288     const char* method;
289     bool expected_result;
290   } tests[] = {
291       { "GET", true },
292       { "OPTIONS", true },
293       { "HEAD", true },
294       { "PUT", true },
295       { "DELETE", true },
296       { "TRACE", true },
297       { "POST", false },
298       { "CONNECT", false },
299   };
300   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
301     net::TestURLRequest request(GURL("http://www.google.com/"),
302                                 net::DEFAULT_PRIORITY,
303                                 NULL,
304                                 &context);
305     request.set_method(tests[i].method);
306     EXPECT_EQ(tests[i].expected_result, IsRequestIdempotent(&request));
307   }
308 }
309
310 // Tests that the response is correctly overwritten as a redirect.
311 TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirect) {
312   net::TestURLRequestContext context;
313   const struct {
314     const char* headers;
315     const char* expected_headers;
316   } tests[] = {
317       { "HTTP/1.1 200 0K\n"
318         "Chrome-Proxy: block=1\n"
319         "Via: 1.1 Chrome-Compression-Proxy\n",
320
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"
325       },
326       { "HTTP/1.1 200 0K\n"
327         "Chrome-Proxy: block-once\n"
328         "Via: 1.1 Chrome-Compression-Proxy\n",
329
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"
334       },
335       { "HTTP/1.1 302 Found\n"
336         "Location: http://foo.com/\n",
337
338         "HTTP/1.1 302 Found\n"
339         "Location: http://www.google.com/\n"
340       },
341   };
342
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,
352                            NULL,
353                            &context);
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);
362   }
363 }
364
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;
369   const struct {
370     const char* headers;
371     const char* expected_headers;
372   } tests[] = {
373       { "HTTP/1.1 200 0K\n"
374         "Chrome-Proxy: block=1\n"
375         "Via: 1.1 Chrome-Compression-Proxy\n",
376
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"
383       },
384   };
385
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,
396                               NULL,
397                               NULL));
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);
406   }
407 }
408
409
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) {
416   const struct {
417     const char* method;
418     const char* first_response;
419     bool expected_retry;
420     size_t expected_bad_proxy_count;
421     bool expect_response_body;
422     int expected_duration;
423     DataReductionProxyBypassType expected_bypass_type;
424   } tests[] = {
425     // Valid data reduction proxy response with no bypass message.
426     { "GET",
427       "HTTP/1.1 200 OK\r\n"
428       "Server: proxy\r\n"
429       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
430       false,
431       0u,
432       true,
433       -1,
434       BYPASS_EVENT_TYPE_MAX,
435     },
436     // Valid data reduction proxy response with older, but still valid via
437     // header.
438     { "GET",
439       "HTTP/1.1 200 OK\r\n"
440       "Server: proxy\r\n"
441       "Via: 1.1 Chrome Compression Proxy\r\n\r\n",
442       false,
443       0u,
444       true,
445       -1,
446       BYPASS_EVENT_TYPE_MAX
447     },
448     // Valid data reduction proxy response with chained via header,
449     // no bypass message.
450     { "GET",
451       "HTTP/1.1 200 OK\r\n"
452       "Server: proxy\r\n"
453       "Via: 1.1 Chrome-Compression-Proxy, 1.0 some-other-proxy\r\n\r\n",
454       false,
455       0u,
456       true,
457       -1,
458       BYPASS_EVENT_TYPE_MAX
459     },
460     // Valid data reduction proxy response with a bypass message.
461     { "GET",
462       "HTTP/1.1 200 OK\r\n"
463       "Server: proxy\r\n"
464       "Chrome-Proxy: bypass=0\r\n"
465       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
466       true,
467       1u,
468       true,
469       0,
470       BYPASS_EVENT_TYPE_MEDIUM
471     },
472     // Valid data reduction proxy response with a bypass message.
473     { "GET",
474       "HTTP/1.1 200 OK\r\n"
475       "Server: proxy\r\n"
476       "Chrome-Proxy: bypass=1\r\n"
477       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
478       true,
479       1u,
480       true,
481       1,
482       BYPASS_EVENT_TYPE_SHORT
483     },
484     // Same as above with the OPTIONS method, which is idempotent.
485     { "OPTIONS",
486       "HTTP/1.1 200 OK\r\n"
487       "Server: proxy\r\n"
488       "Chrome-Proxy: bypass=0\r\n"
489       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
490       true,
491       1u,
492       true,
493       0,
494       BYPASS_EVENT_TYPE_MEDIUM
495     },
496     // Same as above with the HEAD method, which is idempotent.
497     { "HEAD",
498       "HTTP/1.1 200 OK\r\n"
499       "Server: proxy\r\n"
500       "Chrome-Proxy: bypass=0\r\n"
501       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
502       true,
503       1u,
504       false,
505       0,
506       BYPASS_EVENT_TYPE_MEDIUM
507     },
508     // Same as above with the PUT method, which is idempotent.
509     { "PUT",
510       "HTTP/1.1 200 OK\r\n"
511       "Server: proxy\r\n"
512       "Chrome-Proxy: bypass=0\r\n"
513       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
514       true,
515       1u,
516       true,
517       0,
518       BYPASS_EVENT_TYPE_MEDIUM
519     },
520     // Same as above with the DELETE method, which is idempotent.
521     { "DELETE",
522       "HTTP/1.1 200 OK\r\n"
523       "Server: proxy\r\n"
524       "Chrome-Proxy: bypass=0\r\n"
525       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
526       true,
527       1u,
528       true,
529       0,
530       BYPASS_EVENT_TYPE_MEDIUM
531     },
532     // Same as above with the TRACE method, which is idempotent.
533     { "TRACE",
534       "HTTP/1.1 200 OK\r\n"
535       "Server: proxy\r\n"
536       "Chrome-Proxy: bypass=0\r\n"
537       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
538       true,
539       1u,
540       true,
541       0,
542       BYPASS_EVENT_TYPE_MEDIUM
543     },
544     // 500 responses should be bypassed.
545     { "GET",
546       "HTTP/1.1 500 Internal Server Error\r\n"
547       "Server: proxy\r\n"
548       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
549       true,
550       1u,
551       true,
552       0,
553       BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR
554     },
555     // 502 responses should be bypassed.
556     { "GET",
557       "HTTP/1.1 502 Internal Server Error\r\n"
558       "Server: proxy\r\n"
559       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
560       true,
561       1u,
562       true,
563       0,
564       BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY
565     },
566     // 503 responses should be bypassed.
567     { "GET",
568       "HTTP/1.1 503 Internal Server Error\r\n"
569       "Server: proxy\r\n"
570       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
571       true,
572       1u,
573       true,
574       0,
575       BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE
576     },
577     // Invalid data reduction proxy response. Missing Via header.
578     { "GET",
579       "HTTP/1.1 200 OK\r\n"
580       "Server: proxy\r\n\r\n",
581       true,
582       1u,
583       true,
584       0,
585       BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
586     },
587     // Invalid data reduction proxy response. Wrong Via header.
588     { "GET",
589       "HTTP/1.1 200 OK\r\n"
590       "Server: proxy\r\n"
591       "Via: 1.0 some-other-proxy\r\n\r\n",
592       true,
593       1u,
594       true,
595       0,
596       BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
597     },
598     // Valid data reduction proxy response. 304 missing Via header.
599     { "GET",
600       "HTTP/1.1 304 Not Modified\r\n"
601       "Server: proxy\r\n\r\n",
602       false,
603       0u,
604       false,
605       0,
606       BYPASS_EVENT_TYPE_MAX
607     },
608     // Valid data reduction proxy response with a bypass message. It will
609     // not be retried because the request is non-idempotent.
610     { "POST",
611       "HTTP/1.1 200 OK\r\n"
612       "Server: proxy\r\n"
613       "Chrome-Proxy: bypass=0\r\n"
614       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
615       false,
616       1u,
617       true,
618       0,
619       BYPASS_EVENT_TYPE_MEDIUM
620     },
621     // Valid data reduction proxy response with block message. Both proxies
622     // should be on the retry list when it completes.
623     { "GET",
624       "HTTP/1.1 200 OK\r\n"
625       "Server: proxy\r\n"
626       "Chrome-Proxy: block=1\r\n"
627       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
628       true,
629       2u,
630       true,
631       1,
632       BYPASS_EVENT_TYPE_SHORT
633     },
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.
637     { "GET",
638       "HTTP/1.1 200 OK\r\n"
639       "Server: proxy\r\n"
640       "Chrome-Proxy: block-once\r\n"
641       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
642       true,
643       0u,
644       true,
645       0,
646       BYPASS_EVENT_TYPE_CURRENT
647     },
648     // Same as above with the OPTIONS method, which is idempotent.
649     { "OPTIONS",
650       "HTTP/1.1 200 OK\r\n"
651       "Server: proxy\r\n"
652       "Chrome-Proxy: block-once\r\n"
653       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
654       true,
655       0u,
656       true,
657       0,
658       BYPASS_EVENT_TYPE_CURRENT
659     },
660     // Same as above with the HEAD method, which is idempotent.
661     { "HEAD",
662       "HTTP/1.1 200 OK\r\n"
663       "Server: proxy\r\n"
664       "Chrome-Proxy: block-once\r\n"
665       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
666       true,
667       0u,
668       false,
669       0,
670       BYPASS_EVENT_TYPE_CURRENT
671     },
672     // Same as above with the PUT method, which is idempotent.
673     { "PUT",
674       "HTTP/1.1 200 OK\r\n"
675       "Server: proxy\r\n"
676       "Chrome-Proxy: block-once\r\n"
677       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
678       true,
679       0u,
680       true,
681       0,
682       BYPASS_EVENT_TYPE_CURRENT
683     },
684     // Same as above with the DELETE method, which is idempotent.
685     { "DELETE",
686       "HTTP/1.1 200 OK\r\n"
687       "Server: proxy\r\n"
688       "Chrome-Proxy: block-once\r\n"
689       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
690       true,
691       0u,
692       true,
693       0,
694       BYPASS_EVENT_TYPE_CURRENT
695     },
696     // Same as above with the TRACE method, which is idempotent.
697     { "TRACE",
698       "HTTP/1.1 200 OK\r\n"
699       "Server: proxy\r\n"
700       "Chrome-Proxy: block-once\r\n"
701       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
702       true,
703       0u,
704       true,
705       0,
706       BYPASS_EVENT_TYPE_CURRENT
707     },
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
711     // request.
712     { "POST",
713       "HTTP/1.1 200 OK\r\n"
714       "Server: proxy\r\n"
715       "Chrome-Proxy: block-once\r\n"
716       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
717       false,
718       0u,
719       true,
720       0,
721       BYPASS_EVENT_TYPE_CURRENT
722     },
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.
726     { "GET",
727       "HTTP/1.1 200 OK\r\n"
728       "Server: proxy\r\n"
729       "Chrome-Proxy: block=1, block-once\r\n"
730       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
731       true,
732       2u,
733       true,
734       1,
735       BYPASS_EVENT_TYPE_SHORT
736     },
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.
740     { "GET",
741       "HTTP/1.1 200 OK\r\n"
742       "Server: proxy\r\n"
743       "Chrome-Proxy: bypass=1, block-once\r\n"
744       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
745       true,
746       1u,
747       true,
748       1,
749       BYPASS_EVENT_TYPE_SHORT
750     },
751   };
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(
757         "PROXY " +
758         HostPortPair::FromURL(GURL(primary)).ToString() + "; PROXY " +
759         HostPortPair::FromURL(GURL(fallback)).ToString() + "; DIRECT"),
760         &bypass_type);
761     TestProxyFallback(tests[i].method,
762                       tests[i].first_response,
763                       tests[i].expected_retry,
764                       tests[i].expect_response_body);
765
766     EXPECT_EQ(tests[i].expected_bypass_type, bypass_type);
767
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,
771                    primary, fallback);
772   }
773 }
774
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"),
780                             NULL);
781
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),
787   };
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"
792               "User-Agent:\r\n"
793               "Accept-Encoding: gzip,deflate\r\n\r\n"),
794   };
795   StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
796                                  data_writes, arraysize(data_writes));
797   mock_socket_factory_.AddSocketDataProvider(&data1);
798
799   TestDelegate d;
800   URLRequest r(GURL("http://www.google.com/"),
801                net::DEFAULT_PRIORITY,
802                &d,
803                context_.get());
804    r.set_method("GET");
805    r.SetLoadFlags(net::LOAD_NORMAL);
806
807    r.Start();
808    base::RunLoop().Run();
809
810    EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status());
811    EXPECT_EQ(net::OK, r.status().error());
812
813   EXPECT_EQ("Bypass message", d.data_received());
814
815   // We should have no entries in our bad proxy list.
816   TestBadProxies(0, -1, "", "");
817 }
818
819 class BadEntropyProvider : public base::FieldTrial::EntropyProvider {
820  public:
821   virtual ~BadEntropyProvider() {}
822
823   virtual double GetEntropyForTrial(const std::string& trial_name,
824                                     uint32 randomization_seed) const OVERRIDE {
825     return 0.5;
826   }
827 };
828
829 TEST_F(DataReductionProxyProtocolTest, OnResolveProxyHandler) {
830   int load_flags = net::LOAD_NORMAL;
831   GURL url("http://www.google.com/");
832
833   TestDataReductionProxyParams test_params(
834             DataReductionProxyParams::kAllowed |
835             DataReductionProxyParams::kFallbackAllowed |
836             DataReductionProxyParams::kPromoAllowed,
837             TestDataReductionProxyParams::HAS_EVERYTHING &
838             ~TestDataReductionProxyParams::HAS_DEV_ORIGIN);
839
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());
846
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);
852
853   // Other proxy info
854   net::ProxyInfo other_proxy_info;
855   other_proxy_info.UseNamedProxy("proxy.com");
856   EXPECT_FALSE(other_proxy_info.is_empty());
857
858   // Direct
859    net::ProxyInfo direct_proxy_info;
860    direct_proxy_info.UseDirect();
861    EXPECT_TRUE(direct_proxy_info.is_direct());
862
863    // Empty retry info map
864    net::ProxyRetryInfoMap empty_proxy_retry_info;
865
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;
874
875    net::ProxyInfo result;
876
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());
882
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());
888
889    // A direct connection is used. The data reduction proxy should be used
890    // afterwards.
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());
896
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,
902                          &result);
903    EXPECT_TRUE(result.proxy_server().is_direct());
904
905
906   // Without DataCompressionProxyCriticalBypass Finch trial set, should never
907   // bypass.
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());
912
913   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
914                         empty_proxy_retry_info, &test_params,
915                         &other_proxy_info);
916   EXPECT_FALSE(other_proxy_info.is_direct());
917
918   load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
919
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());
924
925   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
926                         empty_proxy_retry_info, &test_params,
927                         &other_proxy_info);
928   EXPECT_FALSE(other_proxy_info.is_direct());
929
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",
934                                          "Enabled");
935   base::FieldTrialList::CreateFieldTrial("DataCompressionProxyCriticalBypass",
936                                          "Enabled");
937   EXPECT_TRUE(
938       DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial());
939
940   load_flags = net::LOAD_NORMAL;
941
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());
946
947   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
948                         empty_proxy_retry_info, &test_params,
949                         &other_proxy_info);
950   EXPECT_FALSE(other_proxy_info.is_direct());
951
952   load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
953
954   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
955                         empty_proxy_retry_info, &test_params,
956                         &other_proxy_info);
957   EXPECT_FALSE(other_proxy_info.is_direct());
958
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());
963 }
964
965 }  // namespace data_reduction_proxy