1// Copyright (c) 2012 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 "net/http/http_network_layer.h"
6
7#include "base/basictypes.h"
8#include "base/strings/stringprintf.h"
9#include "net/base/net_log.h"
10#include "net/cert/mock_cert_verifier.h"
11#include "net/dns/mock_host_resolver.h"
12#include "net/http/http_network_session.h"
13#include "net/http/http_server_properties_impl.h"
14#include "net/http/http_transaction_test_util.h"
15#include "net/http/transport_security_state.h"
16#include "net/proxy/proxy_service.h"
17#include "net/socket/socket_test_util.h"
18#include "net/spdy/spdy_session_pool.h"
19#include "net/ssl/ssl_config_service_defaults.h"
20#include "testing/gtest/include/gtest/gtest.h"
21#include "testing/platform_test.h"
22
23namespace net {
24
25namespace {
26
27class HttpNetworkLayerTest : public PlatformTest {
28 protected:
29  HttpNetworkLayerTest() : ssl_config_service_(new SSLConfigServiceDefaults) {}
30
31  virtual void SetUp() {
32    ConfigureTestDependencies(ProxyService::CreateDirect());
33  }
34
35  void ConfigureTestDependencies(ProxyService* proxy_service) {
36    cert_verifier_.reset(new MockCertVerifier);
37    transport_security_state_.reset(new TransportSecurityState);
38    proxy_service_.reset(proxy_service);
39    HttpNetworkSession::Params session_params;
40    session_params.client_socket_factory = &mock_socket_factory_;
41    session_params.host_resolver = &host_resolver_;
42    session_params.cert_verifier = cert_verifier_.get();
43    session_params.transport_security_state = transport_security_state_.get();
44    session_params.proxy_service = proxy_service_.get();
45    session_params.ssl_config_service = ssl_config_service_.get();
46    session_params.http_server_properties =
47        http_server_properties_.GetWeakPtr();
48    network_session_ = new HttpNetworkSession(session_params);
49    factory_.reset(new HttpNetworkLayer(network_session_.get()));
50  }
51
52  void ExecuteRequestExpectingContentAndHeader(const std::string& method,
53                                               const std::string& content,
54                                               const std::string& header,
55                                               const std::string& value) {
56    TestCompletionCallback callback;
57
58    HttpRequestInfo request_info;
59    request_info.url = GURL("http://www.google.com/");
60    request_info.method = method;
61    request_info.load_flags = LOAD_NORMAL;
62
63    scoped_ptr<HttpTransaction> trans;
64    int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
65    EXPECT_EQ(OK, rv);
66
67    rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
68    if (rv == ERR_IO_PENDING)
69      rv = callback.WaitForResult();
70    ASSERT_EQ(OK, rv);
71
72    std::string contents;
73    rv = ReadTransaction(trans.get(), &contents);
74    EXPECT_EQ(OK, rv);
75    EXPECT_EQ(content, contents);
76
77    if (!header.empty()) {
78      // We also have a server header here that isn't set by the proxy.
79      EXPECT_TRUE(trans->GetResponseInfo()->headers->HasHeaderValue(
80          header, value));
81    }
82  }
83
84  // Check that |proxy_count| proxies are in the retry list.
85  // These will be, in order, |bad_proxy| and |bad_proxy2|".
86  void TestBadProxies(unsigned int proxy_count, const std::string& bad_proxy,
87                      const std::string& bad_proxy2) {
88    const ProxyRetryInfoMap& retry_info = proxy_service_->proxy_retry_info();
89    ASSERT_EQ(proxy_count, retry_info.size());
90    if (proxy_count > 0)
91      ASSERT_TRUE(retry_info.find(bad_proxy) != retry_info.end());
92    if (proxy_count > 1)
93      ASSERT_TRUE(retry_info.find(bad_proxy2) != retry_info.end());
94  }
95
96  // Simulates a request through a proxy which returns a bypass, which is then
97  // retried through a second proxy that doesn't bypass.
98  // Checks that the expected requests were issued, the expected content was
99  // received, and the first proxy |bad_proxy| was marked as bad.
100  void TestProxyFallback(const std::string& bad_proxy) {
101    MockRead data_reads[] = {
102      MockRead("HTTP/1.1 200 OK\r\n"
103               "Chrome-Proxy: bypass=0\r\n\r\n"),
104      MockRead("Bypass message"),
105      MockRead(SYNCHRONOUS, OK),
106    };
107    TestProxyFallbackWithMockReads(bad_proxy, "", data_reads,
108                                   arraysize(data_reads), 1u);
109  }
110
111  void TestProxyFallbackWithMockReads(const std::string& bad_proxy,
112                                      const std::string& bad_proxy2,
113                                      MockRead data_reads[],
114                                      int data_reads_size,
115                                      unsigned int expected_retry_info_size) {
116    TestProxyFallbackByMethodWithMockReads(bad_proxy, bad_proxy2, data_reads,
117                                           data_reads_size, "GET", "content",
118                                           true, expected_retry_info_size);
119  }
120
121  void TestProxyFallbackByMethodWithMockReads(
122      const std::string& bad_proxy,
123      const std::string& bad_proxy2,
124      MockRead data_reads[],
125      int data_reads_size,
126      std::string method,
127      std::string content,
128      bool retry_expected,
129      unsigned int expected_retry_info_size) {
130    std::string trailer =
131        (method == "HEAD" || method == "PUT" || method == "POST") ?
132        "Content-Length: 0\r\n\r\n" : "\r\n";
133    std::string request =
134        base::StringPrintf("%s http://www.google.com/ HTTP/1.1\r\n"
135                           "Host: www.google.com\r\n"
136                           "Proxy-Connection: keep-alive\r\n"
137                           "%s", method.c_str(), trailer.c_str());
138
139    MockWrite data_writes[] = {
140      MockWrite(request.c_str()),
141    };
142
143    StaticSocketDataProvider data1(data_reads, data_reads_size,
144                                  data_writes, arraysize(data_writes));
145    mock_socket_factory_.AddSocketDataProvider(&data1);
146
147    // Second data provider returns the expected content.
148    MockRead data_reads2[3];
149    size_t data_reads2_index = 0;
150    data_reads2[data_reads2_index++] = MockRead("HTTP/1.0 200 OK\r\n"
151                                                "Server: not-proxy\r\n\r\n");
152    if (!content.empty())
153      data_reads2[data_reads2_index++] = MockRead(content.c_str());
154    data_reads2[data_reads2_index++] = MockRead(SYNCHRONOUS, OK);
155
156    MockWrite data_writes2[] = {
157      MockWrite(request.c_str()),
158    };
159    StaticSocketDataProvider data2(data_reads2, data_reads2_index,
160                                  data_writes2, arraysize(data_writes2));
161    mock_socket_factory_.AddSocketDataProvider(&data2);
162
163    // Expect that we get "content" and not "Bypass message", and that there's
164    // a "not-proxy" "Server:" header in the final response.
165    if (retry_expected) {
166      ExecuteRequestExpectingContentAndHeader(method, content,
167                                              "server", "not-proxy");
168    } else {
169      ExecuteRequestExpectingContentAndHeader(method, content, "", "");
170    }
171
172    // We should also observe the bad proxy in the retry list.
173    TestBadProxies(expected_retry_info_size, bad_proxy, bad_proxy2);
174  }
175
176  // Simulates a request through a proxy which returns a bypass, which is then
177  // retried through a direct connection to the origin site.
178  // Checks that the expected requests were issued, the expected content was
179  // received, and the proxy |bad_proxy| was marked as bad.
180  void TestProxyFallbackToDirect(const std::string& bad_proxy) {
181    MockRead data_reads[] = {
182      MockRead("HTTP/1.1 200 OK\r\n"
183               "Chrome-Proxy: bypass=0\r\n\r\n"),
184      MockRead("Bypass message"),
185      MockRead(SYNCHRONOUS, OK),
186    };
187    MockWrite data_writes[] = {
188      MockWrite("GET http://www.google.com/ HTTP/1.1\r\n"
189                "Host: www.google.com\r\n"
190                "Proxy-Connection: keep-alive\r\n\r\n"),
191    };
192    StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
193                                  data_writes, arraysize(data_writes));
194    mock_socket_factory_.AddSocketDataProvider(&data1);
195
196    // Second data provider returns the expected content.
197    MockRead data_reads2[] = {
198      MockRead("HTTP/1.0 200 OK\r\n"
199               "Server: not-proxy\r\n\r\n"),
200      MockRead("content"),
201      MockRead(SYNCHRONOUS, OK),
202    };
203    MockWrite data_writes2[] = {
204      MockWrite("GET / HTTP/1.1\r\n"
205                "Host: www.google.com\r\n"
206                "Connection: keep-alive\r\n\r\n"),
207    };
208    StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
209                                   data_writes2, arraysize(data_writes2));
210    mock_socket_factory_.AddSocketDataProvider(&data2);
211
212    // Expect that we get "content" and not "Bypass message", and that there's
213    // a "not-proxy" "Server:" header in the final response.
214    ExecuteRequestExpectingContentAndHeader("GET", "content",
215                                            "server", "not-proxy");
216
217    // We should also observe the bad proxy in the retry list.
218    TestBadProxies(1u, bad_proxy, "");
219  }
220
221  // Simulates a request through a proxy which returns a bypass, under a
222  // configuration where there is no valid bypass. |proxy_count| proxies
223  // are expected to be configured.
224  // Checks that the expected requests were issued, the bypass message was the
225  // final received content,  and all proxies were marked as bad.
226  void TestProxyFallbackFail(unsigned int proxy_count,
227                             const std::string& bad_proxy,
228                             const std::string& bad_proxy2) {
229    MockRead data_reads[] = {
230      MockRead("HTTP/1.1 200 OK\r\n"
231               "Chrome-Proxy: bypass=0\r\n\r\n"),
232      MockRead("Bypass message"),
233      MockRead(SYNCHRONOUS, OK),
234    };
235    MockWrite data_writes[] = {
236      MockWrite("GET http://www.google.com/ HTTP/1.1\r\n"
237                "Host: www.google.com\r\n"
238                "Proxy-Connection: keep-alive\r\n\r\n"),
239    };
240    StaticSocketDataProvider data1(data_reads, arraysize(data_reads),
241                                   data_writes, arraysize(data_writes));
242    StaticSocketDataProvider data2(data_reads, arraysize(data_reads),
243                                   data_writes, arraysize(data_writes));
244
245    mock_socket_factory_.AddSocketDataProvider(&data1);
246    if (proxy_count > 1)
247      mock_socket_factory_.AddSocketDataProvider(&data2);
248
249    // Expect that we get "Bypass message", and not "content"..
250    ExecuteRequestExpectingContentAndHeader("GET", "Bypass message", "", "");
251
252    // We should also observe the bad proxy or proxies in the retry list.
253    TestBadProxies(proxy_count, bad_proxy, bad_proxy2);
254  }
255
256  MockClientSocketFactory mock_socket_factory_;
257  MockHostResolver host_resolver_;
258  scoped_ptr<CertVerifier> cert_verifier_;
259  scoped_ptr<TransportSecurityState> transport_security_state_;
260  scoped_ptr<ProxyService> proxy_service_;
261  const scoped_refptr<SSLConfigService> ssl_config_service_;
262  scoped_refptr<HttpNetworkSession> network_session_;
263  scoped_ptr<HttpNetworkLayer> factory_;
264  HttpServerPropertiesImpl http_server_properties_;
265};
266
267TEST_F(HttpNetworkLayerTest, CreateAndDestroy) {
268  scoped_ptr<HttpTransaction> trans;
269  int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
270  EXPECT_EQ(OK, rv);
271  EXPECT_TRUE(trans.get() != NULL);
272}
273
274TEST_F(HttpNetworkLayerTest, Suspend) {
275  scoped_ptr<HttpTransaction> trans;
276  int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
277  EXPECT_EQ(OK, rv);
278
279  trans.reset();
280
281  factory_->OnSuspend();
282
283  rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
284  EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, rv);
285
286  ASSERT_TRUE(trans == NULL);
287
288  factory_->OnResume();
289
290  rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
291  EXPECT_EQ(OK, rv);
292}
293
294TEST_F(HttpNetworkLayerTest, GET) {
295  MockRead data_reads[] = {
296    MockRead("HTTP/1.0 200 OK\r\n\r\n"),
297    MockRead("hello world"),
298    MockRead(SYNCHRONOUS, OK),
299  };
300  MockWrite data_writes[] = {
301    MockWrite("GET / HTTP/1.1\r\n"
302              "Host: www.google.com\r\n"
303              "Connection: keep-alive\r\n"
304              "User-Agent: Foo/1.0\r\n\r\n"),
305  };
306  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
307                                data_writes, arraysize(data_writes));
308  mock_socket_factory_.AddSocketDataProvider(&data);
309
310  TestCompletionCallback callback;
311
312  HttpRequestInfo request_info;
313  request_info.url = GURL("http://www.google.com/");
314  request_info.method = "GET";
315  request_info.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
316                                       "Foo/1.0");
317  request_info.load_flags = LOAD_NORMAL;
318
319  scoped_ptr<HttpTransaction> trans;
320  int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
321  EXPECT_EQ(OK, rv);
322
323  rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
324  rv = callback.GetResult(rv);
325  ASSERT_EQ(OK, rv);
326
327  std::string contents;
328  rv = ReadTransaction(trans.get(), &contents);
329  EXPECT_EQ(OK, rv);
330  EXPECT_EQ("hello world", contents);
331}
332
333TEST_F(HttpNetworkLayerTest, NetworkVerified) {
334  MockRead data_reads[] = {
335    MockRead("HTTP/1.0 200 OK\r\n\r\n"),
336    MockRead("hello world"),
337    MockRead(SYNCHRONOUS, OK),
338  };
339  MockWrite data_writes[] = {
340    MockWrite("GET / HTTP/1.1\r\n"
341              "Host: www.google.com\r\n"
342              "Connection: keep-alive\r\n"
343              "User-Agent: Foo/1.0\r\n\r\n"),
344  };
345  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
346                                data_writes, arraysize(data_writes));
347  mock_socket_factory_.AddSocketDataProvider(&data);
348
349  TestCompletionCallback callback;
350
351  HttpRequestInfo request_info;
352  request_info.url = GURL("http://www.google.com/");
353  request_info.method = "GET";
354  request_info.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
355                                       "Foo/1.0");
356  request_info.load_flags = LOAD_NORMAL;
357
358  scoped_ptr<HttpTransaction> trans;
359  int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
360  EXPECT_EQ(OK, rv);
361
362  rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
363  ASSERT_EQ(OK, callback.GetResult(rv));
364
365  EXPECT_TRUE(trans->GetResponseInfo()->network_accessed);
366}
367
368TEST_F(HttpNetworkLayerTest, NetworkUnVerified) {
369  MockRead data_reads[] = {
370    MockRead(ASYNC, ERR_CONNECTION_RESET),
371  };
372  MockWrite data_writes[] = {
373    MockWrite("GET / HTTP/1.1\r\n"
374              "Host: www.google.com\r\n"
375              "Connection: keep-alive\r\n"
376              "User-Agent: Foo/1.0\r\n\r\n"),
377  };
378  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
379                                data_writes, arraysize(data_writes));
380  mock_socket_factory_.AddSocketDataProvider(&data);
381
382  TestCompletionCallback callback;
383
384  HttpRequestInfo request_info;
385  request_info.url = GURL("http://www.google.com/");
386  request_info.method = "GET";
387  request_info.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
388                                       "Foo/1.0");
389  request_info.load_flags = LOAD_NORMAL;
390
391  scoped_ptr<HttpTransaction> trans;
392  int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans);
393  EXPECT_EQ(OK, rv);
394
395  rv = trans->Start(&request_info, callback.callback(), BoundNetLog());
396  ASSERT_EQ(ERR_CONNECTION_RESET, callback.GetResult(rv));
397
398  // If the response info is null, that means that any consumer won't
399  // see the network accessed bit set.
400  EXPECT_EQ(NULL, trans->GetResponseInfo());
401}
402
403}  // namespace
404
405}  // namespace net
406