|  | // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "net/base/load_timing_info.h" | 
|  | #include "net/base/network_delegate.h" | 
|  | #include "net/cert/mock_cert_verifier.h" | 
|  | #include "net/dns/mapped_host_resolver.h" | 
|  | #include "net/dns/mock_host_resolver.h" | 
|  | #include "net/log/net_log_event_type.h" | 
|  | #include "net/log/test_net_log.h" | 
|  | #include "net/log/test_net_log_entry.h" | 
|  | #include "net/quic/crypto/proof_source_chromium.h" | 
|  | #include "net/test/cert_test_util.h" | 
|  | #include "net/test/gtest_util.h" | 
|  | #include "net/test/test_data_directory.h" | 
|  | #include "net/test/test_with_scoped_task_environment.h" | 
|  | #include "net/third_party/quic/core/quic_dispatcher.h" | 
|  | #include "net/third_party/quic/test_tools/crypto_test_utils.h" | 
|  | #include "net/third_party/quic/tools/quic_memory_cache_backend.h" | 
|  | #include "net/third_party/quic/tools/quic_simple_dispatcher.h" | 
|  | #include "net/tools/quic/quic_simple_server.h" | 
|  | #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" | 
|  | #include "net/url_request/url_request.h" | 
|  | #include "net/url_request/url_request_test_util.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "url/gurl.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // This must match the certificate used (quic-chain.pem and quic-leaf-cert.key). | 
|  | const char kTestServerHost[] = "test.example.com"; | 
|  | // Used as a simple response from the server. | 
|  | const char kHelloPath[] = "/hello.txt"; | 
|  | const char kHelloBodyValue[] = "Hello from QUIC Server"; | 
|  | const int kHelloStatus = 200; | 
|  |  | 
|  | class URLRequestQuicTest : public TestWithScopedTaskEnvironment { | 
|  | protected: | 
|  | URLRequestQuicTest() : context_(new TestURLRequestContext(true)) { | 
|  | StartQuicServer(); | 
|  |  | 
|  | std::unique_ptr<HttpNetworkSession::Params> params( | 
|  | new HttpNetworkSession::Params); | 
|  | CertVerifyResult verify_result; | 
|  | verify_result.verified_cert = ImportCertFromFile( | 
|  | GetTestCertsDirectory(), "quic-chain.pem"); | 
|  | cert_verifier_.AddResultForCertAndHost(verify_result.verified_cert.get(), | 
|  | "test.example.com", verify_result, | 
|  | OK); | 
|  | // To simplify the test, and avoid the race with the HTTP request, we force | 
|  | // QUIC for these requests. | 
|  | params->origins_to_force_quic_on.insert(HostPortPair(kTestServerHost, 443)); | 
|  | params->enable_quic = true; | 
|  | params->enable_server_push_cancellation = true; | 
|  | context_->set_host_resolver(host_resolver_.get()); | 
|  | context_->set_http_network_session_params(std::move(params)); | 
|  | context_->set_cert_verifier(&cert_verifier_); | 
|  | context_->set_net_log(&net_log_); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | if (server_) { | 
|  | server_->Shutdown(); | 
|  | // If possible, deliver the conncetion close packet to the client before | 
|  | // destruct the TestURLRequestContext. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Sets a NetworkDelegate to use for |context_|. Must be done before Init(). | 
|  | void SetNetworkDelegate(NetworkDelegate* network_delegate) { | 
|  | context_->set_network_delegate(network_delegate); | 
|  | } | 
|  |  | 
|  | // Initializes the TestURLRequestContext |context_|. | 
|  | void Init() { context_->Init(); } | 
|  |  | 
|  | std::unique_ptr<URLRequest> CreateRequest(const GURL& url, | 
|  | RequestPriority priority, | 
|  | URLRequest::Delegate* delegate) { | 
|  | return context_->CreateRequest(url, priority, delegate, | 
|  | TRAFFIC_ANNOTATION_FOR_TESTS); | 
|  | } | 
|  |  | 
|  | void ExtractNetLog(NetLogEventType type, | 
|  | TestNetLogEntry::List* entry_list) const { | 
|  | TestNetLogEntry::List entries; | 
|  | net_log_.GetEntries(&entries); | 
|  |  | 
|  | for (const auto& entry : entries) { | 
|  | if (entry.type == type) | 
|  | entry_list->push_back(entry); | 
|  | } | 
|  | } | 
|  |  | 
|  | unsigned int GetRstErrorCountReceivedByServer( | 
|  | quic::QuicRstStreamErrorCode error_code) const { | 
|  | return (static_cast<quic::QuicSimpleDispatcher*>(server_->dispatcher())) | 
|  | ->GetRstErrorCount(error_code); | 
|  | } | 
|  |  | 
|  | static const NetLogSource FindPushUrlSource( | 
|  | const TestNetLogEntry::List& entries, | 
|  | const std::string& push_url) { | 
|  | std::string entry_push_url; | 
|  | for (const auto& entry : entries) { | 
|  | if (entry.phase == NetLogEventPhase::BEGIN && | 
|  | entry.source.type == | 
|  | NetLogSourceType::SERVER_PUSH_LOOKUP_TRANSACTION && | 
|  | entry.GetStringValue("push_url", &entry_push_url) && | 
|  | entry_push_url == push_url) { | 
|  | return entry.source; | 
|  | } | 
|  | } | 
|  | return NetLogSource(); | 
|  | } | 
|  |  | 
|  | static const TestNetLogEntry* FindEndBySource( | 
|  | const TestNetLogEntry::List& entries, | 
|  | const NetLogSource& source) { | 
|  | for (const auto& entry : entries) { | 
|  | if (entry.phase == NetLogEventPhase::END && | 
|  | entry.source.type == source.type && entry.source.id == source.id) | 
|  | return &entry; | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void StartQuicServer() { | 
|  | // Set up in-memory cache. | 
|  | memory_cache_backend_.AddSimpleResponse(kTestServerHost, kHelloPath, | 
|  | kHelloStatus, kHelloBodyValue); | 
|  | memory_cache_backend_.InitializeBackend(ServerPushCacheDirectory()); | 
|  | quic::QuicConfig config; | 
|  | // Set up server certs. | 
|  | std::unique_ptr<net::ProofSourceChromium> proof_source( | 
|  | new net::ProofSourceChromium()); | 
|  | base::FilePath directory = GetTestCertsDirectory(); | 
|  | CHECK(proof_source->Initialize( | 
|  | directory.Append(FILE_PATH_LITERAL("quic-chain.pem")), | 
|  | directory.Append(FILE_PATH_LITERAL("quic-leaf-cert.key")), | 
|  | base::FilePath())); | 
|  | server_.reset(new QuicSimpleServer( | 
|  | quic::test::crypto_test_utils::ProofSourceForTesting(), config, | 
|  | quic::QuicCryptoServerConfig::ConfigOptions(), | 
|  | quic::AllSupportedVersions(), &memory_cache_backend_)); | 
|  | int rv = | 
|  | server_->Listen(net::IPEndPoint(net::IPAddress::IPv4AllZeros(), 0)); | 
|  | EXPECT_GE(rv, 0) << "Quic server fails to start"; | 
|  |  | 
|  | std::unique_ptr<MockHostResolver> resolver(new MockHostResolver()); | 
|  | resolver->rules()->AddRule("test.example.com", "127.0.0.1"); | 
|  | host_resolver_.reset(new MappedHostResolver(std::move(resolver))); | 
|  | // Use a mapped host resolver so that request for test.example.com | 
|  | // reach the server running on localhost. | 
|  | std::string map_rule = "MAP test.example.com test.example.com:" + | 
|  | base::IntToString(server_->server_address().port()); | 
|  | EXPECT_TRUE(host_resolver_->AddRuleFromString(map_rule)); | 
|  | } | 
|  |  | 
|  | std::string ServerPushCacheDirectory() { | 
|  | base::FilePath path; | 
|  | base::PathService::Get(base::DIR_TEST_DATA, &path); | 
|  | path = path.AppendASCII("net").AppendASCII("data").AppendASCII( | 
|  | "quic_http_response_cache_data_with_push"); | 
|  | // The file path is known to be an ascii string. | 
|  | return path.MaybeAsASCII(); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<MappedHostResolver> host_resolver_; | 
|  | std::unique_ptr<QuicSimpleServer> server_; | 
|  | std::unique_ptr<TestURLRequestContext> context_; | 
|  | TestNetLog net_log_; | 
|  | quic::QuicMemoryCacheBackend memory_cache_backend_; | 
|  | MockCertVerifier cert_verifier_; | 
|  | }; | 
|  |  | 
|  | // A URLRequest::Delegate that checks LoadTimingInfo when response headers are | 
|  | // received. | 
|  | class CheckLoadTimingDelegate : public TestDelegate { | 
|  | public: | 
|  | CheckLoadTimingDelegate(bool session_reused) | 
|  | : session_reused_(session_reused) {} | 
|  | void OnResponseStarted(URLRequest* request, int error) override { | 
|  | TestDelegate::OnResponseStarted(request, error); | 
|  | LoadTimingInfo load_timing_info; | 
|  | request->GetLoadTimingInfo(&load_timing_info); | 
|  | assertLoadTimingValid(load_timing_info, session_reused_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | void assertLoadTimingValid(const LoadTimingInfo& load_timing_info, | 
|  | bool session_reused) { | 
|  | EXPECT_EQ(session_reused, load_timing_info.socket_reused); | 
|  |  | 
|  | // If |session_reused| is true, these fields should all be null, non-null | 
|  | // otherwise. | 
|  | EXPECT_EQ(session_reused, | 
|  | load_timing_info.connect_timing.connect_start.is_null()); | 
|  | EXPECT_EQ(session_reused, | 
|  | load_timing_info.connect_timing.connect_end.is_null()); | 
|  | EXPECT_EQ(session_reused, | 
|  | load_timing_info.connect_timing.ssl_start.is_null()); | 
|  | EXPECT_EQ(session_reused, | 
|  | load_timing_info.connect_timing.ssl_end.is_null()); | 
|  | EXPECT_EQ(load_timing_info.connect_timing.connect_start, | 
|  | load_timing_info.connect_timing.ssl_start); | 
|  | EXPECT_EQ(load_timing_info.connect_timing.connect_end, | 
|  | load_timing_info.connect_timing.ssl_end); | 
|  | EXPECT_EQ(session_reused, | 
|  | load_timing_info.connect_timing.dns_start.is_null()); | 
|  | EXPECT_EQ(session_reused, | 
|  | load_timing_info.connect_timing.dns_end.is_null()); | 
|  | } | 
|  |  | 
|  | bool session_reused_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(CheckLoadTimingDelegate); | 
|  | }; | 
|  |  | 
|  | // A TestNetworkDelegate that invokes |all_requests_completed_callback| when | 
|  | // |num_expected_requests| requests are completed. | 
|  | class WaitForCompletionNetworkDelegate : public net::TestNetworkDelegate { | 
|  | public: | 
|  | WaitForCompletionNetworkDelegate( | 
|  | const base::Closure& all_requests_completed_callback, | 
|  | size_t num_expected_requests) | 
|  | : all_requests_completed_callback_(all_requests_completed_callback), | 
|  | num_expected_requests_(num_expected_requests) {} | 
|  |  | 
|  | void OnCompleted(URLRequest* request, bool started, int net_error) override { | 
|  | net::TestNetworkDelegate::OnCompleted(request, started, net_error); | 
|  | num_expected_requests_--; | 
|  | if (num_expected_requests_ == 0) | 
|  | all_requests_completed_callback_.Run(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const base::Closure all_requests_completed_callback_; | 
|  | size_t num_expected_requests_; | 
|  | DISALLOW_COPY_AND_ASSIGN(WaitForCompletionNetworkDelegate); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(URLRequestQuicTest, TestGetRequest) { | 
|  | Init(); | 
|  | CheckLoadTimingDelegate delegate(false); | 
|  | std::string url = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, kHelloPath); | 
|  | std::unique_ptr<URLRequest> request = | 
|  | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate); | 
|  |  | 
|  | request->Start(); | 
|  | ASSERT_TRUE(request->is_pending()); | 
|  | delegate.RunUntilComplete(); | 
|  |  | 
|  | EXPECT_TRUE(request->status().is_success()); | 
|  | EXPECT_EQ(kHelloBodyValue, delegate.data_received()); | 
|  | } | 
|  |  | 
|  | #if !defined(HTTP_CACHE_DISABLED_FOR_STARBOARD) | 
|  | TEST_F(URLRequestQuicTest, CancelPushIfCached_SomeCached) { | 
|  | Init(); | 
|  |  | 
|  | // Send a request to the pushed url: /kitten-1.jpg to pull the resource into | 
|  | // cache. | 
|  | CheckLoadTimingDelegate delegate_0(false); | 
|  | std::string url_0 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/kitten-1.jpg"); | 
|  | std::unique_ptr<URLRequest> request_0 = | 
|  | CreateRequest(GURL(url_0), DEFAULT_PRIORITY, &delegate_0); | 
|  |  | 
|  | request_0->Start(); | 
|  | ASSERT_TRUE(request_0->is_pending()); | 
|  |  | 
|  | // Spin the message loop until the client receives the response for the first | 
|  | // request. | 
|  | do { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } while (request_0->status().is_io_pending()); | 
|  | EXPECT_TRUE(request_0->status().is_success()); | 
|  |  | 
|  | // Send a request to /index2.html which pushes /kitten-1.jpg and /favicon.ico. | 
|  | // Should cancel push for /kitten-1.jpg. | 
|  | CheckLoadTimingDelegate delegate(true); | 
|  | std::string url = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/index2.html"); | 
|  | std::unique_ptr<URLRequest> request = | 
|  | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate); | 
|  |  | 
|  | request->Start(); | 
|  | ASSERT_TRUE(request->is_pending()); | 
|  |  | 
|  | // Spin the message loop until the client receives the response for the second | 
|  | // request. | 
|  | do { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } while (request->status().is_io_pending()); | 
|  | EXPECT_TRUE(request->status().is_success()); | 
|  |  | 
|  | // Extract net logs on client side to verify push lookup transactions. | 
|  | net::TestNetLogEntry::List entries; | 
|  | ExtractNetLog(NetLogEventType::SERVER_PUSH_LOOKUP_TRANSACTION, &entries); | 
|  |  | 
|  | ASSERT_EQ(4u, entries.size()); | 
|  |  | 
|  | std::string value; | 
|  | int net_error; | 
|  | std::string push_url_1 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/kitten-1.jpg"); | 
|  | std::string push_url_2 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/favicon.ico"); | 
|  |  | 
|  | const NetLogSource source_1 = FindPushUrlSource(entries, push_url_1); | 
|  | EXPECT_TRUE(source_1.IsValid()); | 
|  |  | 
|  | // No net error code for this lookup transaction, the push is found. | 
|  | const TestNetLogEntry* end_entry_1 = FindEndBySource(entries, source_1); | 
|  | EXPECT_FALSE(end_entry_1->params); | 
|  | EXPECT_FALSE(end_entry_1->GetIntegerValue("net_error", &net_error)); | 
|  |  | 
|  | const NetLogSource source_2 = FindPushUrlSource(entries, push_url_2); | 
|  | EXPECT_TRUE(source_2.IsValid()); | 
|  | EXPECT_NE(source_1.id, source_2.id); | 
|  |  | 
|  | // Net error code -400 is found for this lookup transaction, the push is not | 
|  | // found in the cache. | 
|  | const TestNetLogEntry* end_entry_2 = FindEndBySource(entries, source_2); | 
|  | EXPECT_TRUE(end_entry_2->params); | 
|  | EXPECT_TRUE(end_entry_2->GetIntegerValue("net_error", &net_error)); | 
|  | EXPECT_EQ(net_error, -400); | 
|  |  | 
|  | // Verify the reset error count received on the server side. | 
|  | EXPECT_LE(1u, GetRstErrorCountReceivedByServer(quic::QUIC_STREAM_CANCELLED)); | 
|  | } | 
|  |  | 
|  | TEST_F(URLRequestQuicTest, CancelPushIfCached_AllCached) { | 
|  | Init(); | 
|  |  | 
|  | // Send a request to the pushed url: /kitten-1.jpg to pull the resource into | 
|  | // cache. | 
|  | CheckLoadTimingDelegate delegate_0(false); | 
|  | std::string url_0 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/kitten-1.jpg"); | 
|  | std::unique_ptr<URLRequest> request_0 = | 
|  | CreateRequest(GURL(url_0), DEFAULT_PRIORITY, &delegate_0); | 
|  |  | 
|  | request_0->Start(); | 
|  | ASSERT_TRUE(request_0->is_pending()); | 
|  |  | 
|  | // Spin the message loop until the client receives the response for the first | 
|  | // request. | 
|  | do { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } while (request_0->status().is_io_pending()); | 
|  | EXPECT_TRUE(request_0->status().is_success()); | 
|  |  | 
|  | // Send a request to the pushed url: /favicon.ico to pull the resource into | 
|  | // cache. | 
|  | CheckLoadTimingDelegate delegate_1(true); | 
|  | std::string url_1 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/favicon.ico"); | 
|  | std::unique_ptr<URLRequest> request_1 = | 
|  | CreateRequest(GURL(url_1), DEFAULT_PRIORITY, &delegate_1); | 
|  |  | 
|  | request_1->Start(); | 
|  | ASSERT_TRUE(request_1->is_pending()); | 
|  |  | 
|  | // Spin the message loop until the client receives the response for the second | 
|  | // request. | 
|  | do { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } while (request_1->status().is_io_pending()); | 
|  | EXPECT_TRUE(request_1->status().is_success()); | 
|  |  | 
|  | // Send a request to /index2.html which pushes /kitten-1.jpg and /favicon.ico. | 
|  | // Should cancel push for both pushed resources, since they're already cached. | 
|  | CheckLoadTimingDelegate delegate(true); | 
|  | std::string url = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/index2.html"); | 
|  | std::unique_ptr<URLRequest> request = | 
|  | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate); | 
|  |  | 
|  | request->Start(); | 
|  | ASSERT_TRUE(request->is_pending()); | 
|  |  | 
|  | // Spin the message loop until the client receives the response for the third | 
|  | // request. | 
|  | do { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } while (request->status().is_io_pending()); | 
|  | EXPECT_TRUE(request->status().is_success()); | 
|  |  | 
|  | // Extract net logs on client side to verify push lookup transactions. | 
|  | net::TestNetLogEntry::List entries; | 
|  | ExtractNetLog(NetLogEventType::SERVER_PUSH_LOOKUP_TRANSACTION, &entries); | 
|  |  | 
|  | EXPECT_EQ(4u, entries.size()); | 
|  |  | 
|  | std::string value; | 
|  | int net_error; | 
|  | std::string push_url_1 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/kitten-1.jpg"); | 
|  | std::string push_url_2 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/favicon.ico"); | 
|  |  | 
|  | const NetLogSource source_1 = FindPushUrlSource(entries, push_url_1); | 
|  | EXPECT_TRUE(source_1.IsValid()); | 
|  |  | 
|  | // No net error code for this lookup transaction, the push is found. | 
|  | const TestNetLogEntry* end_entry_1 = FindEndBySource(entries, source_1); | 
|  | EXPECT_FALSE(end_entry_1->params); | 
|  | EXPECT_FALSE(end_entry_1->GetIntegerValue("net_error", &net_error)); | 
|  |  | 
|  | const NetLogSource source_2 = FindPushUrlSource(entries, push_url_2); | 
|  | EXPECT_TRUE(source_1.IsValid()); | 
|  | EXPECT_NE(source_1.id, source_2.id); | 
|  |  | 
|  | // No net error code for this lookup transaction, the push is found. | 
|  | const TestNetLogEntry* end_entry_2 = FindEndBySource(entries, source_2); | 
|  | EXPECT_FALSE(end_entry_2->params); | 
|  | EXPECT_FALSE(end_entry_2->GetIntegerValue("net_error", &net_error)); | 
|  |  | 
|  | // Verify the reset error count received on the server side. | 
|  | EXPECT_LE(2u, GetRstErrorCountReceivedByServer(quic::QUIC_STREAM_CANCELLED)); | 
|  | } | 
|  |  | 
|  | TEST_F(URLRequestQuicTest, DoNotCancelPushIfNotFoundInCache) { | 
|  | Init(); | 
|  |  | 
|  | // Send a request to /index2.hmtl which pushes /kitten-1.jpg and /favicon.ico | 
|  | // and shouldn't cancel any since neither is in cache. | 
|  | CheckLoadTimingDelegate delegate(false); | 
|  | std::string url = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/index2.html"); | 
|  | std::unique_ptr<URLRequest> request = | 
|  | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate); | 
|  |  | 
|  | request->Start(); | 
|  | ASSERT_TRUE(request->is_pending()); | 
|  |  | 
|  | // Spin the message loop until the client receives response. | 
|  | do { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } while (request->status().is_io_pending()); | 
|  | EXPECT_TRUE(request->status().is_success()); | 
|  |  | 
|  | // Extract net logs on client side to verify push lookup transactions. | 
|  | net::TestNetLogEntry::List entries; | 
|  | ExtractNetLog(NetLogEventType::SERVER_PUSH_LOOKUP_TRANSACTION, &entries); | 
|  |  | 
|  | EXPECT_EQ(4u, entries.size()); | 
|  |  | 
|  | std::string value; | 
|  | int net_error; | 
|  | std::string push_url_1 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/kitten-1.jpg"); | 
|  | std::string push_url_2 = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, "/favicon.ico"); | 
|  |  | 
|  | const NetLogSource source_1 = FindPushUrlSource(entries, push_url_1); | 
|  | EXPECT_TRUE(source_1.IsValid()); | 
|  | const TestNetLogEntry* end_entry_1 = FindEndBySource(entries, source_1); | 
|  | EXPECT_TRUE(end_entry_1->params); | 
|  | EXPECT_TRUE(end_entry_1->GetIntegerValue("net_error", &net_error)); | 
|  | EXPECT_EQ(net_error, -400); | 
|  |  | 
|  | const NetLogSource source_2 = FindPushUrlSource(entries, push_url_2); | 
|  | EXPECT_TRUE(source_2.IsValid()); | 
|  | EXPECT_NE(source_1.id, source_2.id); | 
|  | const TestNetLogEntry* end_entry_2 = FindEndBySource(entries, source_2); | 
|  | EXPECT_TRUE(end_entry_2->params); | 
|  | EXPECT_TRUE(end_entry_2->GetIntegerValue("net_error", &net_error)); | 
|  | EXPECT_EQ(net_error, -400); | 
|  |  | 
|  | // Verify the reset error count received on the server side. | 
|  | EXPECT_EQ(0u, GetRstErrorCountReceivedByServer(quic::QUIC_STREAM_CANCELLED)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // This test is found to be flaky in Cobalt and was disabled due to its | 
|  | // unguaranteed assumption that the two requests finish in order. | 
|  | // The response for request2 from server sometimes can arrive earlier than | 
|  | // 1st request's response. In that case, request2 is the one to receive the | 
|  | // first byte stream from the same QUICSession the requests share and request | 
|  | // will be marked as re-using the QUICSession in that case. | 
|  | #if !defined(STARBOARD) | 
|  | // Tests that if two requests use the same QUIC session, the second request | 
|  | // should not have |LoadTimingInfo::connect_timing|. | 
|  | TEST_F(URLRequestQuicTest, TestTwoRequests) { | 
|  | base::RunLoop run_loop; | 
|  | WaitForCompletionNetworkDelegate network_delegate( | 
|  | run_loop.QuitClosure(), /*num_expected_requests=*/2); | 
|  | SetNetworkDelegate(&network_delegate); | 
|  | Init(); | 
|  | CheckLoadTimingDelegate delegate(false); | 
|  | delegate.set_on_complete(base::DoNothing()); | 
|  | std::string url = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, kHelloPath); | 
|  | std::unique_ptr<URLRequest> request = | 
|  | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate); | 
|  |  | 
|  | CheckLoadTimingDelegate delegate2(true); | 
|  | delegate2.set_on_complete(base::DoNothing()); | 
|  | std::unique_ptr<URLRequest> request2 = | 
|  | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate2); | 
|  |  | 
|  | request->Start(); | 
|  | request2->Start(); | 
|  | ASSERT_TRUE(request->is_pending()); | 
|  | ASSERT_TRUE(request2->is_pending()); | 
|  | run_loop.Run(); | 
|  |  | 
|  | EXPECT_TRUE(request->status().is_success()); | 
|  | EXPECT_TRUE(request2->status().is_success()); | 
|  | EXPECT_EQ(kHelloBodyValue, delegate.data_received()); | 
|  | EXPECT_EQ(kHelloBodyValue, delegate2.data_received()); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | TEST_F(URLRequestQuicTest, RequestHeadersCallback) { | 
|  | Init(); | 
|  | HttpRawRequestHeaders raw_headers; | 
|  | TestDelegate delegate; | 
|  | TestURLRequestContext context; | 
|  | HttpRequestHeaders extra_headers; | 
|  | extra_headers.SetHeader("X-Foo", "bar"); | 
|  |  | 
|  | std::string url = | 
|  | base::StringPrintf("https://%s%s", kTestServerHost, kHelloPath); | 
|  | std::unique_ptr<URLRequest> request = | 
|  | CreateRequest(GURL(url), DEFAULT_PRIORITY, &delegate); | 
|  |  | 
|  | request->SetExtraRequestHeaders(extra_headers); | 
|  | request->SetRequestHeadersCallback(base::Bind( | 
|  | &HttpRawRequestHeaders::Assign, base::Unretained(&raw_headers))); | 
|  | request->Start(); | 
|  | ASSERT_TRUE(request->is_pending()); | 
|  | do { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } while (!delegate.response_started_count()); | 
|  | EXPECT_FALSE(raw_headers.headers().empty()); | 
|  | std::string value; | 
|  | EXPECT_TRUE(raw_headers.FindHeaderForTest("x-foo", &value)); | 
|  | EXPECT_EQ("bar", value); | 
|  | EXPECT_TRUE(raw_headers.FindHeaderForTest("accept-encoding", &value)); | 
|  | EXPECT_EQ("gzip, deflate", value); | 
|  | EXPECT_TRUE(raw_headers.FindHeaderForTest(":path", &value)); | 
|  | EXPECT_EQ("/hello.txt", value); | 
|  | EXPECT_TRUE(raw_headers.FindHeaderForTest(":authority", &value)); | 
|  | EXPECT_EQ("test.example.com", value); | 
|  | EXPECT_TRUE(raw_headers.request_line().empty()); | 
|  | } | 
|  |  | 
|  | }  // namespace net |