Upstream version 11.39.266.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 "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"
30
31 using net::HttpResponseHeaders;
32 using net::HostPortPair;
33 using net::MockRead;
34 using net::MockWrite;
35 using net::ProxyRetryInfoMap;
36 using net::ProxyService;
37 using net::StaticSocketDataProvider;
38 using net::TestDelegate;
39 using net::URLRequest;
40 using net::TestURLRequestContext;
41
42
43 namespace data_reduction_proxy {
44
45 // A test network delegate that exercises the bypass logic of the data
46 // reduction proxy.
47 class TestDataReductionProxyNetworkDelegate : public net::NetworkDelegate {
48  public:
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) {
55   }
56
57   virtual int OnHeadersReceived(
58       URLRequest* request,
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_,
65         request,
66         original_response_headers,
67         override_response_headers,
68         bypass_type_);
69     return net::OK;
70   }
71
72   TestDataReductionProxyParams* test_data_reduction_proxy_params_;
73   DataReductionProxyBypassType* bypass_type_;
74 };
75
76 // Constructs a |TestURLRequestContext| that uses a |MockSocketFactory| to
77 // simulate requests and responses.
78 class DataReductionProxyProtocolTest : public testing::Test {
79  public:
80   DataReductionProxyProtocolTest() : http_user_agent_settings_("", "") {
81     proxy_params_.reset(
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));
89   }
90
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));
97
98     proxy_service_.reset(proxy_service);
99     network_delegate_.reset(new TestDataReductionProxyNetworkDelegate(
100         proxy_params_.get(), bypass_type));
101
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
106     // to requests.
107     context_->set_http_user_agent_settings(&http_user_agent_settings_);
108
109     context_->Init();
110   }
111
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,
120                          bool expected_retry,
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),
128     };
129     std::string m(method);
130     std::string trailer =
131         (m == "HEAD" || m == "PUT" || m == "POST") ?
132             "Content-Length: 0\r\n" : "";
133
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"
138                            "User-Agent:\r\n"
139                            "Accept-Encoding: gzip, deflate\r\n\r\n",
140                            method, trailer.c_str());
141     MockWrite data_writes[] = {
142       MockWrite(request1.c_str()),
143     };
144     StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
145                                   data_writes, arraysize(data_writes));
146     mock_socket_factory_.AddSocketDataProvider(&data1);
147
148       MockRead data_reads2[] = {
149         MockRead("HTTP/1.0 200 OK\r\n"
150                  "Server: not-proxy\r\n\r\n"),
151         MockRead("content"),
152         MockRead(net::SYNCHRONOUS, net::OK),
153       };
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"
158                              "User-Agent:\r\n"
159                              "Accept-Encoding: gzip, deflate\r\n\r\n",
160                              method, trailer.c_str());
161       MockWrite data_writes2[] = {
162           MockWrite(request2.c_str()),
163       };
164       StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
165                                      data_writes2, arraysize(data_writes2));
166       if (expected_retry) {
167         mock_socket_factory_.AddSocketDataProvider(&data2);
168     }
169
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(
173         method,
174         (expect_response_body ? "content" : ""),
175         "server",
176         (expected_retry == 0 ? "proxy" : "not-proxy"),
177         expected_retry);
178   }
179
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) {
189     TestDelegate d;
190     scoped_ptr<URLRequest> r(context_->CreateRequest(
191         GURL("http://www.google.com/"),
192         net::DEFAULT_PRIORITY,
193         &d,
194         NULL));
195     r->set_method(method);
196     r->SetLoadFlags(net::LOAD_NORMAL);
197
198     r->Start();
199     base::RunLoop().Run();
200
201     EXPECT_EQ(net::URLRequestStatus::SUCCESS, r->status().status());
202     EXPECT_EQ(net::OK, r->status().error());
203     if (expected_retry)
204       EXPECT_EQ(2U, r->url_chain().size());
205     else
206       EXPECT_EQ(1U, r->url_chain().size());
207
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));
211     }
212
213     EXPECT_EQ(content, d.data_received());
214   }
215
216   // Returns the key to the |ProxyRetryInfoMap|.
217   std::string GetProxyKey(std::string proxy) {
218     GURL gurl(proxy);
219     std::string host_port = HostPortPair::FromURL(GURL(proxy)).ToString();
220     if (gurl.SchemeIs("https"))
221       return "https://" + host_port;
222     return host_port;
223   }
224
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());
235
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);
241     } else {
242       expected_min_duration = base::TimeDelta::FromSeconds(duration_seconds);
243       expected_max_duration = base::TimeDelta::FromSeconds(duration_seconds);
244     }
245
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);
252     }
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);
259     }
260   }
261
262  protected:
263   base::MessageLoopForIO loop_;
264
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_;
270
271   scoped_ptr<TestURLRequestContext> context_;
272 };
273
274 // Tests that request are deemed idempotent or not according to the method used.
275 TEST_F(DataReductionProxyProtocolTest, TestIdempotency) {
276   net::TestURLRequestContext context;
277   const struct {
278     const char* method;
279     bool expected_result;
280   } tests[] = {
281       { "GET", true },
282       { "OPTIONS", true },
283       { "HEAD", true },
284       { "PUT", true },
285       { "DELETE", true },
286       { "TRACE", true },
287       { "POST", false },
288       { "CONNECT", false },
289   };
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,
294                               NULL,
295                               NULL));
296     request->set_method(tests[i].method);
297     EXPECT_EQ(tests[i].expected_result, IsRequestIdempotent(request.get()));
298   }
299 }
300
301 // Tests that the response is correctly overwritten as a redirect.
302 TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirect) {
303   net::TestURLRequestContext context;
304   const struct {
305     const char* headers;
306     const char* expected_headers;
307   } tests[] = {
308       { "HTTP/1.1 200 0K\n"
309         "Chrome-Proxy: block=1\n"
310         "Via: 1.1 Chrome-Compression-Proxy\n",
311
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"
316       },
317       { "HTTP/1.1 200 0K\n"
318         "Chrome-Proxy: block-once\n"
319         "Via: 1.1 Chrome-Compression-Proxy\n",
320
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"
325       },
326       { "HTTP/1.1 302 Found\n"
327         "Location: http://foo.com/\n",
328
329         "HTTP/1.1 302 Found\n"
330         "Location: http://www.google.com/\n"
331       },
332   };
333
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,
344                               NULL,
345                               NULL));
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);
353   }
354 }
355
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;
360   const struct {
361     const char* headers;
362     const char* expected_headers;
363   } tests[] = {
364       { "HTTP/1.1 200 0K\n"
365         "Chrome-Proxy: block=1\n"
366         "Via: 1.1 Chrome-Compression-Proxy\n",
367
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"
374       },
375   };
376
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,
387                               NULL,
388                               NULL));
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);
397   }
398 }
399
400
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) {
407   const struct {
408     const char* method;
409     const char* first_response;
410     bool expected_retry;
411     size_t expected_bad_proxy_count;
412     bool expect_response_body;
413     int expected_duration;
414     DataReductionProxyBypassType expected_bypass_type;
415   } tests[] = {
416     // Valid data reduction proxy response with no bypass message.
417     { "GET",
418       "HTTP/1.1 200 OK\r\n"
419       "Server: proxy\r\n"
420       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
421       false,
422       0u,
423       true,
424       -1,
425       BYPASS_EVENT_TYPE_MAX,
426     },
427     // Valid data reduction proxy response with older, but still valid via
428     // header.
429     { "GET",
430       "HTTP/1.1 200 OK\r\n"
431       "Server: proxy\r\n"
432       "Via: 1.1 Chrome Compression Proxy\r\n\r\n",
433       false,
434       0u,
435       true,
436       -1,
437       BYPASS_EVENT_TYPE_MAX
438     },
439     // Valid data reduction proxy response with chained via header,
440     // no bypass message.
441     { "GET",
442       "HTTP/1.1 200 OK\r\n"
443       "Server: proxy\r\n"
444       "Via: 1.1 Chrome-Compression-Proxy, 1.0 some-other-proxy\r\n\r\n",
445       false,
446       0u,
447       true,
448       -1,
449       BYPASS_EVENT_TYPE_MAX
450     },
451     // Valid data reduction proxy response with a bypass message.
452     { "GET",
453       "HTTP/1.1 200 OK\r\n"
454       "Server: proxy\r\n"
455       "Chrome-Proxy: bypass=0\r\n"
456       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
457       true,
458       1u,
459       true,
460       0,
461       BYPASS_EVENT_TYPE_MEDIUM
462     },
463     // Valid data reduction proxy response with a bypass message.
464     { "GET",
465       "HTTP/1.1 200 OK\r\n"
466       "Server: proxy\r\n"
467       "Chrome-Proxy: bypass=1\r\n"
468       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
469       true,
470       1u,
471       true,
472       1,
473       BYPASS_EVENT_TYPE_SHORT
474     },
475     // Same as above with the OPTIONS method, which is idempotent.
476     { "OPTIONS",
477       "HTTP/1.1 200 OK\r\n"
478       "Server: proxy\r\n"
479       "Chrome-Proxy: bypass=0\r\n"
480       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
481       true,
482       1u,
483       true,
484       0,
485       BYPASS_EVENT_TYPE_MEDIUM
486     },
487     // Same as above with the HEAD method, which is idempotent.
488     { "HEAD",
489       "HTTP/1.1 200 OK\r\n"
490       "Server: proxy\r\n"
491       "Chrome-Proxy: bypass=0\r\n"
492       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
493       true,
494       1u,
495       false,
496       0,
497       BYPASS_EVENT_TYPE_MEDIUM
498     },
499     // Same as above with the PUT method, which is idempotent.
500     { "PUT",
501       "HTTP/1.1 200 OK\r\n"
502       "Server: proxy\r\n"
503       "Chrome-Proxy: bypass=0\r\n"
504       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
505       true,
506       1u,
507       true,
508       0,
509       BYPASS_EVENT_TYPE_MEDIUM
510     },
511     // Same as above with the DELETE method, which is idempotent.
512     { "DELETE",
513       "HTTP/1.1 200 OK\r\n"
514       "Server: proxy\r\n"
515       "Chrome-Proxy: bypass=0\r\n"
516       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
517       true,
518       1u,
519       true,
520       0,
521       BYPASS_EVENT_TYPE_MEDIUM
522     },
523     // Same as above with the TRACE method, which is idempotent.
524     { "TRACE",
525       "HTTP/1.1 200 OK\r\n"
526       "Server: proxy\r\n"
527       "Chrome-Proxy: bypass=0\r\n"
528       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
529       true,
530       1u,
531       true,
532       0,
533       BYPASS_EVENT_TYPE_MEDIUM
534     },
535     // 500 responses should be bypassed.
536     { "GET",
537       "HTTP/1.1 500 Internal Server Error\r\n"
538       "Server: proxy\r\n"
539       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
540       true,
541       1u,
542       true,
543       0,
544       BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR
545     },
546     // 502 responses should be bypassed.
547     { "GET",
548       "HTTP/1.1 502 Internal Server Error\r\n"
549       "Server: proxy\r\n"
550       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
551       true,
552       1u,
553       true,
554       0,
555       BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY
556     },
557     // 503 responses should be bypassed.
558     { "GET",
559       "HTTP/1.1 503 Internal Server Error\r\n"
560       "Server: proxy\r\n"
561       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
562       true,
563       1u,
564       true,
565       0,
566       BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE
567     },
568     // Invalid data reduction proxy response. Missing Via header.
569     { "GET",
570       "HTTP/1.1 200 OK\r\n"
571       "Server: proxy\r\n\r\n",
572       true,
573       1u,
574       true,
575       0,
576       BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
577     },
578     // Invalid data reduction proxy response. Wrong Via header.
579     { "GET",
580       "HTTP/1.1 200 OK\r\n"
581       "Server: proxy\r\n"
582       "Via: 1.0 some-other-proxy\r\n\r\n",
583       true,
584       1u,
585       true,
586       0,
587       BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
588     },
589     // Valid data reduction proxy response. 304 missing Via header.
590     { "GET",
591       "HTTP/1.1 304 Not Modified\r\n"
592       "Server: proxy\r\n\r\n",
593       false,
594       0u,
595       false,
596       0,
597       BYPASS_EVENT_TYPE_MAX
598     },
599     // Valid data reduction proxy response with a bypass message. It will
600     // not be retried because the request is non-idempotent.
601     { "POST",
602       "HTTP/1.1 200 OK\r\n"
603       "Server: proxy\r\n"
604       "Chrome-Proxy: bypass=0\r\n"
605       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
606       false,
607       1u,
608       true,
609       0,
610       BYPASS_EVENT_TYPE_MEDIUM
611     },
612     // Valid data reduction proxy response with block message. Both proxies
613     // should be on the retry list when it completes.
614     { "GET",
615       "HTTP/1.1 200 OK\r\n"
616       "Server: proxy\r\n"
617       "Chrome-Proxy: block=1\r\n"
618       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
619       true,
620       2u,
621       true,
622       1,
623       BYPASS_EVENT_TYPE_SHORT
624     },
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.
628     { "GET",
629       "HTTP/1.1 200 OK\r\n"
630       "Server: proxy\r\n"
631       "Chrome-Proxy: block-once\r\n"
632       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
633       true,
634       0u,
635       true,
636       0,
637       BYPASS_EVENT_TYPE_CURRENT
638     },
639     // Same as above with the OPTIONS method, which is idempotent.
640     { "OPTIONS",
641       "HTTP/1.1 200 OK\r\n"
642       "Server: proxy\r\n"
643       "Chrome-Proxy: block-once\r\n"
644       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
645       true,
646       0u,
647       true,
648       0,
649       BYPASS_EVENT_TYPE_CURRENT
650     },
651     // Same as above with the HEAD method, which is idempotent.
652     { "HEAD",
653       "HTTP/1.1 200 OK\r\n"
654       "Server: proxy\r\n"
655       "Chrome-Proxy: block-once\r\n"
656       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
657       true,
658       0u,
659       false,
660       0,
661       BYPASS_EVENT_TYPE_CURRENT
662     },
663     // Same as above with the PUT method, which is idempotent.
664     { "PUT",
665       "HTTP/1.1 200 OK\r\n"
666       "Server: proxy\r\n"
667       "Chrome-Proxy: block-once\r\n"
668       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
669       true,
670       0u,
671       true,
672       0,
673       BYPASS_EVENT_TYPE_CURRENT
674     },
675     // Same as above with the DELETE method, which is idempotent.
676     { "DELETE",
677       "HTTP/1.1 200 OK\r\n"
678       "Server: proxy\r\n"
679       "Chrome-Proxy: block-once\r\n"
680       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
681       true,
682       0u,
683       true,
684       0,
685       BYPASS_EVENT_TYPE_CURRENT
686     },
687     // Same as above with the TRACE method, which is idempotent.
688     { "TRACE",
689       "HTTP/1.1 200 OK\r\n"
690       "Server: proxy\r\n"
691       "Chrome-Proxy: block-once\r\n"
692       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
693       true,
694       0u,
695       true,
696       0,
697       BYPASS_EVENT_TYPE_CURRENT
698     },
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
702     // request.
703     { "POST",
704       "HTTP/1.1 200 OK\r\n"
705       "Server: proxy\r\n"
706       "Chrome-Proxy: block-once\r\n"
707       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
708       false,
709       0u,
710       true,
711       0,
712       BYPASS_EVENT_TYPE_CURRENT
713     },
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.
717     { "GET",
718       "HTTP/1.1 200 OK\r\n"
719       "Server: proxy\r\n"
720       "Chrome-Proxy: block=1, block-once\r\n"
721       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
722       true,
723       2u,
724       true,
725       1,
726       BYPASS_EVENT_TYPE_SHORT
727     },
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.
731     { "GET",
732       "HTTP/1.1 200 OK\r\n"
733       "Server: proxy\r\n"
734       "Chrome-Proxy: bypass=1, block-once\r\n"
735       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
736       true,
737       1u,
738       true,
739       1,
740       BYPASS_EVENT_TYPE_SHORT
741     },
742   };
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(
748         "PROXY " +
749         HostPortPair::FromURL(GURL(primary)).ToString() + "; PROXY " +
750         HostPortPair::FromURL(GURL(fallback)).ToString() + "; DIRECT"),
751         &bypass_type);
752     TestProxyFallback(tests[i].method,
753                       tests[i].first_response,
754                       tests[i].expected_retry,
755                       tests[i].expect_response_body);
756
757     EXPECT_EQ(tests[i].expected_bypass_type, bypass_type);
758
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,
762                    primary, fallback);
763   }
764 }
765
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"),
771                             NULL);
772
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),
778   };
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"
783               "User-Agent:\r\n"
784               "Accept-Encoding: gzip, deflate\r\n\r\n"),
785   };
786   StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
787                                  data_writes, arraysize(data_writes));
788   mock_socket_factory_.AddSocketDataProvider(&data1);
789
790   TestDelegate d;
791   scoped_ptr<URLRequest> r(context_->CreateRequest(
792       GURL("http://www.google.com/"),
793       net::DEFAULT_PRIORITY,
794       &d,
795       NULL));
796   r->set_method("GET");
797   r->SetLoadFlags(net::LOAD_NORMAL);
798
799   r->Start();
800   base::RunLoop().Run();
801
802   EXPECT_EQ(net::URLRequestStatus::SUCCESS, r->status().status());
803   EXPECT_EQ(net::OK, r->status().error());
804
805   EXPECT_EQ("Bypass message", d.data_received());
806
807   // We should have no entries in our bad proxy list.
808   TestBadProxies(0, -1, "", "");
809 }
810
811 class BadEntropyProvider : public base::FieldTrial::EntropyProvider {
812  public:
813   virtual ~BadEntropyProvider() {}
814
815   virtual double GetEntropyForTrial(const std::string& trial_name,
816                                     uint32 randomization_seed) const OVERRIDE {
817     return 0.5;
818   }
819 };
820
821 TEST_F(DataReductionProxyProtocolTest, OnResolveProxyHandler) {
822   int load_flags = net::LOAD_NORMAL;
823   GURL url("http://www.google.com/");
824
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);
832
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());
839
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);
845
846   // Other proxy info
847   net::ProxyInfo other_proxy_info;
848   other_proxy_info.UseNamedProxy("proxy.com");
849   EXPECT_FALSE(other_proxy_info.is_empty());
850
851   // Direct
852    net::ProxyInfo direct_proxy_info;
853    direct_proxy_info.UseDirect();
854    EXPECT_TRUE(direct_proxy_info.is_direct());
855
856    // Empty retry info map
857    net::ProxyRetryInfoMap empty_proxy_retry_info;
858
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;
867
868    net::ProxyInfo result;
869
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());
875
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());
881
882    // A direct connection is used. The data reduction proxy should be used
883    // afterwards.
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);
892
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,
899                          &result);
900    EXPECT_TRUE(result.proxy_server().is_direct());
901    EXPECT_EQ(result.config_id(), prev_id);
902
903
904   // Without DataCompressionProxyCriticalBypass Finch trial set, should never
905   // bypass.
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());
910
911   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
912                         empty_proxy_retry_info, &test_params,
913                         &other_proxy_info);
914   EXPECT_FALSE(other_proxy_info.is_direct());
915
916   load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
917
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());
922
923   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
924                         empty_proxy_retry_info, &test_params,
925                         &other_proxy_info);
926   EXPECT_FALSE(other_proxy_info.is_direct());
927
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",
932                                          "Enabled");
933   EXPECT_TRUE(
934       DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial());
935
936   load_flags = net::LOAD_NORMAL;
937
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());
942
943   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
944                         empty_proxy_retry_info, &test_params,
945                         &other_proxy_info);
946   EXPECT_FALSE(other_proxy_info.is_direct());
947
948   load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY;
949
950   OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config,
951                         empty_proxy_retry_info, &test_params,
952                         &other_proxy_info);
953   EXPECT_FALSE(other_proxy_info.is_direct());
954
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());
959 }
960
961 }  // namespace data_reduction_proxy