• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/run_loop.h"
12 #include "base/strings/stringprintf.h"
13 #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h"
14 #include "net/base/completion_callback.h"
15 #include "net/base/host_port_pair.h"
16 #include "net/base/network_delegate.h"
17 #include "net/http/http_response_headers.h"
18 #include "net/http/http_transaction_test_util.h"
19 #include "net/socket/socket_test_util.h"
20 #include "net/url_request/static_http_user_agent_settings.h"
21 #include "net/url_request/url_request.h"
22 #include "net/url_request/url_request_context.h"
23 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 using net::HttpResponseHeaders;
27 using net::HostPortPair;
28 using net::MockRead;
29 using net::MockWrite;
30 using net::ProxyRetryInfoMap;
31 using net::ProxyService;
32 using net::StaticSocketDataProvider;
33 using net::TestDelegate;
34 using net::URLRequest;
35 using net::TestURLRequestContext;
36 
37 
38 namespace {
39 // Transform "normal"-looking headers (\n-separated) to the appropriate
40 // input format for ParseRawHeaders (\0-separated).
HeadersToRaw(std::string * headers)41 void HeadersToRaw(std::string* headers) {
42   std::replace(headers->begin(), headers->end(), '\n', '\0');
43   if (!headers->empty())
44     *headers += '\0';
45 }
46 
GetDataReductionProxy()47 std::string GetDataReductionProxy() {
48   return "https://proxy1.com:443/";
49 }
50 
GetDataReductionProxyFallback()51 std::string GetDataReductionProxyFallback() {
52   return "http://proxy2.com:80/";
53 }
54 } // namespace
55 
56 
57 namespace data_reduction_proxy {
58 namespace {
59 class TestDataReductionProxyParams : public DataReductionProxyParams {
60  public:
TestDataReductionProxyParams()61   TestDataReductionProxyParams() : DataReductionProxyParams(0, false) {}
62 
63   virtual bool WasDataReductionProxyUsed(
64       const net::URLRequest* request,
65       std::pair<GURL, GURL>* proxy_servers) const OVERRIDE;
66 };
67 
WasDataReductionProxyUsed(const net::URLRequest * request,std::pair<GURL,GURL> * proxy_servers) const68 bool TestDataReductionProxyParams::WasDataReductionProxyUsed(
69     const net::URLRequest* request,
70     std::pair<GURL, GURL>* proxy_servers) const {
71   if (net::HostPortPair::FromURL(GURL(GetDataReductionProxy())).Equals(
72           request->proxy_server())) {
73     proxy_servers->first = GURL(GetDataReductionProxy());
74     proxy_servers->second = GURL(GetDataReductionProxyFallback());
75     return true;
76   }
77   if (net::HostPortPair::FromURL(
78           GURL(GetDataReductionProxyFallback())).Equals(
79           request->proxy_server())) {
80     proxy_servers->first = GURL(GetDataReductionProxyFallback());
81     proxy_servers->second = GURL();
82     return true;
83   }
84   return false;
85 }
86 }  // namespace
87 
88 // A test network delegate that exercises the bypass logic of the data
89 // reduction proxy.
90 class TestDataReductionProxyNetworkDelegate : public net::NetworkDelegate {
91  public:
TestDataReductionProxyNetworkDelegate(TestDataReductionProxyParams * test_params)92   TestDataReductionProxyNetworkDelegate(
93       TestDataReductionProxyParams* test_params)
94       : net::NetworkDelegate(), test_data_reduction_proxy_params_(test_params) {
95   }
96 
OnHeadersReceived(URLRequest * request,const net::CompletionCallback & callback,const HttpResponseHeaders * original_response_headers,scoped_refptr<HttpResponseHeaders> * override_response_headers,GURL * allowed_unsafe_redirect_url)97   virtual int OnHeadersReceived(
98       URLRequest* request,
99       const net::CompletionCallback& callback,
100       const HttpResponseHeaders* original_response_headers,
101       scoped_refptr<HttpResponseHeaders>* override_response_headers,
102       GURL* allowed_unsafe_redirect_url) OVERRIDE {
103     data_reduction_proxy::MaybeBypassProxyAndPrepareToRetry(
104         test_data_reduction_proxy_params_,
105         request,
106         original_response_headers,
107         override_response_headers);
108     return net::OK;
109   }
110 
111   TestDataReductionProxyParams* test_data_reduction_proxy_params_;
112 };
113 
114 // Constructs a |TestURLRequestContext| that uses a |MockSocketFactory| to
115 // simulate requests and responses.
116 class DataReductionProxyProtocolTest : public testing::Test {
117  public:
DataReductionProxyProtocolTest()118   DataReductionProxyProtocolTest() : http_user_agent_settings_("", "") {}
119 
120   // Sets up the |TestURLRequestContext| with the provided |ProxyService|.
ConfigureTestDependencies(ProxyService * proxy_service)121   void ConfigureTestDependencies(ProxyService* proxy_service) {
122     // Create a context with delayed initialization.
123     context_.reset(new TestURLRequestContext(true));
124 
125     proxy_service_.reset(proxy_service);
126     proxy_params_.reset(new TestDataReductionProxyParams());
127     network_delegate_.reset(new TestDataReductionProxyNetworkDelegate(
128         proxy_params_.get()));
129 
130     context_->set_client_socket_factory(&mock_socket_factory_);
131     context_->set_proxy_service(proxy_service_.get());
132     context_->set_network_delegate(network_delegate_.get());
133     // This is needed to prevent the test context from adding language headers
134     // to requests.
135     context_->set_http_user_agent_settings(&http_user_agent_settings_);
136 
137     context_->Init();
138   }
139 
140   // Simulates a request to a data reduction proxy that may result in bypassing
141   // the proxy and retrying the the request.
142   // Runs a test with the given request |method| that expects the first response
143   // from the server to be |first_response|. If |expected_retry|, the test
144   // will expect a retry of the request. A response body will be expected
145   // if |expect_response_body|.
TestProxyFallback(const char * method,const char * first_response,bool expected_retry,bool expect_response_body)146   void TestProxyFallback(const char* method,
147                          const char* first_response,
148                          bool expected_retry,
149                          bool expect_response_body) {
150     std::string payload1 =
151         (expected_retry ? "Bypass message" : "content");
152     MockRead data_reads[] = {
153       MockRead(first_response),
154       MockRead(payload1.c_str()),
155       MockRead(net::SYNCHRONOUS, net::OK),
156     };
157     std::string m(method);
158     std::string trailer =
159         (m == "HEAD" || m == "PUT" || m == "POST") ?
160             "Content-Length: 0\r\n" : "";
161 
162     std::string request1 =
163         base::StringPrintf("%s http://www.google.com/ HTTP/1.1\r\n"
164                            "Host: www.google.com\r\n"
165                            "Proxy-Connection: keep-alive\r\n%s"
166                            "User-Agent:\r\n"
167                            "Accept-Encoding: gzip,deflate\r\n\r\n",
168                            method, trailer.c_str());
169     MockWrite data_writes[] = {
170       MockWrite(request1.c_str()),
171     };
172     StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
173                                   data_writes, arraysize(data_writes));
174     mock_socket_factory_.AddSocketDataProvider(&data1);
175 
176       MockRead data_reads2[] = {
177         MockRead("HTTP/1.0 200 OK\r\n"
178                  "Server: not-proxy\r\n\r\n"),
179         MockRead("content"),
180         MockRead(net::SYNCHRONOUS, net::OK),
181       };
182       std::string request2 =
183           base::StringPrintf("%s / HTTP/1.1\r\n"
184                              "Host: www.google.com\r\n"
185                              "Connection: keep-alive\r\n%s"
186                              "User-Agent:\r\n"
187                              "Accept-Encoding: gzip,deflate\r\n\r\n",
188                              method, trailer.c_str());
189       MockWrite data_writes2[] = {
190           MockWrite(request2.c_str()),
191       };
192       StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
193                                      data_writes2, arraysize(data_writes2));
194       if (expected_retry) {
195         mock_socket_factory_.AddSocketDataProvider(&data2);
196     }
197 
198     // Expect that we get "content" and not "Bypass message", and that there's
199     // a "not-proxy" "Server:" header in the final response.
200     ExecuteRequestExpectingContentAndHeader(
201         method,
202         (expect_response_body ? "content" : ""),
203         "server",
204         (expected_retry == 0 ? "proxy" : "not-proxy"),
205         expected_retry);
206   }
207 
208   // Starts a request with the given |method| and checks that the response
209   // contains |content| and the the header |header|: |value|, if |header| is
210   // non-empty. Verifies that the request's URL chain is the right length
211   // depending on whether or not a retry was expected (|expected_retry|).
ExecuteRequestExpectingContentAndHeader(const std::string & method,const std::string & content,const std::string & header,const std::string & value,bool expected_retry)212   void ExecuteRequestExpectingContentAndHeader(const std::string& method,
213                                                const std::string& content,
214                                                const std::string& header,
215                                                const std::string& value,
216                                                bool expected_retry) {
217     TestDelegate d;
218     URLRequest r(GURL("http://www.google.com/"),
219                  net::DEFAULT_PRIORITY,
220                  &d,
221                  context_.get());
222     r.set_method(method);
223     r.SetLoadFlags(net::LOAD_NORMAL);
224 
225     r.Start();
226     base::RunLoop().Run();
227 
228     EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status());
229     EXPECT_EQ(net::OK, r.status().error());
230     if (expected_retry)
231       EXPECT_EQ(2U, r.url_chain().size());
232     else
233       EXPECT_EQ(1U, r.url_chain().size());
234 
235     if (!header.empty()) {
236       // We also have a server header here that isn't set by the proxy.
237       EXPECT_TRUE(r.response_headers()->HasHeaderValue(header, value));
238     }
239 
240     EXPECT_EQ(content, d.data_received());
241   }
242 
243   // Returns the key to the |ProxyRetryInfoMap|.
GetProxyKey(std::string proxy)244   std::string GetProxyKey(std::string proxy) {
245     GURL gurl(proxy);
246     std::string host_port = HostPortPair::FromURL(GURL(proxy)).ToString();
247     if (gurl.SchemeIs("https"))
248       return "https://" + host_port;
249     return host_port;
250   }
251 
252   // Checks that |expected_num_bad_proxies| proxies are on the proxy retry list.
253   // If the list has one proxy, it should match |bad_proxy|. If it has two
254   // proxies, it should match |bad_proxy| and |bad_proxy2|. Checks also that
255   // the current delay associated with each bad proxy is |duration_seconds|.
TestBadProxies(unsigned int expected_num_bad_proxies,int duration_seconds,const std::string & bad_proxy,const std::string & bad_proxy2)256   void TestBadProxies(unsigned int expected_num_bad_proxies,
257                       int duration_seconds,
258                       const std::string& bad_proxy,
259                       const std::string& bad_proxy2) {
260     const ProxyRetryInfoMap& retry_info = proxy_service_->proxy_retry_info();
261     ASSERT_EQ(expected_num_bad_proxies, retry_info.size());
262 
263     base::TimeDelta expected_min_duration;
264     base::TimeDelta expected_max_duration;
265     if (duration_seconds == 0) {
266       expected_min_duration = base::TimeDelta::FromMinutes(1);
267       expected_max_duration = base::TimeDelta::FromMinutes(5);
268     }
269     else {
270       expected_min_duration = base::TimeDelta::FromSeconds(duration_seconds);
271       expected_max_duration = base::TimeDelta::FromSeconds(duration_seconds);
272     }
273 
274     if (expected_num_bad_proxies >= 1u) {
275       ProxyRetryInfoMap::const_iterator i =
276           retry_info.find(GetProxyKey(bad_proxy));
277       ASSERT_TRUE(i != retry_info.end());
278       EXPECT_TRUE(expected_min_duration <= (*i).second.current_delay);
279       EXPECT_TRUE((*i).second.current_delay <= expected_max_duration);
280     }
281     if (expected_num_bad_proxies == 2u) {
282       ProxyRetryInfoMap::const_iterator i =
283           retry_info.find(GetProxyKey(bad_proxy2));
284       ASSERT_TRUE(i != retry_info.end());
285       EXPECT_TRUE(expected_min_duration <= (*i).second.current_delay);
286       EXPECT_TRUE((*i).second.current_delay <= expected_max_duration);
287     }
288   }
289 
290  protected:
291   base::MessageLoopForIO loop_;
292 
293   net::MockClientSocketFactory mock_socket_factory_;
294   scoped_ptr<ProxyService> proxy_service_;
295   scoped_ptr<TestDataReductionProxyParams> proxy_params_;
296   scoped_ptr<TestDataReductionProxyNetworkDelegate> network_delegate_;
297   net::StaticHttpUserAgentSettings http_user_agent_settings_;
298 
299   scoped_ptr<TestURLRequestContext> context_;
300 };
301 
302 // Tests that request are deemed idempotent or not according to the method used.
TEST_F(DataReductionProxyProtocolTest,TestIdempotency)303 TEST_F(DataReductionProxyProtocolTest, TestIdempotency) {
304   net::TestURLRequestContext context;
305   const struct {
306     const char* method;
307     bool expected_result;
308   } tests[] = {
309       { "GET", true },
310       { "OPTIONS", true },
311       { "HEAD", true },
312       { "PUT", true },
313       { "DELETE", true },
314       { "TRACE", true },
315       { "POST", false },
316       { "CONNECT", false },
317   };
318   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
319     net::TestURLRequest request(GURL("http://www.google.com/"),
320                                 net::DEFAULT_PRIORITY,
321                                 NULL,
322                                 &context);
323     request.set_method(tests[i].method);
324     EXPECT_EQ(tests[i].expected_result, IsRequestIdempotent(&request));
325   }
326 }
327 
328 // Tests that the response is correctly overwritten as a redirect.
TEST_F(DataReductionProxyProtocolTest,OverrideResponseAsRedirect)329 TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirect) {
330   net::TestURLRequestContext context;
331   const struct {
332     const char* headers;
333     const char* expected_headers;
334   } tests[] = {
335       { "HTTP/1.1 200 0K\n"
336         "Chrome-Proxy: block=1\n"
337         "Via: 1.1 Chrome-Compression-Proxy\n",
338 
339         "HTTP/1.1 302 Found\n"
340         "Chrome-Proxy: block=1\n"
341         "Via: 1.1 Chrome-Compression-Proxy\n"
342         "Location: http://www.google.com/\n"
343       },
344       { "HTTP/1.1 302 Found\n"
345         "Location: http://foo.com/\n",
346 
347         "HTTP/1.1 302 Found\n"
348         "Location: http://www.google.com/\n"
349       },
350   };
351 
352   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
353     std::string headers(tests[i].headers);
354     HeadersToRaw(&headers);
355     scoped_refptr<HttpResponseHeaders> original_response_headers(
356         new HttpResponseHeaders(headers));
357     scoped_refptr<HttpResponseHeaders> override_response_headers;
358     TestDelegate test_delegate;
359     net::TestURLRequest request(GURL("http://www.google.com/"),
360                            net::DEFAULT_PRIORITY,
361                            NULL,
362                            &context);
363     OverrideResponseAsRedirect(&request,
364                                original_response_headers,
365                                &override_response_headers);
366     int expected_flags = net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_PROXY;
367     EXPECT_EQ(expected_flags, request.load_flags());
368     std::string override_headers;
369     override_response_headers->GetNormalizedHeaders(&override_headers);
370     EXPECT_EQ(std::string(tests[i].expected_headers), override_headers);
371   }
372 }
373 
374 
375 // After each test, the proxy retry info will contain zero, one, or two of the
376 // data reduction proxies depending on whether no bypass was indicated by the
377 // initial response, a single proxy bypass was indicated, or a double bypass
378 // was indicated. In both the single and double bypass cases, if the request
379 // was idempotent, it will be retried over a direct connection.
TEST_F(DataReductionProxyProtocolTest,BypassLogic)380 TEST_F(DataReductionProxyProtocolTest, BypassLogic) {
381   std::string primary = GetDataReductionProxy();
382   std::string fallback = GetDataReductionProxyFallback();
383   const struct {
384     const char* method;
385     const char* first_response;
386     bool expected_retry;
387     size_t expected_bad_proxy_count;
388     bool expect_response_body;
389     int expected_duration;
390   } tests[] = {
391     // Valid data reduction proxy response with no bypass message.
392     { "GET",
393       "HTTP/1.1 200 OK\r\n"
394       "Server: proxy\r\n"
395       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
396       false,
397       0u,
398       true,
399       -1
400     },
401     // Valid data reduction proxy response with older, but still valid via
402     // header.
403     { "GET",
404       "HTTP/1.1 200 OK\r\n"
405       "Server: proxy\r\n"
406       "Via: 1.1 Chrome Compression Proxy\r\n\r\n",
407       false,
408       0u,
409       true,
410       -1
411     },
412     // Valid data reduction proxy response with chained via header,
413     // no bypass message.
414     { "GET",
415       "HTTP/1.1 200 OK\r\n"
416       "Server: proxy\r\n"
417       "Via: 1.1 Chrome-Compression-Proxy, 1.0 some-other-proxy\r\n\r\n",
418       false,
419       0u,
420       true,
421       -1
422     },
423     // Valid data reduction proxy response with a bypass message.
424     { "GET",
425       "HTTP/1.1 200 OK\r\n"
426       "Server: proxy\r\n"
427       "Chrome-Proxy: bypass=0\r\n"
428       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
429       true,
430       1u,
431       true,
432       0
433     },
434     // Valid data reduction proxy response with a bypass message.
435     { "GET",
436       "HTTP/1.1 200 OK\r\n"
437       "Server: proxy\r\n"
438       "Chrome-Proxy: bypass=1\r\n"
439       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
440       true,
441       1u,
442       true,
443       1
444     },
445     // Same as above with the OPTIONS method, which is idempotent.
446     { "OPTIONS",
447       "HTTP/1.1 200 OK\r\n"
448       "Server: proxy\r\n"
449       "Chrome-Proxy: bypass=0\r\n"
450       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
451       true,
452       1u,
453       true,
454       0
455     },
456     // Same as above with the HEAD method, which is idempotent.
457     { "HEAD",
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       true,
463       1u,
464       false,
465       0
466     },
467     // Same as above with the PUT method, which is idempotent.
468     { "PUT",
469       "HTTP/1.1 200 OK\r\n"
470       "Server: proxy\r\n"
471       "Chrome-Proxy: bypass=0\r\n"
472       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
473       true,
474       1u,
475       true,
476       0
477     },
478     // Same as above with the DELETE method, which is idempotent.
479     { "DELETE",
480       "HTTP/1.1 200 OK\r\n"
481       "Server: proxy\r\n"
482       "Chrome-Proxy: bypass=0\r\n"
483       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
484       true,
485       1u,
486       true,
487       0
488     },
489     // Same as above with the TRACE method, which is idempotent.
490     { "TRACE",
491       "HTTP/1.1 200 OK\r\n"
492       "Server: proxy\r\n"
493       "Chrome-Proxy: bypass=0\r\n"
494       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
495       true,
496       1u,
497       true,
498       0
499     },
500     // 500 responses should be bypassed.
501     { "GET",
502       "HTTP/1.1 500 Internal Server Error\r\n"
503       "Server: proxy\r\n"
504       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
505       true,
506       1u,
507       true,
508       0
509     },
510     // 502 responses should be bypassed.
511     { "GET",
512       "HTTP/1.1 502 Internal Server Error\r\n"
513       "Server: proxy\r\n"
514       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
515       true,
516       1u,
517       true,
518       0
519     },
520     // 503 responses should be bypassed.
521     { "GET",
522       "HTTP/1.1 503 Internal Server Error\r\n"
523       "Server: proxy\r\n"
524       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
525       true,
526       1u,
527       true,
528       0
529     },
530     // Invalid data reduction proxy response. Missing Via header.
531     { "GET",
532       "HTTP/1.1 200 OK\r\n"
533       "Server: proxy\r\n\r\n",
534       true,
535       1u,
536       true,
537       0
538     },
539     // Invalid data reduction proxy response. Wrong Via header.
540     { "GET",
541       "HTTP/1.1 200 OK\r\n"
542       "Server: proxy\r\n"
543       "Via: 1.0 some-other-proxy\r\n\r\n",
544       true,
545       1u,
546       true,
547       0
548     },
549     // Valid data reduction proxy response. 304 missing Via header.
550     { "GET",
551       "HTTP/1.1 304 Not Modified\r\n"
552       "Server: proxy\r\n\r\n",
553       false,
554       0u,
555       false,
556       0
557     },
558     // Valid data reduction proxy response with a bypass message. It will
559     // not be retried because the request is non-idempotent.
560     { "POST",
561       "HTTP/1.1 200 OK\r\n"
562       "Server: proxy\r\n"
563       "Chrome-Proxy: bypass=0\r\n"
564       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
565       false,
566       1u,
567       true,
568       0
569     },
570     // Valid data reduction proxy response with block message. Both proxies
571     // should be on the retry list when it completes.
572     { "GET",
573       "HTTP/1.1 200 OK\r\n"
574       "Server: proxy\r\n"
575       "Chrome-Proxy: block=1\r\n"
576       "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
577       true,
578       2u,
579       true,
580       1
581     }
582   };
583   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
584     ConfigureTestDependencies(
585         ProxyService::CreateFixedFromPacResult("PROXY " +
586             HostPortPair::FromURL(GURL(primary)).ToString() + "; PROXY " +
587             HostPortPair::FromURL(GURL(fallback)).ToString() + "; DIRECT"));
588     TestProxyFallback(tests[i].method,
589                       tests[i].first_response,
590                       tests[i].expected_retry,
591                       tests[i].expect_response_body);
592 
593     // We should also observe the bad proxy in the retry list.
594     TestBadProxies(tests[i].expected_bad_proxy_count,
595                    tests[i].expected_duration,
596                    primary, fallback);
597   }
598 }
599 
TEST_F(DataReductionProxyProtocolTest,ProxyBypassIgnoredOnDirectConnection)600 TEST_F(DataReductionProxyProtocolTest,
601        ProxyBypassIgnoredOnDirectConnection) {
602   // Verify that a Chrome-Proxy header is ignored when returned from a directly
603   // connected origin server.
604   ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult("DIRECT"));
605 
606   MockRead data_reads[] = {
607     MockRead("HTTP/1.1 200 OK\r\n"
608              "Chrome-Proxy: bypass=0\r\n\r\n"),
609     MockRead("Bypass message"),
610     MockRead(net::SYNCHRONOUS, net::OK),
611   };
612   MockWrite data_writes[] = {
613     MockWrite("GET / HTTP/1.1\r\n"
614               "Host: www.google.com\r\n"
615               "Connection: keep-alive\r\n"
616               "User-Agent:\r\n"
617               "Accept-Encoding: gzip,deflate\r\n\r\n"),
618   };
619   StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
620                                  data_writes, arraysize(data_writes));
621   mock_socket_factory_.AddSocketDataProvider(&data1);
622 
623   TestDelegate d;
624   URLRequest r(GURL("http://www.google.com/"),
625                net::DEFAULT_PRIORITY,
626                &d,
627                context_.get());
628    r.set_method("GET");
629    r.SetLoadFlags(net::LOAD_NORMAL);
630 
631    r.Start();
632    base::RunLoop().Run();
633 
634    EXPECT_EQ(net::URLRequestStatus::SUCCESS, r.status().status());
635    EXPECT_EQ(net::OK, r.status().error());
636 
637   EXPECT_EQ("Bypass message", d.data_received());
638 
639   // We should have no entries in our bad proxy list.
640   TestBadProxies(0, -1, "", "");
641 }
642 
643 }  // namespace data_reduction_proxy
644