xunjieli | 100937eb5 | 2016-09-15 20:09:37 | [diff] [blame] | 1 | // Copyright 2016 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 <memory> |
| 6 | |
| 7 | #include "base/files/file_path.h" |
| 8 | #include "base/macros.h" |
| 9 | #include "base/memory/ptr_util.h" |
| 10 | #include "base/run_loop.h" |
| 11 | #include "base/strings/string_number_conversions.h" |
| 12 | #include "base/strings/stringprintf.h" |
| 13 | #include "net/base/load_timing_info.h" |
| 14 | #include "net/base/network_delegate.h" |
| 15 | #include "net/cert/mock_cert_verifier.h" |
| 16 | #include "net/dns/mapped_host_resolver.h" |
| 17 | #include "net/dns/mock_host_resolver.h" |
| 18 | #include "net/quic/chromium/crypto/proof_source_chromium.h" |
| 19 | #include "net/quic/test_tools/crypto_test_utils.h" |
| 20 | #include "net/test/cert_test_util.h" |
| 21 | #include "net/test/gtest_util.h" |
| 22 | #include "net/test/test_data_directory.h" |
vasilvv | 28270e8f | 2016-12-01 21:38:09 | [diff] [blame^] | 23 | #include "net/tools/quic/quic_http_response_cache.h" |
xunjieli | 100937eb5 | 2016-09-15 20:09:37 | [diff] [blame] | 24 | #include "net/tools/quic/quic_simple_server.h" |
xunjieli | 100937eb5 | 2016-09-15 20:09:37 | [diff] [blame] | 25 | #include "net/url_request/url_request.h" |
| 26 | #include "net/url_request/url_request_test_util.h" |
| 27 | #include "testing/gmock/include/gmock/gmock.h" |
| 28 | #include "testing/gtest/include/gtest/gtest.h" |
| 29 | #include "url/gurl.h" |
| 30 | |
| 31 | namespace net { |
| 32 | |
| 33 | namespace { |
| 34 | |
| 35 | // This must match the certificate used (quic_test.example.com.crt and |
| 36 | // quic_test.example.com.key.pkcs8). |
| 37 | const int kTestServerPort = 6121; |
| 38 | const char kTestServerHost[] = "test.example.com:6121"; |
| 39 | // Used as a simple response from the server. |
| 40 | const char kHelloPath[] = "/hello.txt"; |
| 41 | const char kHelloBodyValue[] = "Hello from QUIC Server"; |
| 42 | const int kHelloStatus = 200; |
| 43 | |
| 44 | class URLRequestQuicTest : public ::testing::Test { |
| 45 | protected: |
| 46 | URLRequestQuicTest() : context_(new TestURLRequestContext(true)) { |
| 47 | StartQuicServer(); |
| 48 | |
| 49 | std::unique_ptr<HttpNetworkSession::Params> params( |
| 50 | new HttpNetworkSession::Params); |
| 51 | CertVerifyResult verify_result; |
| 52 | verify_result.verified_cert = ImportCertFromFile( |
| 53 | GetTestCertsDirectory(), "quic_test.example.com.crt"); |
| 54 | cert_verifier_.AddResultForCertAndHost(verify_result.verified_cert.get(), |
| 55 | "test.example.com", verify_result, |
| 56 | OK); |
| 57 | verify_result.verified_cert = ImportCertFromFile( |
| 58 | GetTestCertsDirectory(), "quic_test_ecc.example.com.crt"); |
| 59 | cert_verifier_.AddResultForCertAndHost(verify_result.verified_cert.get(), |
| 60 | "test.example.com", verify_result, |
| 61 | OK); |
| 62 | // To simplify the test, and avoid the race with the HTTP request, we force |
| 63 | // QUIC for these requests. |
| 64 | params->origins_to_force_quic_on.insert( |
| 65 | HostPortPair::FromString(kTestServerHost)); |
| 66 | params->cert_verifier = &cert_verifier_; |
| 67 | params->enable_quic = true; |
| 68 | params->host_resolver = host_resolver_.get(); |
| 69 | context_->set_http_network_session_params(std::move(params)); |
| 70 | context_->set_cert_verifier(&cert_verifier_); |
| 71 | } |
| 72 | |
| 73 | void TearDown() override { |
| 74 | if (server_) |
| 75 | server_->Shutdown(); |
| 76 | } |
| 77 | |
| 78 | // Sets a NetworkDelegate to use for |context_|. Must be done before Init(). |
| 79 | void SetNetworkDelegate(NetworkDelegate* network_delegate) { |
| 80 | context_->set_network_delegate(network_delegate); |
| 81 | } |
| 82 | |
| 83 | // Initializes the TestURLRequestContext |context_|. |
| 84 | void Init() { context_->Init(); } |
| 85 | |
| 86 | std::unique_ptr<URLRequest> CreateRequest(const GURL& url, |
| 87 | RequestPriority priority, |
| 88 | URLRequest::Delegate* delegate) { |
| 89 | return context_->CreateRequest(url, priority, delegate); |
| 90 | } |
| 91 | |
| 92 | private: |
| 93 | void StartQuicServer() { |
| 94 | // Set up in-memory cache. |
vasilvv | 28270e8f | 2016-12-01 21:38:09 | [diff] [blame^] | 95 | response_cache_.AddSimpleResponse(kTestServerHost, kHelloPath, kHelloStatus, |
| 96 | kHelloBodyValue); |
xunjieli | 100937eb5 | 2016-09-15 20:09:37 | [diff] [blame] | 97 | net::QuicConfig config; |
| 98 | // Set up server certs. |
| 99 | std::unique_ptr<net::ProofSourceChromium> proof_source( |
| 100 | new net::ProofSourceChromium()); |
| 101 | base::FilePath directory = GetTestCertsDirectory(); |
| 102 | CHECK(proof_source->Initialize( |
| 103 | directory.Append(FILE_PATH_LITERAL("quic_test.example.com.crt")), |
| 104 | directory.Append(FILE_PATH_LITERAL("quic_test.example.com.key.pkcs8")), |
| 105 | directory.Append(FILE_PATH_LITERAL("quic_test.example.com.key.sct")))); |
| 106 | server_.reset(new QuicSimpleServer( |
| 107 | test::CryptoTestUtils::ProofSourceForTesting(), config, |
vasilvv | 479f032 | 2016-11-29 16:06:48 | [diff] [blame] | 108 | net::QuicCryptoServerConfig::ConfigOptions(), AllSupportedVersions(), |
vasilvv | 28270e8f | 2016-12-01 21:38:09 | [diff] [blame^] | 109 | &response_cache_)); |
xunjieli | 100937eb5 | 2016-09-15 20:09:37 | [diff] [blame] | 110 | int rv = server_->Listen( |
| 111 | net::IPEndPoint(net::IPAddress::IPv4AllZeros(), kTestServerPort)); |
| 112 | EXPECT_GE(rv, 0) << "Quic server fails to start"; |
| 113 | |
| 114 | std::unique_ptr<MockHostResolver> resolver(new MockHostResolver()); |
| 115 | resolver->rules()->AddRule("test.example.com", "127.0.0.1"); |
| 116 | host_resolver_.reset(new MappedHostResolver(std::move(resolver))); |
| 117 | // Use a mapped host resolver so that request for test.example.com (port 80) |
| 118 | // reach the server running on localhost. |
| 119 | std::string map_rule = "MAP test.example.com test.example.com:" + |
| 120 | base::IntToString(server_->server_address().port()); |
| 121 | EXPECT_TRUE(host_resolver_->AddRuleFromString(map_rule)); |
| 122 | } |
| 123 | |
| 124 | std::unique_ptr<MappedHostResolver> host_resolver_; |
| 125 | std::unique_ptr<QuicSimpleServer> server_; |
| 126 | std::unique_ptr<TestURLRequestContext> context_; |
vasilvv | 28270e8f | 2016-12-01 21:38:09 | [diff] [blame^] | 127 | QuicHttpResponseCache response_cache_; |
xunjieli | 100937eb5 | 2016-09-15 20:09:37 | [diff] [blame] | 128 | MockCertVerifier cert_verifier_; |
| 129 | }; |
| 130 | |
| 131 | // A URLRequest::Delegate that checks LoadTimingInfo when response headers are |
| 132 | // received. |
| 133 | class CheckLoadTimingDelegate : public TestDelegate { |
| 134 | public: |
| 135 | CheckLoadTimingDelegate(bool session_reused) |
| 136 | : session_reused_(session_reused) {} |
| 137 | void OnResponseStarted(URLRequest* request, int error) override { |
| 138 | TestDelegate::OnResponseStarted(request, error); |
| 139 | LoadTimingInfo load_timing_info; |
| 140 | request->GetLoadTimingInfo(&load_timing_info); |
| 141 | assertLoadTimingValid(load_timing_info, session_reused_); |
| 142 | } |
| 143 | |
| 144 | private: |
| 145 | void assertLoadTimingValid(const LoadTimingInfo& load_timing_info, |
| 146 | bool session_reused) { |
| 147 | EXPECT_EQ(session_reused, load_timing_info.socket_reused); |
| 148 | |
| 149 | // If |session_reused| is true, these fields should all be null, non-null |
| 150 | // otherwise. |
| 151 | EXPECT_EQ(session_reused, |
| 152 | load_timing_info.connect_timing.connect_start.is_null()); |
| 153 | EXPECT_EQ(session_reused, |
| 154 | load_timing_info.connect_timing.connect_end.is_null()); |
| 155 | EXPECT_EQ(session_reused, |
| 156 | load_timing_info.connect_timing.ssl_start.is_null()); |
| 157 | EXPECT_EQ(session_reused, |
| 158 | load_timing_info.connect_timing.ssl_end.is_null()); |
| 159 | EXPECT_EQ(load_timing_info.connect_timing.connect_start, |
| 160 | load_timing_info.connect_timing.ssl_start); |
| 161 | EXPECT_EQ(load_timing_info.connect_timing.connect_end, |
| 162 | load_timing_info.connect_timing.ssl_end); |
| 163 | EXPECT_EQ(session_reused, |
| 164 | load_timing_info.connect_timing.dns_start.is_null()); |
| 165 | EXPECT_EQ(session_reused, |
| 166 | load_timing_info.connect_timing.dns_end.is_null()); |
| 167 | } |
| 168 | |
| 169 | bool session_reused_; |
| 170 | |
| 171 | DISALLOW_COPY_AND_ASSIGN(CheckLoadTimingDelegate); |
| 172 | }; |
| 173 | |
| 174 | // A TestNetworkDelegate that invokes |all_requests_completed_callback| when |
| 175 | // |num_expected_requests| requests are completed. |
| 176 | class WaitForCompletionNetworkDelegate : public net::TestNetworkDelegate { |
| 177 | public: |
| 178 | WaitForCompletionNetworkDelegate( |
| 179 | const base::Closure& all_requests_completed_callback, |
| 180 | size_t num_expected_requests) |
| 181 | : all_requests_completed_callback_(all_requests_completed_callback), |
| 182 | num_expected_requests_(num_expected_requests) {} |
| 183 | |
| 184 | void OnCompleted(URLRequest* request, bool started, int net_error) override { |
| 185 | net::TestNetworkDelegate::OnCompleted(request, started, net_error); |
| 186 | num_expected_requests_--; |
| 187 | if (num_expected_requests_ == 0) |
| 188 | all_requests_completed_callback_.Run(); |
| 189 | } |
| 190 | |
| 191 | private: |
| 192 | const base::Closure all_requests_completed_callback_; |
| 193 | size_t num_expected_requests_; |
| 194 | DISALLOW_COPY_AND_ASSIGN(WaitForCompletionNetworkDelegate); |
| 195 | }; |
| 196 | |
| 197 | } // namespace |
| 198 | |
| 199 | TEST_F(URLRequestQuicTest, TestGetRequest) { |
| 200 | Init(); |
| 201 | CheckLoadTimingDelegate delegate(false); |
| 202 | std::string url = |
| 203 | base::StringPrintf("https://%s%s", kTestServerHost, kHelloPath); |
| 204 | std::unique_ptr<URLRequest> request = |
| 205 | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate); |
| 206 | |
| 207 | request->Start(); |
| 208 | ASSERT_TRUE(request->is_pending()); |
| 209 | base::RunLoop().Run(); |
| 210 | |
| 211 | EXPECT_TRUE(request->status().is_success()); |
| 212 | EXPECT_EQ(kHelloBodyValue, delegate.data_received()); |
| 213 | } |
| 214 | |
| 215 | // Tests that if two requests use the same QUIC session, the second request |
| 216 | // should not have |LoadTimingInfo::connect_timing|. |
| 217 | TEST_F(URLRequestQuicTest, TestTwoRequests) { |
| 218 | base::RunLoop run_loop; |
| 219 | WaitForCompletionNetworkDelegate network_delegate( |
| 220 | run_loop.QuitClosure(), /*num_expected_requests=*/2); |
| 221 | SetNetworkDelegate(&network_delegate); |
| 222 | Init(); |
| 223 | CheckLoadTimingDelegate delegate(false); |
| 224 | delegate.set_quit_on_complete(false); |
| 225 | std::string url = |
| 226 | base::StringPrintf("https://%s%s", kTestServerHost, kHelloPath); |
| 227 | std::unique_ptr<URLRequest> request = |
| 228 | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate); |
| 229 | |
| 230 | CheckLoadTimingDelegate delegate2(true); |
| 231 | delegate2.set_quit_on_complete(false); |
| 232 | std::unique_ptr<URLRequest> request2 = |
| 233 | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate2); |
| 234 | request->Start(); |
| 235 | request2->Start(); |
| 236 | ASSERT_TRUE(request->is_pending()); |
| 237 | ASSERT_TRUE(request2->is_pending()); |
| 238 | run_loop.Run(); |
| 239 | |
| 240 | EXPECT_TRUE(request->status().is_success()); |
| 241 | EXPECT_TRUE(request2->status().is_success()); |
| 242 | EXPECT_EQ(kHelloBodyValue, delegate.data_received()); |
| 243 | EXPECT_EQ(kHelloBodyValue, delegate2.data_received()); |
| 244 | } |
| 245 | |
| 246 | } // namespace net |