// Copyright (c) 2012 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 #include #include #include #include #include #include "base/memory/raw_ptr.h" // This must be before Windows headers #include "base/callback_helpers.h" #include "base/memory/ptr_util.h" #include "base/time/time.h" #include "build/build_config.h" #include "net/dns/public/secure_dns_policy.h" #include "net/log/net_log.h" #include "url/url_constants.h" #if BUILDFLAG(IS_WIN) #include #include #include #include #endif #include "base/base64url.h" #include "base/bind.h" #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/format_macros.h" #include "base/json/json_reader.h" #include "base/location.h" #include "base/memory/weak_ptr.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/strings/escape.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/task/single_thread_task_runner.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "build/buildflag.h" #include "crypto/sha2.h" #include "net/base/chunked_upload_data_stream.h" #include "net/base/directory_listing.h" #include "net/base/elements_upload_data_stream.h" #include "net/base/features.h" #include "net/base/hash_value.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" #include "net/base/isolation_info.h" #include "net/base/load_flags.h" #include "net/base/load_timing_info.h" #include "net/base/load_timing_info_test_util.h" #include "net/base/net_errors.h" #include "net/base/net_module.h" #include "net/base/proxy_server.h" #include "net/base/proxy_string_util.h" #include "net/base/request_priority.h" #include "net/base/test_completion_callback.h" #include "net/base/transport_info.h" #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_data_stream.h" #include "net/base/upload_file_element_reader.h" #include "net/base/url_util.h" #include "net/cert/asn1_util.h" #include "net/cert/cert_net_fetcher.h" #include "net/cert/crl_set.h" #include "net/cert/ct_policy_enforcer.h" #include "net/cert/ct_policy_status.h" #include "net/cert/do_nothing_ct_verifier.h" #include "net/cert/ev_root_ca_metadata.h" #include "net/cert/mock_cert_verifier.h" #include "net/cert/multi_log_ct_verifier.h" #include "net/cert/signed_certificate_timestamp_and_status.h" #include "net/cert/test_root_certs.h" #include "net/cert/x509_util.h" #include "net/cert_net/cert_net_fetcher_url_request.h" #include "net/cookies/canonical_cookie_test_helpers.h" #include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_monster.h" #include "net/cookies/cookie_store_test_helpers.h" #include "net/cookies/test_cookie_access_delegate.h" #include "net/disk_cache/disk_cache.h" #include "net/dns/host_resolver_results.h" #include "net/dns/mock_host_resolver.h" #include "net/dns/public/secure_dns_policy.h" #include "net/http/http_byte_range.h" #include "net/http/http_cache.h" #include "net/http/http_network_layer.h" #include "net/http/http_network_session.h" #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/http/http_server_properties.h" #include "net/http/http_transaction_test_util.h" #include "net/http/http_util.h" #include "net/http/transport_security_state.h" #include "net/http/transport_security_state_source.h" #include "net/log/file_net_log_observer.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_source.h" #include "net/log/test_net_log.h" #include "net/log/test_net_log_util.h" #include "net/net_buildflags.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" #include "net/quic/mock_crypto_client_stream_factory.h" #include "net/quic/quic_server_info.h" #include "net/socket/read_buffering_stream_socket.h" #include "net/socket/socket_test_util.h" #include "net/socket/ssl_client_socket.h" #include "net/ssl/client_cert_identity_test_util.h" #include "net/ssl/ssl_connection_status_flags.h" #include "net/ssl/ssl_private_key.h" #include "net/ssl/ssl_server_config.h" #include "net/ssl/test_ssl_config_service.h" #include "net/test/cert_test_util.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" #include "net/test/gtest_util.h" #include "net/test/spawned_test_server/spawned_test_server.h" #include "net/test/test_data_directory.h" #include "net/test/test_with_task_environment.h" #include "net/test/url_request/url_request_failed_job.h" #include "net/test/url_request/url_request_mock_http_job.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/redirect_util.h" #include "net/url_request/referrer_policy.h" #include "net/url_request/static_http_user_agent_settings.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_filter.h" #include "net/url_request/url_request_http_job.h" #include "net/url_request/url_request_interceptor.h" #include "net/url_request/url_request_redirect_job.h" #include "net/url_request/url_request_test_job.h" #include "net/url_request/url_request_test_util.h" #include "net/url_request/websocket_handshake_userdata_key.h" #include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/boringssl/src/include/openssl/ssl.h" #include "url/url_constants.h" #include "url/url_util.h" #if BUILDFLAG(IS_WIN) #include "base/win/scoped_com_initializer.h" #endif #if BUILDFLAG(IS_APPLE) #include "base/mac/mac_util.h" #endif #if BUILDFLAG(ENABLE_REPORTING) #include "net/network_error_logging/network_error_logging_service.h" #include "net/network_error_logging/network_error_logging_test_util.h" #endif // BUILDFLAG(ENABLE_REPORTING) #if BUILDFLAG(ENABLE_WEBSOCKETS) #include "net/websockets/websocket_test_util.h" #endif // BUILDFLAG(ENABLE_WEBSOCKETS) using net::test::IsError; using net::test::IsOk; using net::test_server::RegisterDefaultHandlers; using testing::_; using testing::AnyOf; using testing::ElementsAre; using testing::IsEmpty; using testing::Optional; using testing::UnorderedElementsAre; using base::ASCIIToUTF16; using base::Time; using std::string; namespace net { namespace { namespace test_default { #include "net/http/transport_security_state_static_unittest_default.h" } const std::u16string kChrome(u"chrome"); const std::u16string kSecret(u"secret"); const std::u16string kUser(u"user"); const base::FilePath::CharType kTestFilePath[] = FILE_PATH_LITERAL("net/data/url_request_unittest"); // Tests load timing information in the case a fresh connection was used, with // no proxy. void TestLoadTimingNotReused(const LoadTimingInfo& load_timing_info, int connect_timing_flags) { EXPECT_FALSE(load_timing_info.socket_reused); EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); EXPECT_FALSE(load_timing_info.request_start_time.is_null()); EXPECT_FALSE(load_timing_info.request_start.is_null()); EXPECT_LE(load_timing_info.request_start, load_timing_info.connect_timing.connect_start); ExpectConnectTimingHasTimes(load_timing_info.connect_timing, connect_timing_flags); EXPECT_LE(load_timing_info.connect_timing.connect_end, load_timing_info.send_start); EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start); EXPECT_LE(load_timing_info.receive_headers_start, load_timing_info.receive_headers_end); EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); } // Same as above, but with proxy times. void TestLoadTimingNotReusedWithProxy(const LoadTimingInfo& load_timing_info, int connect_timing_flags) { EXPECT_FALSE(load_timing_info.socket_reused); EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); EXPECT_FALSE(load_timing_info.request_start_time.is_null()); EXPECT_FALSE(load_timing_info.request_start.is_null()); EXPECT_LE(load_timing_info.request_start, load_timing_info.proxy_resolve_start); EXPECT_LE(load_timing_info.proxy_resolve_start, load_timing_info.proxy_resolve_end); EXPECT_LE(load_timing_info.proxy_resolve_end, load_timing_info.connect_timing.connect_start); ExpectConnectTimingHasTimes(load_timing_info.connect_timing, connect_timing_flags); EXPECT_LE(load_timing_info.connect_timing.connect_end, load_timing_info.send_start); EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start); EXPECT_LE(load_timing_info.receive_headers_start, load_timing_info.receive_headers_end); } // Same as above, but with a reused socket and proxy times. void TestLoadTimingReusedWithProxy(const LoadTimingInfo& load_timing_info) { EXPECT_TRUE(load_timing_info.socket_reused); EXPECT_NE(NetLogSource::kInvalidId, load_timing_info.socket_log_id); EXPECT_FALSE(load_timing_info.request_start_time.is_null()); EXPECT_FALSE(load_timing_info.request_start.is_null()); ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); EXPECT_LE(load_timing_info.request_start, load_timing_info.proxy_resolve_start); EXPECT_LE(load_timing_info.proxy_resolve_start, load_timing_info.proxy_resolve_end); EXPECT_LE(load_timing_info.proxy_resolve_end, load_timing_info.send_start); EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start); EXPECT_LE(load_timing_info.receive_headers_start, load_timing_info.receive_headers_end); } CookieList GetAllCookies(URLRequestContext* request_context) { CookieList cookie_list; base::RunLoop run_loop; request_context->cookie_store()->GetAllCookiesAsync( base::BindLambdaForTesting([&](const CookieList& cookies) { cookie_list = cookies; run_loop.Quit(); })); run_loop.Run(); return cookie_list; } void TestLoadTimingCacheHitNoNetwork(const LoadTimingInfo& load_timing_info) { EXPECT_FALSE(load_timing_info.socket_reused); EXPECT_EQ(NetLogSource::kInvalidId, load_timing_info.socket_log_id); EXPECT_FALSE(load_timing_info.request_start_time.is_null()); EXPECT_FALSE(load_timing_info.request_start.is_null()); ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); EXPECT_LE(load_timing_info.request_start, load_timing_info.send_start); EXPECT_LE(load_timing_info.send_start, load_timing_info.send_end); EXPECT_LE(load_timing_info.send_end, load_timing_info.receive_headers_start); EXPECT_LE(load_timing_info.receive_headers_start, load_timing_info.receive_headers_end); EXPECT_TRUE(load_timing_info.proxy_resolve_start.is_null()); EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); } // Job that allows monitoring of its priority. class PriorityMonitoringURLRequestJob : public URLRequestTestJob { public: // The latest priority of the job is always written to |request_priority_|. PriorityMonitoringURLRequestJob(URLRequest* request, RequestPriority* request_priority) : URLRequestTestJob(request), request_priority_(request_priority) { *request_priority_ = DEFAULT_PRIORITY; } void SetPriority(RequestPriority priority) override { *request_priority_ = priority; URLRequestTestJob::SetPriority(priority); } private: const raw_ptr request_priority_; }; // Do a case-insensitive search through |haystack| for |needle|. bool ContainsString(const std::string& haystack, const char* needle) { std::string::const_iterator it = std::search( haystack.begin(), haystack.end(), needle, needle + strlen(needle), base::CaseInsensitiveCompareASCII()); return it != haystack.end(); } std::unique_ptr CreateSimpleUploadData(const char* data) { auto reader = std::make_unique(data, strlen(data)); return ElementsUploadDataStream::CreateWithReader(std::move(reader), 0); } // Verify that the SSLInfo of a successful SSL connection has valid values. void CheckSSLInfo(const SSLInfo& ssl_info) { // The cipher suite TLS_NULL_WITH_NULL_NULL (0) must not be negotiated. uint16_t cipher_suite = SSLConnectionStatusToCipherSuite(ssl_info.connection_status); EXPECT_NE(0U, cipher_suite); } // A network delegate that allows the user to choose a subset of request stages // to block in. When blocking, the delegate can do one of the following: // * synchronously return a pre-specified error code, or // * asynchronously return that value via an automatically called callback, // or // * block and wait for the user to do a callback. // Additionally, the user may also specify a redirect URL -- then each request // with the current URL different from the redirect target will be redirected // to that target, in the on-before-URL-request stage, independent of whether // the delegate blocks in ON_BEFORE_URL_REQUEST or not. class BlockingNetworkDelegate : public TestNetworkDelegate { public: // Stages in which the delegate can block. enum Stage { NOT_BLOCKED = 0, ON_BEFORE_URL_REQUEST = 1 << 0, ON_BEFORE_SEND_HEADERS = 1 << 1, ON_HEADERS_RECEIVED = 1 << 2, }; // Behavior during blocked stages. During other stages, just // returns OK or NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION. enum BlockMode { SYNCHRONOUS, // No callback, returns specified return values. AUTO_CALLBACK, // |this| posts a task to run the callback using the // specified return codes. USER_CALLBACK, // User takes care of doing a callback. |retval_| and // |auth_retval_| are ignored. In every blocking stage the // message loop is quit. }; // Creates a delegate which does not block at all. explicit BlockingNetworkDelegate(BlockMode block_mode); BlockingNetworkDelegate(const BlockingNetworkDelegate&) = delete; BlockingNetworkDelegate& operator=(const BlockingNetworkDelegate&) = delete; // Runs the message loop until the delegate blocks. void RunUntilBlocked(); // For users to trigger a callback returning |response|. // Side-effects: resets |stage_blocked_for_callback_| and stored callbacks. // Only call if |block_mode_| == USER_CALLBACK. void DoCallback(int response); // Setters. void set_retval(int retval) { ASSERT_NE(USER_CALLBACK, block_mode_); ASSERT_NE(ERR_IO_PENDING, retval); ASSERT_NE(OK, retval); retval_ = retval; } void set_redirect_url(const GURL& url) { redirect_url_ = url; } void set_block_on(int block_on) { block_on_ = block_on; } // Allows the user to check in which state did we block. Stage stage_blocked_for_callback() const { EXPECT_EQ(USER_CALLBACK, block_mode_); return stage_blocked_for_callback_; } private: void OnBlocked(); void RunCallback(int response, CompletionOnceCallback callback); // TestNetworkDelegate implementation. int OnBeforeURLRequest(URLRequest* request, CompletionOnceCallback callback, GURL* new_url) override; int OnBeforeStartTransaction( URLRequest* request, const HttpRequestHeaders& headers, OnBeforeStartTransactionCallback callback) override; int OnHeadersReceived( URLRequest* request, CompletionOnceCallback callback, const HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, const IPEndPoint& endpoint, absl::optional* preserve_fragment_on_redirect_url) override; // Resets the callbacks and |stage_blocked_for_callback_|. void Reset(); // Checks whether we should block in |stage|. If yes, returns an error code // and optionally sets up callback based on |block_mode_|. If no, returns OK. int MaybeBlockStage(Stage stage, CompletionOnceCallback callback); // Configuration parameters, can be adjusted by public methods: const BlockMode block_mode_; // Values returned on blocking stages when mode is SYNCHRONOUS or // AUTO_CALLBACK. For USER_CALLBACK these are set automatically to IO_PENDING. int retval_ = OK; GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest. int block_on_ = 0; // Bit mask: in which stages to block. // Internal variables, not set by not the user: // Last blocked stage waiting for user callback (unused if |block_mode_| != // USER_CALLBACK). Stage stage_blocked_for_callback_ = NOT_BLOCKED; // Callback objects stored during blocking stages. CompletionOnceCallback callback_; // Closure to run to exit RunUntilBlocked(). base::OnceClosure on_blocked_; base::WeakPtrFactory weak_factory_{this}; }; BlockingNetworkDelegate::BlockingNetworkDelegate(BlockMode block_mode) : block_mode_(block_mode) {} void BlockingNetworkDelegate::RunUntilBlocked() { base::RunLoop run_loop; on_blocked_ = run_loop.QuitClosure(); run_loop.Run(); } void BlockingNetworkDelegate::DoCallback(int response) { ASSERT_EQ(USER_CALLBACK, block_mode_); ASSERT_NE(NOT_BLOCKED, stage_blocked_for_callback_); CompletionOnceCallback callback = std::move(callback_); Reset(); // |callback| may trigger completion of a request, so post it as a task, so // it will run under a subsequent TestDelegate::RunUntilComplete() loop. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback, weak_factory_.GetWeakPtr(), response, std::move(callback))); } void BlockingNetworkDelegate::OnBlocked() { // If this fails due to |on_blocked_| being null then OnBlocked() was run by // a RunLoop other than RunUntilBlocked(), indicating a bug in the calling // test. std::move(on_blocked_).Run(); } void BlockingNetworkDelegate::RunCallback(int response, CompletionOnceCallback callback) { std::move(callback).Run(response); } int BlockingNetworkDelegate::OnBeforeURLRequest(URLRequest* request, CompletionOnceCallback callback, GURL* new_url) { if (redirect_url_ == request->url()) return OK; // We've already seen this request and redirected elsewhere. // TestNetworkDelegate always completes synchronously. CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeURLRequest( request, base::NullCallback(), new_url)); if (!redirect_url_.is_empty()) *new_url = redirect_url_; return MaybeBlockStage(ON_BEFORE_URL_REQUEST, std::move(callback)); } int BlockingNetworkDelegate::OnBeforeStartTransaction( URLRequest* request, const HttpRequestHeaders& headers, OnBeforeStartTransactionCallback callback) { // TestNetworkDelegate always completes synchronously. CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeStartTransaction( request, headers, base::NullCallback())); return MaybeBlockStage( ON_BEFORE_SEND_HEADERS, base::BindOnce( [](OnBeforeStartTransactionCallback callback, int result) { std::move(callback).Run(result, absl::nullopt); }, std::move(callback))); } int BlockingNetworkDelegate::OnHeadersReceived( URLRequest* request, CompletionOnceCallback callback, const HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, const IPEndPoint& endpoint, absl::optional* preserve_fragment_on_redirect_url) { // TestNetworkDelegate always completes synchronously. CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnHeadersReceived( request, base::NullCallback(), original_response_headers, override_response_headers, endpoint, preserve_fragment_on_redirect_url)); return MaybeBlockStage(ON_HEADERS_RECEIVED, std::move(callback)); } void BlockingNetworkDelegate::Reset() { EXPECT_NE(NOT_BLOCKED, stage_blocked_for_callback_); stage_blocked_for_callback_ = NOT_BLOCKED; callback_.Reset(); } int BlockingNetworkDelegate::MaybeBlockStage( BlockingNetworkDelegate::Stage stage, CompletionOnceCallback callback) { // Check that the user has provided callback for the previous blocked stage. EXPECT_EQ(NOT_BLOCKED, stage_blocked_for_callback_); if ((block_on_ & stage) == 0) { return OK; } switch (block_mode_) { case SYNCHRONOUS: EXPECT_NE(OK, retval_); return retval_; case AUTO_CALLBACK: base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback, weak_factory_.GetWeakPtr(), retval_, std::move(callback))); return ERR_IO_PENDING; case USER_CALLBACK: callback_ = std::move(callback); stage_blocked_for_callback_ = stage; // We may reach here via a callback prior to RunUntilBlocked(), so post // a task to fetch and run the |on_blocked_| closure. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::OnBlocked, weak_factory_.GetWeakPtr())); return ERR_IO_PENDING; } NOTREACHED(); return 0; } // A mock ReportSenderInterface that just remembers the latest report // URI and report to be sent. class MockCertificateReportSender : public TransportSecurityState::ReportSenderInterface { public: MockCertificateReportSender() = default; ~MockCertificateReportSender() override = default; void Send( const GURL& report_uri, base::StringPiece content_type, base::StringPiece report, const NetworkIsolationKey& network_isolation_key, base::OnceCallback success_callback, base::OnceCallback error_callback) override { latest_report_uri_ = report_uri; latest_report_.assign(report.data(), report.size()); latest_content_type_.assign(content_type.data(), content_type.size()); latest_network_isolation_key_ = network_isolation_key; } const GURL& latest_report_uri() { return latest_report_uri_; } const std::string& latest_report() { return latest_report_; } const std::string& latest_content_type() { return latest_content_type_; } const NetworkIsolationKey& latest_network_isolation_key() { return latest_network_isolation_key_; } private: GURL latest_report_uri_; std::string latest_report_; std::string latest_content_type_; NetworkIsolationKey latest_network_isolation_key_; }; // OCSPErrorTestDelegate caches the SSLInfo passed to OnSSLCertificateError. // This is needed because after the certificate failure, the URLRequest will // retry the connection, and return a partial SSLInfo with a cached cert status. // The partial SSLInfo does not have the OCSP information filled out. class OCSPErrorTestDelegate : public TestDelegate { public: void OnSSLCertificateError(URLRequest* request, int net_error, const SSLInfo& ssl_info, bool fatal) override { ssl_info_ = ssl_info; on_ssl_certificate_error_called_ = true; TestDelegate::OnSSLCertificateError(request, net_error, ssl_info, fatal); } bool on_ssl_certificate_error_called() { return on_ssl_certificate_error_called_; } SSLInfo ssl_info() { return ssl_info_; } private: bool on_ssl_certificate_error_called_ = false; SSLInfo ssl_info_; }; #if !BUILDFLAG(IS_IOS) // Compute the root cert's SPKI hash on the fly, to avoid hardcoding it within // tests. bool GetTestRootCertSPKIHash(SHA256HashValue* root_hash) { scoped_refptr root_cert = ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem"); if (!root_cert) return false; base::StringPiece root_spki; if (!asn1::ExtractSPKIFromDERCert( x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer()), &root_spki)) { return false; } crypto::SHA256HashString(root_spki, root_hash, sizeof(SHA256HashValue)); return true; } #endif } // namespace // Inherit PlatformTest since we require the autorelease pool on Mac OS X. class URLRequestTest : public PlatformTest, public WithTaskEnvironment { public: URLRequestTest() = default; ~URLRequestTest() override { // URLRequestJobs may post clean-up tasks on destruction. base::RunLoop().RunUntilIdle(); SetTransportSecurityStateSourceForTesting(nullptr); } void SetUp() override { auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_net_log(NetLog::Get()); SetUpContextBuilder(*context_builder); // We set the TestNetworkDelegate after calling SetUpContextBuilder as // default_network_delegate() relies on this set up and we don't want to // allow subclasses to break the assumption. context_builder->set_network_delegate( std::make_unique()); default_context_ = context_builder->Build(); PlatformTest::SetUp(); } void TearDown() override { default_context_.reset(); } virtual void SetUpContextBuilder(URLRequestContextBuilder& builder) {} TestNetworkDelegate& default_network_delegate() { // This cast is safe because we provided a TestNetworkDelegate in SetUp(). return *static_cast( default_context_->network_delegate()); } URLRequestContext& default_context() const { return *default_context_; } // Creates a temp test file and writes |data| to the file. The file will be // deleted after the test completes. void CreateTestFile(const char* data, size_t data_size, base::FilePath* test_file) { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); // Get an absolute path since |temp_dir| can contain a symbolic link. As of // now, Mac and Android bots return a path with a symbolic link. base::FilePath absolute_temp_dir = base::MakeAbsoluteFilePath(temp_dir_.GetPath()); ASSERT_TRUE(base::CreateTemporaryFileInDir(absolute_temp_dir, test_file)); ASSERT_EQ(static_cast(data_size), base::WriteFile(*test_file, data, data_size)); } static std::unique_ptr CreateFixedProxyResolutionService(const std::string& proxy) { return ConfiguredProxyResolutionService::CreateFixedForTest( proxy, TRAFFIC_ANNOTATION_FOR_TESTS); } std::unique_ptr CreateFirstPartyRequest( const URLRequestContext& context, const GURL& url, URLRequest::Delegate* delegate) { auto req = context.CreateRequest(url, DEFAULT_PRIORITY, delegate, TRAFFIC_ANNOTATION_FOR_TESTS); req->set_initiator(url::Origin::Create(url)); req->set_site_for_cookies(SiteForCookies::FromUrl(url)); return req; } protected: RecordingNetLogObserver net_log_observer_; std::unique_ptr default_context_; base::ScopedTempDir temp_dir_; }; TEST_F(URLRequestTest, AboutBlankTest) { TestDelegate d; { std::unique_ptr r( default_context().CreateRequest(GURL("about:blank"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_TRUE(!r->is_pending()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(d.bytes_received(), 0); EXPECT_TRUE(r->GetResponseRemoteEndpoint().address().empty()); EXPECT_EQ(0, r->GetResponseRemoteEndpoint().port()); } } TEST_F(URLRequestTest, InvalidUrlTest) { TestDelegate d; { std::unique_ptr r( default_context().CreateRequest(GURL("invalid url"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_TRUE(d.request_failed()); } } // Test that URLRequest rejects WS URLs by default. TEST_F(URLRequestTest, WsUrlTest) { const url::Origin kOrigin = url::Origin::Create(GURL("http://foo.test/")); TestDelegate d; std::unique_ptr r( default_context().CreateRequest(GURL("ws://foo.test/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // This is not strictly necessary for this test, but used to trigger a DCHECK. // See https://crbug.com/1245115. r->set_isolation_info( IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, SiteForCookies::FromOrigin(kOrigin))); r->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.request_failed()); EXPECT_THAT(d.request_status(), IsError(ERR_UNKNOWN_URL_SCHEME)); } // Test that URLRequest rejects WSS URLs by default. TEST_F(URLRequestTest, WssUrlTest) { const url::Origin kOrigin = url::Origin::Create(GURL("https://foo.test/")); TestDelegate d; std::unique_ptr r( default_context().CreateRequest(GURL("wss://foo.test/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // This is not strictly necessary for this test, but used to trigger a DCHECK. // See https://crbug.com/1245115. r->set_isolation_info( IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, SiteForCookies::FromOrigin(kOrigin))); r->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.request_failed()); EXPECT_THAT(d.request_status(), IsError(ERR_UNKNOWN_URL_SCHEME)); } TEST_F(URLRequestTest, InvalidReferrerTest) { default_network_delegate().set_cancel_request_with_policy_violating_referrer( true); TestDelegate d; std::unique_ptr req = default_context().CreateRequest( GURL("http://localhost/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS); req->SetReferrer("https://somewhere.com/"); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.request_failed()); } TEST_F(URLRequestTest, RecordsSameOriginReferrerHistogram) { default_network_delegate().set_cancel_request_with_policy_violating_referrer( false); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( GURL("http://google.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer("http://google.com"); req->set_referrer_policy(ReferrerPolicy::NEVER_CLEAR); base::HistogramTester histograms; req->Start(); d.RunUntilComplete(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerPolicyForRequest.SameOrigin", static_cast(ReferrerPolicy::NEVER_CLEAR), 1); } TEST_F(URLRequestTest, RecordsCrossOriginReferrerHistogram) { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( GURL("http://google.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer("http://origin.com"); // Set a different policy just to make sure we aren't always logging the same // policy. req->set_referrer_policy( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE); base::HistogramTester histograms; req->Start(); d.RunUntilComplete(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin", static_cast( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), 1); } TEST_F(URLRequestTest, RecordsReferrerHistogramAgainOnRedirect) { auto context_builder = CreateTestURLRequestContextBuilder(); auto network_delegate = std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS); network_delegate->set_redirect_url(GURL("http://redirect.com/")); context_builder->set_network_delegate(std::move(network_delegate)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer("http://google.com"); req->set_referrer_policy( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE); base::HistogramTester histograms; req->Start(); d.RunUntilRedirect(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerPolicyForRequest.SameOrigin", static_cast( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), 1); req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, /*modified_headers=*/absl::nullopt); d.RunUntilComplete(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin", static_cast( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), 1); } TEST_F(URLRequestTest, RecordsReferrrerWithInformativePath) { auto context_builder = CreateTestURLRequestContextBuilder(); auto network_delegate = std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS); network_delegate->set_cancel_request_with_policy_violating_referrer(true); network_delegate->set_redirect_url(GURL("http://redirect.com/")); context_builder->set_network_delegate(std::move(network_delegate)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Since this referrer is much more informative than the initiating origin, // we should see the histograms' true buckets populated. req->SetReferrer("http://google.com/very-informative-path"); base::HistogramTester histograms; req->Start(); d.RunUntilRedirect(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerHasInformativePath.SameOrigin", /* Check the count of the "true" bucket in the boolean histogram. */ true, 1); req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, /*modified_headers=*/absl::nullopt); d.RunUntilComplete(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1); } TEST_F(URLRequestTest, RecordsReferrerWithInformativeQuery) { auto context_builder = CreateTestURLRequestContextBuilder(); auto network_delegate = std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS); network_delegate->set_cancel_request_with_policy_violating_referrer(true); network_delegate->set_redirect_url(GURL("http://redirect.com/")); context_builder->set_network_delegate(std::move(network_delegate)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Since this referrer is much more informative than the initiating origin, // we should see the histograms' true buckets populated. req->SetReferrer("http://google.com/?very-informative-query"); base::HistogramTester histograms; req->Start(); d.RunUntilRedirect(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerHasInformativePath.SameOrigin", /* Check the count of the "true" bucket in the boolean histogram. */ true, 1); req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, /*modified_headers=*/absl::nullopt); d.RunUntilComplete(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1); } TEST_F(URLRequestTest, RecordsReferrerWithoutInformativePathOrQuery) { auto context_builder = CreateTestURLRequestContextBuilder(); auto network_delegate = std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS); network_delegate->set_cancel_request_with_policy_violating_referrer(false); network_delegate->set_redirect_url(GURL("http://origin.com/")); context_builder->set_network_delegate(std::move(network_delegate)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://google.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Since this referrer _isn't_ more informative than the initiating origin, // we should see the histograms' false buckets populated. req->SetReferrer("http://origin.com"); base::HistogramTester histograms; req->Start(); d.RunUntilRedirect(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", false, 1); req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, /*modified_headers=*/absl::nullopt); d.RunUntilComplete(); histograms.ExpectUniqueSample( "Net.URLRequest.ReferrerHasInformativePath.SameOrigin", false, 1); } // A URLRequestInterceptor that allows setting the LoadTimingInfo value of the // URLRequestJobs it creates. class URLRequestInterceptorWithLoadTimingInfo : public URLRequestInterceptor { public: // Static getters for canned response header and data strings. static std::string ok_data() { return URLRequestTestJob::test_data_1(); } static std::string ok_headers() { return URLRequestTestJob::test_headers(); } URLRequestInterceptorWithLoadTimingInfo() = default; ~URLRequestInterceptorWithLoadTimingInfo() override = default; // URLRequestInterceptor implementation: std::unique_ptr MaybeInterceptRequest( URLRequest* request) const override { std::unique_ptr job = std::make_unique(request, ok_headers(), ok_data(), true); job->set_load_timing_info(main_request_load_timing_info_); return job; } void set_main_request_load_timing_info( const LoadTimingInfo& main_request_load_timing_info) { main_request_load_timing_info_ = main_request_load_timing_info; } private: mutable LoadTimingInfo main_request_load_timing_info_; }; // These tests inject a MockURLRequestInterceptor class URLRequestLoadTimingTest : public URLRequestTest { public: URLRequestLoadTimingTest() { std::unique_ptr interceptor = std::make_unique(); interceptor_ = interceptor.get(); URLRequestFilter::GetInstance()->AddHostnameInterceptor( "http", "test_intercept", std::move(interceptor)); } ~URLRequestLoadTimingTest() override { URLRequestFilter::GetInstance()->ClearHandlers(); } URLRequestInterceptorWithLoadTimingInfo* interceptor() const { return interceptor_; } private: raw_ptr interceptor_; }; // "Normal" LoadTimingInfo as returned by a job. Everything is in order, not // reused. |connect_time_flags| is used to indicate if there should be dns // or SSL times, and |used_proxy| is used for proxy times. LoadTimingInfo NormalLoadTimingInfo(base::TimeTicks now, int connect_time_flags, bool used_proxy) { LoadTimingInfo load_timing; load_timing.socket_log_id = 1; if (used_proxy) { load_timing.proxy_resolve_start = now + base::Days(1); load_timing.proxy_resolve_end = now + base::Days(2); } LoadTimingInfo::ConnectTiming& connect_timing = load_timing.connect_timing; if (connect_time_flags & CONNECT_TIMING_HAS_DNS_TIMES) { connect_timing.dns_start = now + base::Days(3); connect_timing.dns_end = now + base::Days(4); } connect_timing.connect_start = now + base::Days(5); if (connect_time_flags & CONNECT_TIMING_HAS_SSL_TIMES) { connect_timing.ssl_start = now + base::Days(6); connect_timing.ssl_end = now + base::Days(7); } connect_timing.connect_end = now + base::Days(8); load_timing.send_start = now + base::Days(9); load_timing.send_end = now + base::Days(10); load_timing.receive_headers_start = now + base::Days(11); load_timing.receive_headers_end = now + base::Days(12); return load_timing; } // Same as above, but in the case of a reused socket. LoadTimingInfo NormalLoadTimingInfoReused(base::TimeTicks now, bool used_proxy) { LoadTimingInfo load_timing; load_timing.socket_log_id = 1; load_timing.socket_reused = true; if (used_proxy) { load_timing.proxy_resolve_start = now + base::Days(1); load_timing.proxy_resolve_end = now + base::Days(2); } load_timing.send_start = now + base::Days(9); load_timing.send_end = now + base::Days(10); load_timing.receive_headers_start = now + base::Days(11); load_timing.receive_headers_end = now + base::Days(12); return load_timing; } LoadTimingInfo RunURLRequestInterceptorLoadTimingTest( const LoadTimingInfo& job_load_timing, const URLRequestContext& context, URLRequestInterceptorWithLoadTimingInfo* interceptor) { interceptor->set_main_request_load_timing_info(job_load_timing); TestDelegate d; std::unique_ptr req( context.CreateRequest(GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); LoadTimingInfo resulting_load_timing; req->GetLoadTimingInfo(&resulting_load_timing); // None of these should be modified by the URLRequest. EXPECT_EQ(job_load_timing.socket_reused, resulting_load_timing.socket_reused); EXPECT_EQ(job_load_timing.socket_log_id, resulting_load_timing.socket_log_id); EXPECT_EQ(job_load_timing.send_start, resulting_load_timing.send_start); EXPECT_EQ(job_load_timing.send_end, resulting_load_timing.send_end); EXPECT_EQ(job_load_timing.receive_headers_start, resulting_load_timing.receive_headers_start); EXPECT_EQ(job_load_timing.receive_headers_end, resulting_load_timing.receive_headers_end); EXPECT_EQ(job_load_timing.push_start, resulting_load_timing.push_start); EXPECT_EQ(job_load_timing.push_end, resulting_load_timing.push_end); return resulting_load_timing; } // Basic test that the intercept + load timing tests work. TEST_F(URLRequestLoadTimingTest, InterceptLoadTiming) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, false); LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( job_load_timing, default_context(), interceptor()); // Nothing should have been changed by the URLRequest. EXPECT_EQ(job_load_timing.proxy_resolve_start, load_timing_result.proxy_resolve_start); EXPECT_EQ(job_load_timing.proxy_resolve_end, load_timing_result.proxy_resolve_end); EXPECT_EQ(job_load_timing.connect_timing.dns_start, load_timing_result.connect_timing.dns_start); EXPECT_EQ(job_load_timing.connect_timing.dns_end, load_timing_result.connect_timing.dns_end); EXPECT_EQ(job_load_timing.connect_timing.connect_start, load_timing_result.connect_timing.connect_start); EXPECT_EQ(job_load_timing.connect_timing.connect_end, load_timing_result.connect_timing.connect_end); EXPECT_EQ(job_load_timing.connect_timing.ssl_start, load_timing_result.connect_timing.ssl_start); EXPECT_EQ(job_load_timing.connect_timing.ssl_end, load_timing_result.connect_timing.ssl_end); // Redundant sanity check. TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_DNS_TIMES); } // Another basic test, with proxy and SSL times, but no DNS times. TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingProxy) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, true); LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( job_load_timing, default_context(), interceptor()); // Nothing should have been changed by the URLRequest. EXPECT_EQ(job_load_timing.proxy_resolve_start, load_timing_result.proxy_resolve_start); EXPECT_EQ(job_load_timing.proxy_resolve_end, load_timing_result.proxy_resolve_end); EXPECT_EQ(job_load_timing.connect_timing.dns_start, load_timing_result.connect_timing.dns_start); EXPECT_EQ(job_load_timing.connect_timing.dns_end, load_timing_result.connect_timing.dns_end); EXPECT_EQ(job_load_timing.connect_timing.connect_start, load_timing_result.connect_timing.connect_start); EXPECT_EQ(job_load_timing.connect_timing.connect_end, load_timing_result.connect_timing.connect_end); EXPECT_EQ(job_load_timing.connect_timing.ssl_start, load_timing_result.connect_timing.ssl_start); EXPECT_EQ(job_load_timing.connect_timing.ssl_end, load_timing_result.connect_timing.ssl_end); // Redundant sanity check. TestLoadTimingNotReusedWithProxy(load_timing_result, CONNECT_TIMING_HAS_SSL_TIMES); } // Make sure that URLRequest correctly adjusts proxy times when they're before // |request_start|, due to already having a connected socket. This happens in // the case of reusing a SPDY session. The connected socket is not considered // reused in this test (May be a preconnect). // // To mix things up from the test above, assumes DNS times but no SSL times. TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyProxyResolution) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, true); job_load_timing.proxy_resolve_start = now - base::Days(6); job_load_timing.proxy_resolve_end = now - base::Days(5); job_load_timing.connect_timing.dns_start = now - base::Days(4); job_load_timing.connect_timing.dns_end = now - base::Days(3); job_load_timing.connect_timing.connect_start = now - base::Days(2); job_load_timing.connect_timing.connect_end = now - base::Days(1); LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( job_load_timing, default_context(), interceptor()); // Proxy times, connect times, and DNS times should all be replaced with // request_start. EXPECT_EQ(load_timing_result.request_start, load_timing_result.proxy_resolve_start); EXPECT_EQ(load_timing_result.request_start, load_timing_result.proxy_resolve_end); EXPECT_EQ(load_timing_result.request_start, load_timing_result.connect_timing.dns_start); EXPECT_EQ(load_timing_result.request_start, load_timing_result.connect_timing.dns_end); EXPECT_EQ(load_timing_result.request_start, load_timing_result.connect_timing.connect_start); EXPECT_EQ(load_timing_result.request_start, load_timing_result.connect_timing.connect_end); // Other times should have been left null. TestLoadTimingNotReusedWithProxy(load_timing_result, CONNECT_TIMING_HAS_DNS_TIMES); } // Same as above, but in the reused case. TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyProxyResolutionReused) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfoReused(now, true); job_load_timing.proxy_resolve_start = now - base::Days(4); job_load_timing.proxy_resolve_end = now - base::Days(3); LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( job_load_timing, default_context(), interceptor()); // Proxy times and connect times should all be replaced with request_start. EXPECT_EQ(load_timing_result.request_start, load_timing_result.proxy_resolve_start); EXPECT_EQ(load_timing_result.request_start, load_timing_result.proxy_resolve_end); // Other times should have been left null. TestLoadTimingReusedWithProxy(load_timing_result); } // Make sure that URLRequest correctly adjusts connect times when they're before // |request_start|, due to reusing a connected socket. The connected socket is // not considered reused in this test (May be a preconnect). // // To mix things up, the request has SSL times, but no DNS times. TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyConnect) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, false); job_load_timing.connect_timing.connect_start = now - base::Days(1); job_load_timing.connect_timing.ssl_start = now - base::Days(2); job_load_timing.connect_timing.ssl_end = now - base::Days(3); job_load_timing.connect_timing.connect_end = now - base::Days(4); LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( job_load_timing, default_context(), interceptor()); // Connect times, and SSL times should be replaced with request_start. EXPECT_EQ(load_timing_result.request_start, load_timing_result.connect_timing.connect_start); EXPECT_EQ(load_timing_result.request_start, load_timing_result.connect_timing.ssl_start); EXPECT_EQ(load_timing_result.request_start, load_timing_result.connect_timing.ssl_end); EXPECT_EQ(load_timing_result.request_start, load_timing_result.connect_timing.connect_end); // Other times should have been left null. TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_SSL_TIMES); } // Make sure that URLRequest correctly adjusts connect times when they're before // |request_start|, due to reusing a connected socket in the case that there // are also proxy times. The connected socket is not considered reused in this // test (May be a preconnect). // // In this test, there are no SSL or DNS times. TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyConnectWithProxy) { base::TimeTicks now = base::TimeTicks::Now(); LoadTimingInfo job_load_timing = NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY, true); job_load_timing.connect_timing.connect_start = now - base::Days(1); job_load_timing.connect_timing.connect_end = now - base::Days(2); LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( job_load_timing, default_context(), interceptor()); // Connect times should be replaced with proxy_resolve_end. EXPECT_EQ(load_timing_result.proxy_resolve_end, load_timing_result.connect_timing.connect_start); EXPECT_EQ(load_timing_result.proxy_resolve_end, load_timing_result.connect_timing.connect_end); // Other times should have been left null. TestLoadTimingNotReusedWithProxy(load_timing_result, CONNECT_TIMING_HAS_CONNECT_TIMES_ONLY); } TEST_F(URLRequestTest, NetworkDelegateProxyError) { auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_proxy_resolution_service( CreateFixedProxyResolutionService("myproxy:70")); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto host_resolver = std::make_unique(); host_resolver->rules()->AddSimulatedTimeoutFailure("*"); context_builder->set_host_resolver(std::move(host_resolver)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_method("GET"); req->Start(); d.RunUntilComplete(); // Check we see a failed request. // The proxy server should be set before failure. EXPECT_EQ(PacResultElementToProxyServer("PROXY myproxy:70"), req->proxy_server()); EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, d.request_status()); EXPECT_THAT(req->response_info().resolve_error_info.error, IsError(ERR_DNS_TIMED_OUT)); EXPECT_EQ(1, network_delegate.error_count()); EXPECT_THAT(network_delegate.last_error(), IsError(ERR_PROXY_CONNECTION_FAILED)); EXPECT_EQ(1, network_delegate.completed_requests()); } // Test that when host resolution fails with `ERR_DNS_NAME_HTTPS_ONLY` for // "http://" requests, scheme is upgraded to "https://". TEST_F(URLRequestTest, DnsNameHttpsOnlyErrorCausesSchemeUpgrade) { base::test::ScopedFeatureList features; features.InitAndEnableFeatureWithParameters( features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}}); EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(https_server.Start()); // Build an http URL that should be auto-upgraded to https. const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES. const GURL https_url = https_server.GetURL(kHost, "/defaultresponse"); GURL::Replacements replacements; replacements.SetSchemeStr(url::kHttpScheme); const GURL http_url = https_url.ReplaceComponents(replacements); // Return `ERR_DNS_NAME_HTTPS_ONLY` for "http://" requests and an address for // "https://" requests. This simulates the HostResolver behavior for a domain // with an HTTPS DNS record. auto host_resolver = std::make_unique(); MockHostResolverBase::RuleResolver::RuleKey unencrypted_resolve_key; unencrypted_resolve_key.scheme = url::kHttpScheme; unencrypted_resolve_key.hostname_pattern = kHost; host_resolver->rules()->AddRule(std::move(unencrypted_resolve_key), ERR_DNS_NAME_HTTPS_ONLY); MockHostResolverBase::RuleResolver::RuleKey encrypted_resolve_key; encrypted_resolve_key.scheme = url::kHttpsScheme; encrypted_resolve_key.hostname_pattern = kHost; host_resolver->rules()->AddRule(std::move(encrypted_resolve_key), https_server.GetIPLiteralString()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_host_resolver(std::move(host_resolver)); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( http_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_FALSE(req->url().SchemeIsCryptographic()); // Note that there is no http server running, so the request should fail or // hang if its scheme is not upgraded to https. req->Start(); d.RunUntilComplete(); EXPECT_EQ(d.received_redirect_count(), 1); EXPECT_EQ(0, network_delegate.error_count()); EXPECT_EQ(200, req->GetResponseCode()); ASSERT_TRUE(req->response_headers()); EXPECT_EQ(200, req->response_headers()->response_code()); // Observe that the scheme has been upgraded to https. EXPECT_TRUE(req->url().SchemeIsCryptographic()); EXPECT_TRUE(req->url().SchemeIs(url::kHttpsScheme)); } // Test that DNS-based scheme upgrade supports deferred redirect. TEST_F(URLRequestTest, DnsNameHttpsOnlyErrorCausesSchemeUpgradeDeferred) { base::test::ScopedFeatureList features; features.InitAndEnableFeatureWithParameters( features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}}); EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(https_server.Start()); // Build an http URL that should be auto-upgraded to https. const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES. const GURL https_url = https_server.GetURL(kHost, "/defaultresponse"); GURL::Replacements replacements; replacements.SetSchemeStr(url::kHttpScheme); const GURL http_url = https_url.ReplaceComponents(replacements); // Return `ERR_DNS_NAME_HTTPS_ONLY` for "http://" requests and an address for // "https://" requests. This simulates the HostResolver behavior for a domain // with an HTTPS DNS record. auto host_resolver = std::make_unique(); MockHostResolverBase::RuleResolver::RuleKey unencrypted_resolve_key; unencrypted_resolve_key.scheme = url::kHttpScheme; unencrypted_resolve_key.hostname_pattern = kHost; host_resolver->rules()->AddRule(std::move(unencrypted_resolve_key), ERR_DNS_NAME_HTTPS_ONLY); MockHostResolverBase::RuleResolver::RuleKey encrypted_resolve_key; encrypted_resolve_key.scheme = url::kHttpsScheme; encrypted_resolve_key.hostname_pattern = kHost; host_resolver->rules()->AddRule(std::move(encrypted_resolve_key), https_server.GetIPLiteralString()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_host_resolver(std::move(host_resolver)); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( http_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_FALSE(req->url().SchemeIsCryptographic()); // Note that there is no http server running, so the request should fail or // hang if its scheme is not upgraded to https. req->Start(); d.RunUntilRedirect(); EXPECT_EQ(d.received_redirect_count(), 1); req->FollowDeferredRedirect(/*removed_headers=*/absl::nullopt, /*modified_headers=*/absl::nullopt); d.RunUntilComplete(); EXPECT_EQ(0, network_delegate.error_count()); EXPECT_EQ(200, req->GetResponseCode()); ASSERT_TRUE(req->response_headers()); EXPECT_EQ(200, req->response_headers()->response_code()); // Observe that the scheme has been upgraded to https. EXPECT_TRUE(req->url().SchemeIsCryptographic()); EXPECT_TRUE(req->url().SchemeIs(url::kHttpsScheme)); } #if BUILDFLAG(ENABLE_WEBSOCKETS) // Test that requests with "ws" scheme are upgraded to "wss" when DNS // indicates that the name is HTTPS-only. TEST_F(URLRequestTest, DnsHttpsRecordPresentCausesWsSchemeUpgrade) { base::test::ScopedFeatureList features; features.InitAndEnableFeatureWithParameters( features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}}); EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(https_server.Start()); // Build an http URL that should be auto-upgraded to https. const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES. const GURL https_url = https_server.GetURL(kHost, "/defaultresponse"); GURL::Replacements replacements; replacements.SetSchemeStr(url::kWsScheme); const GURL ws_url = https_url.ReplaceComponents(replacements); auto host_resolver = std::make_unique(); MockHostResolverBase::RuleResolver::RuleKey unencrypted_resolve_key; unencrypted_resolve_key.scheme = url::kHttpScheme; unencrypted_resolve_key.hostname_pattern = kHost; host_resolver->rules()->AddRule(std::move(unencrypted_resolve_key), ERR_DNS_NAME_HTTPS_ONLY); MockHostResolverBase::RuleResolver::RuleKey encrypted_resolve_key; encrypted_resolve_key.scheme = url::kHttpsScheme; encrypted_resolve_key.hostname_pattern = kHost; host_resolver->rules()->AddRule(std::move(encrypted_resolve_key), https_server.GetIPLiteralString()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_host_resolver(std::move(host_resolver)); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( ws_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS, /*is_for_websockets=*/true)); EXPECT_FALSE(req->url().SchemeIsCryptographic()); HttpRequestHeaders headers = WebSocketCommonTestHeaders(); req->SetExtraRequestHeaders(headers); auto websocket_stream_create_helper = std::make_unique(); req->SetUserData(kWebSocketHandshakeUserDataKey, std::move(websocket_stream_create_helper)); // Note that there is no ws server running, so the request should fail or hang // if its scheme is not upgraded to wss. req->Start(); d.RunUntilComplete(); EXPECT_EQ(d.received_redirect_count(), 1); // Expect failure because test server is not set up to provide websocket // responses. EXPECT_EQ(network_delegate.error_count(), 1); EXPECT_EQ(network_delegate.last_error(), ERR_INVALID_RESPONSE); // Observe that the scheme has been upgraded to wss. EXPECT_TRUE(req->url().SchemeIsCryptographic()); EXPECT_TRUE(req->url().SchemeIs(url::kWssScheme)); } #endif // BUILDFLAG(ENABLE_WEBSOCKETS) TEST_F(URLRequestTest, DnsHttpsRecordAbsentNoSchemeUpgrade) { base::test::ScopedFeatureList features; features.InitAndEnableFeatureWithParameters( features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}}); EmbeddedTestServer http_server(EmbeddedTestServer::TYPE_HTTP); RegisterDefaultHandlers(&http_server); ASSERT_TRUE(http_server.Start()); // Build an http URL that should be auto-upgraded to https. const std::string kHost = "foo.a.test"; // Covered by CERT_TEST_NAMES. const GURL http_url = http_server.GetURL(kHost, "/defaultresponse"); auto context_builder = CreateTestURLRequestContextBuilder(); auto host_resolver = std::make_unique(); host_resolver->rules()->AddRule(kHost, http_server.GetIPLiteralString()); context_builder->set_host_resolver(std::move(host_resolver)); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( http_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_FALSE(req->url().SchemeIsCryptographic()); req->Start(); d.RunUntilComplete(); EXPECT_EQ(d.received_redirect_count(), 0); EXPECT_EQ(0, network_delegate.error_count()); EXPECT_EQ(200, req->GetResponseCode()); ASSERT_TRUE(req->response_headers()); EXPECT_EQ(200, req->response_headers()->response_code()); // Observe that the scheme has not been upgraded. EXPECT_EQ(http_url, req->url()); EXPECT_FALSE(req->url().SchemeIsCryptographic()); EXPECT_TRUE(req->url().SchemeIs(url::kHttpScheme)); } TEST_F(URLRequestTest, SkipSecureDnsDisabledByDefault) { auto context_builder = CreateTestURLRequestContextBuilder(); auto host_resolver = std::make_unique(); host_resolver->rules()->AddRule("example.com", "127.0.0.1"); context_builder->set_host_resolver(std::move(host_resolver)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(SecureDnsPolicy::kAllow, static_cast(context->host_resolver()) ->last_secure_dns_policy()); } TEST_F(URLRequestTest, SkipSecureDnsEnabled) { auto context_builder = CreateTestURLRequestContextBuilder(); auto host_resolver = std::make_unique(); host_resolver->rules()->AddRule("example.com", "127.0.0.1"); context_builder->set_host_resolver(std::move(host_resolver)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetSecureDnsPolicy(SecureDnsPolicy::kDisable); req->Start(); d.RunUntilComplete(); EXPECT_EQ(SecureDnsPolicy::kDisable, static_cast(context->host_resolver()) ->last_secure_dns_policy()); } // Make sure that NetworkDelegate::NotifyCompleted is called if // content is empty. TEST_F(URLRequestTest, RequestCompletionForEmptyResponse) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL("/nocontent"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_THAT(d.request_status(), IsOk()); EXPECT_EQ(204, req->GetResponseCode()); EXPECT_EQ("", d.data_received()); EXPECT_EQ(1, default_network_delegate().completed_requests()); } // Make sure that SetPriority actually sets the URLRequest's priority // correctly, both before and after start. TEST_F(URLRequestTest, SetPriorityBasic) { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); req->SetPriority(LOW); EXPECT_EQ(LOW, req->priority()); req->Start(); EXPECT_EQ(LOW, req->priority()); req->SetPriority(MEDIUM); EXPECT_EQ(MEDIUM, req->priority()); } // Make sure that URLRequest calls SetPriority on a job before calling // Start on it. TEST_F(URLRequestTest, SetJobPriorityBeforeJobStart) { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); RequestPriority job_priority; std::unique_ptr job = std::make_unique(req.get(), &job_priority); TestScopedURLInterceptor interceptor(req->url(), std::move(job)); EXPECT_EQ(DEFAULT_PRIORITY, job_priority); req->SetPriority(LOW); req->Start(); EXPECT_EQ(LOW, job_priority); } // Make sure that URLRequest passes on its priority updates to its // job. TEST_F(URLRequestTest, SetJobPriority) { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); RequestPriority job_priority; std::unique_ptr job = std::make_unique(req.get(), &job_priority); TestScopedURLInterceptor interceptor(req->url(), std::move(job)); req->SetPriority(LOW); req->Start(); EXPECT_EQ(LOW, job_priority); req->SetPriority(MEDIUM); EXPECT_EQ(MEDIUM, req->priority()); EXPECT_EQ(MEDIUM, job_priority); } // Setting the IGNORE_LIMITS load flag should be okay if the priority // is MAXIMUM_PRIORITY. TEST_F(URLRequestTest, PriorityIgnoreLimits) { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( GURL("http://test_intercept/foo"), MAXIMUM_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); RequestPriority job_priority; std::unique_ptr job = std::make_unique(req.get(), &job_priority); TestScopedURLInterceptor interceptor(req->url(), std::move(job)); req->SetLoadFlags(LOAD_IGNORE_LIMITS); EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); req->SetPriority(MAXIMUM_PRIORITY); EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); req->Start(); EXPECT_EQ(MAXIMUM_PRIORITY, req->priority()); EXPECT_EQ(MAXIMUM_PRIORITY, job_priority); } // This test verifies that URLRequest::Delegate's OnConnected() callback is // never called if the request fails before connecting to a remote endpoint. TEST_F(URLRequestTest, NotifyDelegateConnectedSkippedOnEarlyFailure) { TestDelegate delegate; // The request will never connect to anything because the URL is invalid. auto request = default_context().CreateRequest(GURL("invalid url"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); request->Start(); delegate.RunUntilComplete(); EXPECT_THAT(delegate.transports(), IsEmpty()); } // This test verifies that URLRequest::Delegate's OnConnected() method // is called once for simple redirect-less requests. TEST_F(URLRequestTest, OnConnected) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); TestDelegate delegate; auto request = default_context().CreateRequest(test_server.GetURL("/echo"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); request->Start(); delegate.RunUntilComplete(); TransportInfo expected_transport; expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); // Make sure URL_REQUEST_DELEGATE_CONNECTED is logged correctly. auto entries = net_log_observer_.GetEntries(); size_t start_event_index = ExpectLogContainsSomewhere( entries, /*min_offset=*/0, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::BEGIN); size_t end_event_index = ExpectLogContainsSomewhereAfter( entries, /*start_offset=*/start_event_index, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::END); EXPECT_FALSE(LogContainsEntryWithTypeAfter( entries, end_event_index + 1, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED)); ASSERT_LT(end_event_index, entries.size()); EXPECT_FALSE(GetOptionalNetErrorCodeFromParams(entries[end_event_index])); } // This test verifies that URLRequest::Delegate's OnConnected() method is // called after each redirect. TEST_F(URLRequestTest, OnConnectedRedirect) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); TestDelegate delegate; // Fetch a page that redirects us once. GURL url = test_server.GetURL("/server-redirect?" + test_server.GetURL("/echo").spec()); auto request = default_context().CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); request->Start(); delegate.RunUntilRedirect(); TransportInfo expected_transport; expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); request->FollowDeferredRedirect(/*removed_headers=*/{}, /*modified_headers=*/{}); delegate.RunUntilComplete(); EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport, expected_transport)); } // This test verifies that when the URLRequest Delegate returns an error from // OnConnected(), the entire request fails with that error. TEST_F(URLRequestTest, OnConnectedError) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); TestDelegate delegate; delegate.set_on_connected_result(ERR_NOT_IMPLEMENTED); auto request = default_context().CreateRequest(test_server.GetURL("/echo"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); request->Start(); delegate.RunUntilComplete(); TransportInfo expected_transport; expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); EXPECT_TRUE(delegate.request_failed()); EXPECT_THAT(delegate.request_status(), IsError(ERR_NOT_IMPLEMENTED)); // Make sure URL_REQUEST_DELEGATE_CONNECTED is logged correctly. auto entries = net_log_observer_.GetEntries(); size_t start_event_index = ExpectLogContainsSomewhere( entries, /*min_offset=*/0, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::BEGIN); size_t end_event_index = ExpectLogContainsSomewhereAfter( entries, /*start_offset=*/start_event_index, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::END); EXPECT_FALSE(LogContainsEntryWithTypeAfter( entries, end_event_index + 1, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED)); ASSERT_LT(end_event_index, entries.size()); EXPECT_EQ(ERR_NOT_IMPLEMENTED, GetOptionalNetErrorCodeFromParams(entries[end_event_index])); } TEST_F(URLRequestTest, OnConnectedAsync) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); TestDelegate d; d.set_on_connected_run_callback(true); d.set_on_connected_result(OK); std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/defaultresponse"), &d); req->Start(); d.RunUntilComplete(); EXPECT_THAT(d.request_status(), IsOk()); // Make sure URL_REQUEST_DELEGATE_CONNECTED is logged correctly. auto entries = net_log_observer_.GetEntries(); size_t start_event_index = ExpectLogContainsSomewhere( entries, /*min_offset=*/0, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::BEGIN); size_t end_event_index = ExpectLogContainsSomewhereAfter( entries, /*start_offset=*/start_event_index, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::END); EXPECT_FALSE(LogContainsEntryWithTypeAfter( entries, end_event_index + 1, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED)); ASSERT_LT(end_event_index, entries.size()); EXPECT_FALSE(GetOptionalNetErrorCodeFromParams(entries[end_event_index])); } TEST_F(URLRequestTest, OnConnectedAsyncError) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); TestDelegate d; d.set_on_connected_run_callback(true); d.set_on_connected_result(ERR_NOT_IMPLEMENTED); std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/defaultresponse"), &d); req->Start(); d.RunUntilComplete(); EXPECT_THAT(d.request_status(), IsError(ERR_NOT_IMPLEMENTED)); // Make sure URL_REQUEST_DELEGATE_CONNECTED is logged correctly. auto entries = net_log_observer_.GetEntries(); size_t start_event_index = ExpectLogContainsSomewhere( entries, /*min_offset=*/0, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::BEGIN); size_t end_event_index = ExpectLogContainsSomewhereAfter( entries, /*start_offset=*/start_event_index, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED, NetLogEventPhase::END); EXPECT_FALSE(LogContainsEntryWithTypeAfter( entries, end_event_index + 1, NetLogEventType::URL_REQUEST_DELEGATE_CONNECTED)); ASSERT_LT(end_event_index, entries.size()); EXPECT_EQ(ERR_NOT_IMPLEMENTED, GetOptionalNetErrorCodeFromParams(entries[end_event_index])); } TEST_F(URLRequestTest, DelayedCookieCallback) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCookieStore(std::make_unique()); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); // Set up a cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( *context, test_server.GetURL("/set-cookie?CookieToNotSend=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, network_delegate.blocked_annotate_cookies_count()); EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); EXPECT_EQ(1, network_delegate.set_cookie_count()); } // Verify that the cookie is set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( *context, test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != std::string::npos); EXPECT_EQ(0, network_delegate.blocked_annotate_cookies_count()); EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); } } TEST_F(URLRequestTest, DelayedCookieCallbackAsync) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // Add a secure cookie so we can try to set an insecure cookie and have // SetCanonicalCookie fail. GURL::Replacements replace_scheme; replace_scheme.SetSchemeStr("https"); GURL url = test_server.base_url().ReplaceComponents(replace_scheme); auto cookie1 = CanonicalCookie::Create( url, "AlreadySetCookie=1;Secure", base::Time::Now(), absl::nullopt /* server_time */, absl::nullopt /* cookie_partition_key */); auto delayed_cm = std::make_unique(); delayed_cm->SetCanonicalCookieAsync(std::move(cookie1), url, net::CookieOptions::MakeAllInclusive(), CookieStore::SetCookiesCallback()); auto cookie2 = CanonicalCookie::Create( url, "AlreadySetCookie=1;Secure", base::Time::Now(), absl::nullopt /* server_time */, absl::nullopt /* cookie_partition_key */); auto cm = std::make_unique(nullptr, nullptr, /*first_party_sets_enabled=*/false); cm->SetCanonicalCookieAsync(std::move(cookie2), url, net::CookieOptions::MakeAllInclusive(), CookieStore::SetCookiesCallback()); auto async_context_builder = CreateTestURLRequestContextBuilder(); async_context_builder->SetCookieStore(std::move(delayed_cm)); auto& async_filter_network_delegate = *async_context_builder->set_network_delegate( std::make_unique()); auto async_context = async_context_builder->Build(); async_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie"); TestDelegate async_delegate; auto sync_context_builder = CreateTestURLRequestContextBuilder(); sync_context_builder->SetCookieStore(std::move(cm)); auto& sync_filter_network_delegate = *sync_context_builder->set_network_delegate( std::make_unique()); auto sync_context = sync_context_builder->Build(); sync_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie"); TestDelegate sync_delegate; std::vector cookie_lines( {// Fails in SetCanonicalCookie for trying to set a secure cookie // on an insecure host. "CookieNotSet=1;Secure", // Fail in FilteringTestNetworkDelegate::CanGetCookie. "CookieBlockedOnCanGetCookie=1", // Fails in SetCanonicalCookie for trying to overwrite a secure cookie // with an insecure cookie. "AlreadySetCookie=1", // Succeeds and added cookie to store. Delayed (which makes the callback // run asynchronously) in DelayedCookieMonster. "CookieSet=1"}); for (auto first_cookie_line : cookie_lines) { for (auto second_cookie_line : cookie_lines) { // Run with the delayed cookie monster. std::unique_ptr request = CreateFirstPartyRequest( *async_context, test_server.GetURL("/set-cookie?" + first_cookie_line + "&" + second_cookie_line), &async_delegate); request->Start(); async_delegate.RunUntilComplete(); EXPECT_THAT(async_delegate.request_status(), IsOk()); // Run with the regular cookie monster. request = CreateFirstPartyRequest( *sync_context, test_server.GetURL("/set-cookie?" + first_cookie_line + "&" + second_cookie_line), &sync_delegate); request->Start(); sync_delegate.RunUntilComplete(); EXPECT_THAT(sync_delegate.request_status(), IsOk()); int expected_set_cookie_count = 0; int expected_blocked_cookie_count = 0; // 2 calls to the delegate's OnCanSetCookie method are expected, even if // the cookies don't end up getting set. expected_set_cookie_count += 2; if (first_cookie_line == "CookieBlockedOnCanGetCookie=1") ++expected_blocked_cookie_count; if (second_cookie_line == "CookieBlockedOnCanGetCookie=1") ++expected_blocked_cookie_count; EXPECT_EQ(expected_set_cookie_count, async_filter_network_delegate.set_cookie_called_count()); EXPECT_EQ(expected_blocked_cookie_count, async_filter_network_delegate.blocked_set_cookie_count()); EXPECT_EQ(expected_set_cookie_count, sync_filter_network_delegate.set_cookie_called_count()); EXPECT_EQ(expected_blocked_cookie_count, sync_filter_network_delegate.blocked_set_cookie_count()); async_filter_network_delegate.ResetSetCookieCalledCount(); async_filter_network_delegate.ResetBlockedSetCookieCount(); sync_filter_network_delegate.ResetSetCookieCalledCount(); sync_filter_network_delegate.ResetBlockedSetCookieCount(); } } } TEST_F(URLRequestTest, DoNotSendCookies) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // Set up a cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotSend=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the cookie is set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the cookie isn't sent when credentials are not allowed. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->set_allow_credentials(false); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == std::string::npos); // When credentials are blocked, OnAnnotateAndMoveUserBlockedCookies() is // not invoked. EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } } TEST_F(URLRequestTest, DoNotSaveCookies) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // Set up a cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); EXPECT_EQ(1, default_network_delegate().set_cookie_count()); } // Try to set-up another cookie and update the previous cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), &d); req->SetLoadFlags(LOAD_DO_NOT_SAVE_COOKIES); req->Start(); d.RunUntilComplete(); // LOAD_DO_NOT_SAVE_COOKIES does not trigger OnSetCookie. EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); EXPECT_EQ(1, default_network_delegate().set_cookie_count()); } // Verify the cookies weren't saved or updated. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") == std::string::npos); EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") != std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); EXPECT_EQ(1, default_network_delegate().set_cookie_count()); } } TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // Set up a cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotSend=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the cookie is set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); auto entries = net_log_observer_.GetEntries(); for (const auto& entry : entries) { EXPECT_NE(entry.type, NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE); } } // Verify that the cookie isn't sent. { TestDelegate d; default_network_delegate().set_cookie_options( TestNetworkDelegate::NO_GET_COOKIES); std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == std::string::npos); EXPECT_EQ(1, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); auto entries = net_log_observer_.GetEntries(); ExpectLogContainsSomewhereAfter( entries, 0, NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE, NetLogEventPhase::NONE); } } // TODO(crbug.com/564656) This test is flaky on iOS. #if BUILDFLAG(IS_IOS) #define MAYBE_DoNotSaveCookies_ViaPolicy FLAKY_DoNotSaveCookies_ViaPolicy #else #define MAYBE_DoNotSaveCookies_ViaPolicy DoNotSaveCookies_ViaPolicy #endif TEST_F(URLRequestTest, MAYBE_DoNotSaveCookies_ViaPolicy) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // Set up a cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); auto entries = net_log_observer_.GetEntries(); for (const auto& entry : entries) { EXPECT_NE(entry.type, NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE); } } // Try to set-up another cookie and update the previous cookie. { TestDelegate d; default_network_delegate().set_cookie_options( TestNetworkDelegate::NO_SET_COOKIE); std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(2, default_network_delegate().blocked_set_cookie_count()); auto entries = net_log_observer_.GetEntries(); ExpectLogContainsSomewhereAfter( entries, 0, NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE, NetLogEventPhase::NONE); } // Verify the cookies weren't saved or updated. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") == std::string::npos); EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") != std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(2, default_network_delegate().blocked_set_cookie_count()); } } TEST_F(URLRequestTest, DoNotSaveEmptyCookies) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // Set up an empty cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); EXPECT_EQ(0, default_network_delegate().set_cookie_count()); } } TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy_Async) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // Set up a cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotSend=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the cookie is set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the cookie isn't sent. { TestDelegate d; default_network_delegate().set_cookie_options( TestNetworkDelegate::NO_GET_COOKIES); std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == std::string::npos); EXPECT_EQ(1, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } } TEST_F(URLRequestTest, DoNotSaveCookies_ViaPolicy_Async) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // Set up a cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Try to set-up another cookie and update the previous cookie. { TestDelegate d; default_network_delegate().set_cookie_options( TestNetworkDelegate::NO_SET_COOKIE); std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(2, default_network_delegate().blocked_set_cookie_count()); } // Verify the cookies weren't saved or updated. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("CookieToNotSave=1") == std::string::npos); EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2") != std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(2, default_network_delegate().blocked_set_cookie_count()); } } // Tests for SameSite cookies. The test param indicates whether the same-site // calculation considers redirect chains. class URLRequestSameSiteCookiesTest : public URLRequestTest, public ::testing::WithParamInterface { public: URLRequestSameSiteCookiesTest() { if (DoesCookieSameSiteConsiderRedirectChain()) { feature_list_.InitAndEnableFeature( features::kCookieSameSiteConsidersRedirectChain); } } bool DoesCookieSameSiteConsiderRedirectChain() { return GetParam(); } private: base::test::ScopedFeatureList feature_list_; }; TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookies) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); const std::string kHost = "example.test"; const std::string kSubHost = "subdomain.example.test"; const std::string kCrossHost = "cross-origin.test"; const url::Origin kOrigin = url::Origin::Create(test_server.GetURL(kHost, "/")); const url::Origin kSubOrigin = url::Origin::Create(test_server.GetURL(kSubHost, "/")); const url::Origin kCrossOrigin = url::Origin::Create(test_server.GetURL(kCrossHost, "/")); const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); const SiteForCookies kCrossSiteForCookies = SiteForCookies::FromOrigin(kCrossOrigin); // Set up two 'SameSite' cookies on 'example.test' { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), test_server.GetURL(kHost, "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" "LaxSameSiteCookie=1;SameSite=Lax"), &d); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); EXPECT_EQ(2, default_network_delegate().set_cookie_count()); } // Verify that both cookies are sent for same-site requests, whether they are // subresource requests, subframe navigations, or main frame navigations. for (IsolationInfo::RequestType request_type : {IsolationInfo::RequestType::kMainFrame, IsolationInfo::RequestType::kSubFrame, IsolationInfo::RequestType::kOther}) { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create(request_type, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_NE(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that both cookies are sent when the request has no initiator (can // happen for main frame browser-initiated navigations). { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies(kSiteForCookies); req->Start(); d.RunUntilComplete(); EXPECT_NE(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that both cookies are sent for same-registrable-domain requests. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies( SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); req->set_initiator(kSubOrigin); req->Start(); d.RunUntilComplete(); EXPECT_NE(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that neither cookie is not sent for cross-site requests. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies(kCrossSiteForCookies); req->set_initiator(kCrossOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the lax cookie is sent for cross-site initiators when the // method is "safe" and the request is a main frame navigation. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kCrossOrigin); req->set_method("GET"); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the lax cookie is sent for cross-site initiators when the // method is "safe" and the request is being forced to be considered as a // main frame navigation. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kCrossOrigin); req->set_method("GET"); req->set_force_main_frame_for_same_site_cookies(true); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that neither cookie is sent for cross-site initiators when the // method is unsafe (e.g. POST), even if the request is a main frame // navigation. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kCrossOrigin); req->set_method("POST"); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that neither cookie is sent for cross-site initiators when the // method is safe and the site-for-cookies is same-site, but the request is // not a main frame navigation. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kSubFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kCrossOrigin); req->set_method("GET"); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); // Check that the appropriate cookie inclusion status is set. ASSERT_EQ(2u, req->maybe_sent_cookies().size()); CookieInclusionStatus expected_strict_status = CookieInclusionStatus::MakeFromReasonsForTesting( {CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT}, {} /* warning_reasons */); CookieInclusionStatus expected_lax_status = CookieInclusionStatus::MakeFromReasonsForTesting( {CookieInclusionStatus::EXCLUDE_SAMESITE_LAX}, {} /* warning_reasons */); EXPECT_EQ(expected_strict_status, req->maybe_sent_cookies()[0].access_result.status); EXPECT_EQ(expected_lax_status, req->maybe_sent_cookies()[1].access_result.status); } } TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookies_Redirect) { EmbeddedTestServer http_server; RegisterDefaultHandlers(&http_server); EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(http_server.Start()); ASSERT_TRUE(https_server.Start()); const std::string kHost = "foo.a.test"; const std::string kSameSiteHost = "bar.a.test"; const std::string kCrossSiteHost = "b.test"; const url::Origin kOrigin = url::Origin::Create(https_server.GetURL(kHost, "/")); const url::Origin kHttpOrigin = url::Origin::Create(http_server.GetURL(kHost, "/")); const url::Origin kSameSiteOrigin = url::Origin::Create(https_server.GetURL(kSameSiteHost, "/")); const url::Origin kCrossSiteOrigin = url::Origin::Create(https_server.GetURL(kCrossSiteHost, "/")); const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); const SiteForCookies kHttpSiteForCookies = SiteForCookies::FromOrigin(kHttpOrigin); const SiteForCookies kCrossSiteForCookies = SiteForCookies::FromOrigin(kCrossSiteOrigin); // Set up two 'SameSite' cookies on foo.a.test { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), https_server.GetURL( kHost, "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" "LaxSameSiteCookie=1;SameSite=Lax"), &d); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); ASSERT_EQ(2u, GetAllCookies(&default_context()).size()); } // Verify that both cookies are sent for same-site, unredirected requests. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( https_server.GetURL(kHost, "/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1u, req->url_chain().size()); EXPECT_NE(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); } // Verify that both cookies are sent for a same-origin redirected top level // navigation. { TestDelegate d; GURL url = https_server.GetURL( kHost, "/server-redirect?" + https_server.GetURL(kHost, "/echoheader?Cookie").spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(2u, req->url_chain().size()); EXPECT_NE(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); } // Verify that both cookies are sent for a same-site redirected top level // navigation. { TestDelegate d; GURL url = https_server.GetURL( kSameSiteHost, "/server-redirect?" + https_server.GetURL(kHost, "/echoheader?Cookie").spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kSameSiteOrigin, kSameSiteOrigin, kSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(2u, req->url_chain().size()); EXPECT_NE(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); } // If redirect chains are considered: // Verify that the Strict cookie may or may not be sent for a cross-scheme // (same-registrable-domain) redirected top level navigation, depending on the // status of Schemeful Same-Site. The Lax cookie is sent regardless, because // this is a top-level navigation. // // If redirect chains are not considered: // Verify that both cookies are sent, because this is a top-level navigation. { base::test::ScopedFeatureList feature_list; feature_list.InitAndDisableFeature(features::kSchemefulSameSite); TestDelegate d; GURL url = http_server.GetURL( kHost, "/server-redirect?" + https_server.GetURL(kHost, "/echoheader?Cookie").spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kHttpOrigin, kHttpOrigin, kHttpSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kHttpSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(2u, req->url_chain().size()); EXPECT_NE(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); } { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature(features::kSchemefulSameSite); TestDelegate d; GURL url = http_server.GetURL( kHost, "/server-redirect?" + https_server.GetURL(kHost, "/echoheader?Cookie").spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kHttpOrigin, kHttpOrigin, kHttpSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kHttpSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(2u, req->url_chain().size()); EXPECT_EQ( DoesCookieSameSiteConsiderRedirectChain(), std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); } // Verify that (depending on whether redirect chains are considered), the // Strict cookie is (not) sent for a cross-site redirected top level // navigation... { TestDelegate d; GURL url = https_server.GetURL( kCrossSiteHost, "/server-redirect?" + https_server.GetURL(kHost, "/echoheader?Cookie").spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kCrossSiteOrigin, kCrossSiteOrigin, kCrossSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kCrossSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(2u, req->url_chain().size()); EXPECT_EQ( DoesCookieSameSiteConsiderRedirectChain(), std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); } // ... even if the initial URL is same-site. { TestDelegate d; GURL middle_url = https_server.GetURL( kCrossSiteHost, "/server-redirect?" + https_server.GetURL(kHost, "/echoheader?Cookie").spec()); GURL url = https_server.GetURL(kHost, "/server-redirect?" + middle_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(3u, req->url_chain().size()); EXPECT_EQ( DoesCookieSameSiteConsiderRedirectChain(), std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); } // Verify that (depending on whether redirect chains are considered), neither // (or both) SameSite cookie is sent for a cross-site redirected subresource // request... { TestDelegate d; GURL url = https_server.GetURL( kCrossSiteHost, "/server-redirect?" + https_server.GetURL(kHost, "/echoheader?Cookie").spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(2u, req->url_chain().size()); EXPECT_EQ( DoesCookieSameSiteConsiderRedirectChain(), std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); EXPECT_EQ( DoesCookieSameSiteConsiderRedirectChain(), std::string::npos == d.data_received().find("LaxSameSiteCookie=1")); } // ... even if the initial URL is same-site. { TestDelegate d; GURL middle_url = https_server.GetURL( kCrossSiteHost, "/server-redirect?" + https_server.GetURL(kHost, "/echoheader?Cookie").spec()); GURL url = https_server.GetURL(kHost, "/server-redirect?" + middle_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); req->Start(); d.RunUntilComplete(); EXPECT_EQ(3u, req->url_chain().size()); EXPECT_EQ( DoesCookieSameSiteConsiderRedirectChain(), std::string::npos == d.data_received().find("StrictSameSiteCookie=1")); EXPECT_EQ( DoesCookieSameSiteConsiderRedirectChain(), std::string::npos == d.data_received().find("LaxSameSiteCookie=1")); } } TEST_P(URLRequestSameSiteCookiesTest, SettingSameSiteCookies) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); const std::string kHost = "example.test"; const std::string kSubHost = "subdomain.example.test"; const std::string kCrossHost = "cross-origin.test"; const url::Origin kOrigin = url::Origin::Create(test_server.GetURL(kHost, "/")); const url::Origin kSubOrigin = url::Origin::Create(test_server.GetURL(kSubHost, "/")); const url::Origin kCrossOrigin = url::Origin::Create(test_server.GetURL(kCrossHost, "/")); const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); const SiteForCookies kCrossSiteForCookies = SiteForCookies::FromOrigin(kCrossOrigin); int expected_cookies = 0; { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/set-cookie?Strict1=1;SameSite=Strict&" "Lax1=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); // 'SameSite' cookies are settable from strict same-site contexts // (same-origin site_for_cookies, same-origin initiator), so this request // should result in two cookies being set. expected_cookies += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_cookies, default_network_delegate().set_cookie_count()); } { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/set-cookie?Strict2=1;SameSite=Strict&" "Lax2=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kCrossOrigin); // 'SameSite' cookies are settable from lax same-site contexts (same-origin // site_for_cookies, cross-site initiator, main frame navigation), so this // request should result in two cookies being set. expected_cookies += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_cookies, default_network_delegate().set_cookie_count()); } { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/set-cookie?Strict3=1;SameSite=Strict&" "Lax3=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kSubOrigin, kSubOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies( SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); req->set_initiator(kCrossOrigin); // 'SameSite' cookies are settable from lax same-site contexts (same-site // site_for_cookies, cross-site initiator, main frame navigation), so this // request should result in two cookies being set. expected_cookies += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_cookies, default_network_delegate().set_cookie_count()); } { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/set-cookie?Strict4=1;SameSite=Strict&" "Lax4=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies( SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); // 'SameSite' cookies are settable from strict same-site contexts (same-site // site_for_cookies, no initiator), so this request should result in two // cookies being set. expected_cookies += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_cookies, default_network_delegate().set_cookie_count()); } int expected_network_delegate_set_cookie_count; { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/set-cookie?Strict5=1;SameSite=Strict&" "Lax5=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies(kCrossSiteForCookies); req->set_initiator(kCrossOrigin); // 'SameSite' cookies are not settable from cross-site contexts, so this // should not result in any new cookies being set. expected_cookies += 0; // This counts the number of successful calls to CanSetCookie() when // attempting to set a cookie. The two cookies above were created and // attempted to be set, and were not rejected by the NetworkDelegate, so the // count here is 2 more than the number of cookies actually set. expected_network_delegate_set_cookie_count = expected_cookies + 2; req->Start(); d.RunUntilComplete(); // This counts the number of cookies actually set. EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_network_delegate_set_cookie_count, default_network_delegate().set_cookie_count()); } { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/set-cookie?Strict6=1;SameSite=Strict&" "Lax6=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kSubFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kCrossOrigin); // Same-site site-for-cookies, cross-site initiator, non main frame // navigation -> context is considered cross-site so no SameSite cookies are // set. expected_cookies += 0; // This counts the number of successful calls to CanSetCookie() when // attempting to set a cookie. The two cookies above were created and // attempted to be set, and were not rejected by the NetworkDelegate, so the // count here is 2 more than the number of cookies actually set. expected_network_delegate_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_network_delegate_set_cookie_count, default_network_delegate().set_cookie_count()); // Check that the appropriate cookie inclusion status is set. ASSERT_EQ(2u, req->maybe_stored_cookies().size()); CookieInclusionStatus expected_strict_status = CookieInclusionStatus::MakeFromReasonsForTesting( {CookieInclusionStatus::EXCLUDE_SAMESITE_STRICT}, {} /* warning_reasons */); CookieInclusionStatus expected_lax_status = CookieInclusionStatus::MakeFromReasonsForTesting( {CookieInclusionStatus::EXCLUDE_SAMESITE_LAX}, {} /* warning_reasons */); EXPECT_EQ(expected_strict_status, req->maybe_stored_cookies()[0].access_result.status); EXPECT_EQ(expected_lax_status, req->maybe_stored_cookies()[1].access_result.status); } { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL(kHost, "/set-cookie?Strict7=1;SameSite=Strict&" "Lax7=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kCrossOrigin); req->set_force_main_frame_for_same_site_cookies(true); // 'SameSite' cookies are settable from lax same-site contexts (same-origin // site_for_cookies, cross-site initiator, main frame navigation), so this // request should result in two cookies being set. expected_cookies += 2; expected_network_delegate_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_network_delegate_set_cookie_count, default_network_delegate().set_cookie_count()); } } // Tests special chrome:// scheme that is supposed to always attach SameSite // cookies if the requested site is secure. TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookiesSpecialScheme) { url::ScopedSchemeRegistryForTests scoped_registry; url::AddStandardScheme("chrome", url::SchemeType::SCHEME_WITH_HOST); EmbeddedTestServer https_test_server(EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&https_test_server); ASSERT_TRUE(https_test_server.Start()); EmbeddedTestServer http_test_server(EmbeddedTestServer::TYPE_HTTP); RegisterDefaultHandlers(&http_test_server); ASSERT_TRUE(http_test_server.Start()); ASSERT_NE(https_test_server.port(), http_test_server.port()); // Both hostnames should be 127.0.0.1 (so that we can use the same set of // cookies on both, for convenience). ASSERT_EQ(https_test_server.host_port_pair().host(), http_test_server.host_port_pair().host()); // Set up special schemes auto cad = std::make_unique(); cad->SetIgnoreSameSiteRestrictionsScheme("chrome", true); auto cm = std::make_unique(nullptr, nullptr, /*first_party_sets_enabled=*/false); cm->SetCookieAccessDelegate(std::move(cad)); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCookieStore(std::move(cm)); auto context = context_builder->Build(); // SameSite cookies are not set for 'chrome' scheme if requested origin is not // secure. { TestDelegate d; std::unique_ptr req(context->CreateRequest( http_test_server.GetURL( "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" "LaxSameSiteCookie=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies( SiteForCookies::FromUrl(GURL("chrome://whatever/"))); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0u, GetAllCookies(context.get()).size()); } // But they are set for 'chrome' scheme if the requested origin is secure. { TestDelegate d; std::unique_ptr req(context->CreateRequest( https_test_server.GetURL( "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" "LaxSameSiteCookie=1;SameSite=Lax"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies( SiteForCookies::FromUrl(GURL("chrome://whatever/"))); req->Start(); d.RunUntilComplete(); CookieList cookies = GetAllCookies(context.get()); EXPECT_EQ(2u, cookies.size()); } // Verify that they are both sent when the site_for_cookies scheme is // 'chrome' and the requested origin is secure. { TestDelegate d; std::unique_ptr req(context->CreateRequest( https_test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies( SiteForCookies::FromUrl(GURL("chrome://whatever/"))); req->Start(); d.RunUntilComplete(); EXPECT_NE(std::string::npos, d.data_received().find("StrictSameSiteCookie=1")); EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); } // Verify that they are not sent when the site_for_cookies scheme is // 'chrome' and the requested origin is not secure. { TestDelegate d; std::unique_ptr req(context->CreateRequest( http_test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies( SiteForCookies::FromUrl(GURL("chrome://whatever/"))); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string::npos, d.data_received().find("StrictSameSiteCookie")); EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie")); } } TEST_P(URLRequestSameSiteCookiesTest, SettingSameSiteCookies_Redirect) { EmbeddedTestServer http_server; RegisterDefaultHandlers(&http_server); EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(http_server.Start()); ASSERT_TRUE(https_server.Start()); auto& network_delegate = default_network_delegate(); const std::string kHost = "foo.a.test"; const std::string kSameSiteHost = "bar.a.test"; const std::string kCrossSiteHost = "b.test"; const url::Origin kOrigin = url::Origin::Create(https_server.GetURL(kHost, "/")); const url::Origin kHttpOrigin = url::Origin::Create(http_server.GetURL(kHost, "/")); const url::Origin kSameSiteOrigin = url::Origin::Create(https_server.GetURL(kSameSiteHost, "/")); const url::Origin kCrossSiteOrigin = url::Origin::Create(https_server.GetURL(kCrossSiteHost, "/")); const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); const SiteForCookies kHttpSiteForCookies = SiteForCookies::FromOrigin(kHttpOrigin); const SiteForCookies kCrossSiteForCookies = SiteForCookies::FromOrigin(kCrossSiteOrigin); int expected_cookies = 0; int expected_set_cookie_count = 0; // Verify that SameSite cookies can be set for a same-origin redirected // top-level navigation request. { TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict1=1;SameSite=Strict&Lax1=1;SameSite=Lax"); GURL url = https_server.GetURL(kHost, "/server-redirect?" + set_cookie_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); expected_cookies += 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } // Verify that SameSite cookies can be set for a same-site redirected // top-level navigation request. { TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict2=1;SameSite=Strict&Lax2=1;SameSite=Lax"); GURL url = https_server.GetURL(kSameSiteHost, "/server-redirect?" + set_cookie_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kSameSiteOrigin, kSameSiteOrigin, kSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kSameSiteOrigin); expected_cookies += 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } // Verify that SameSite cookies can be set for a cross-site redirected // top-level navigation request. { TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict3=1;SameSite=Strict&Lax3=1;SameSite=Lax"); GURL url = https_server.GetURL(kCrossSiteHost, "/server-redirect?" + set_cookie_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kMainFrame, kCrossSiteOrigin, kCrossSiteOrigin, kCrossSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kCrossSiteForCookies); req->set_initiator(kCrossSiteOrigin); expected_cookies += 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } // Verify that SameSite cookies can be set for a same-origin redirected // subresource request. { TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict4=1;SameSite=Strict&Lax4=1;SameSite=Lax"); GURL url = https_server.GetURL(kHost, "/server-redirect?" + set_cookie_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); expected_cookies += 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } // Verify that SameSite cookies can be set for a same-site redirected // subresource request. { TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict5=1;SameSite=Strict&Lax5=1;SameSite=Lax"); GURL url = https_server.GetURL(kSameSiteHost, "/server-redirect?" + set_cookie_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kSameSiteOrigin, kSameSiteOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kSameSiteOrigin); expected_cookies += 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } // Verify that (depending on whether redirect chains are considered) SameSite // cookies can/cannot be set for a cross-site redirected subresource request, // even if the site-for-cookies and initiator are same-site, ... { TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict6=1;SameSite=Strict&Lax6=1;SameSite=Lax"); GURL url = https_server.GetURL(kCrossSiteHost, "/server-redirect?" + set_cookie_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } // ... even if the initial URL is same-site. { TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict7=1;SameSite=Strict&Lax7=1;SameSite=Lax"); GURL middle_url = https_server.GetURL( kCrossSiteHost, "/server-redirect?" + set_cookie_url.spec()); GURL url = https_server.GetURL(kHost, "/server-redirect?" + middle_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->set_site_for_cookies(kSiteForCookies); req->set_initiator(kOrigin); expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } // Verify that SameSite cookies may or may not be set for a cross-scheme // (same-registrable-domain) redirected subresource request, depending on the // status of Schemeful Same-Site and whether redirect chains are considered. { base::test::ScopedFeatureList feature_list; feature_list.InitAndDisableFeature(features::kSchemefulSameSite); TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict8=1;SameSite=Strict&Lax8=1;SameSite=Lax"); GURL url = http_server.GetURL(kHost, "/server-redirect?" + set_cookie_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kHttpOrigin, kHttpOrigin, kHttpSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kHttpSiteForCookies); req->set_initiator(kOrigin); expected_cookies += 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature(features::kSchemefulSameSite); TestDelegate d; GURL set_cookie_url = https_server.GetURL( kHost, "/set-cookie?Strict9=1;SameSite=Strict&Lax9=1;SameSite=Lax"); GURL url = http_server.GetURL(kHost, "/server-redirect?" + set_cookie_url.spec()); std::unique_ptr req(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(IsolationInfo::Create( IsolationInfo::RequestType::kOther, kHttpOrigin, kHttpOrigin, kHttpSiteForCookies, {} /* party_context */)); req->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); req->set_site_for_cookies(kHttpSiteForCookies); req->set_initiator(kOrigin); expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2; expected_set_cookie_count += 2; req->Start(); d.RunUntilComplete(); EXPECT_EQ(expected_cookies, static_cast(GetAllCookies(&default_context()).size())); EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count()); } } INSTANTIATE_TEST_SUITE_P(/* no label */, URLRequestSameSiteCookiesTest, ::testing::Bool()); // Tests that __Secure- cookies can't be set on non-secure origins. TEST_F(URLRequestTest, SecureCookiePrefixOnNonsecureOrigin) { EmbeddedTestServer http_server; RegisterDefaultHandlers(&http_server); EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(http_server.Start()); ASSERT_TRUE(https_server.Start()); // Try to set a Secure __Secure- cookie on http://a.test (non-secure origin). { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), http_server.GetURL("a.test", "/set-cookie?__Secure-nonsecure-origin=1;Secure&" "cookienotsecure=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the __Secure- cookie was not set by checking cookies for // https://a.test (secure origin). { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), https_server.GetURL("a.test", "/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(d.data_received().find("__Secure-nonsecure-origin=1"), std::string::npos); EXPECT_NE(d.data_received().find("cookienotsecure=1"), std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } } TEST_F(URLRequestTest, SecureCookiePrefixNonsecure) { EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(https_server.Start()); // Try to set a non-Secure __Secure- cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), https_server.GetURL("/set-cookie?__Secure-foo=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().set_cookie_count()); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the cookie is not set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), https_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(d.data_received().find("__Secure-foo=1"), std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } } TEST_F(URLRequestTest, SecureCookiePrefixSecure) { EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(https_server.Start()); // Try to set a Secure __Secure- cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), https_server.GetURL("/set-cookie?__Secure-bar=1;Secure"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the cookie is set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), https_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_NE(d.data_received().find("__Secure-bar=1"), std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } } // Tests that secure cookies can't be set on non-secure origins if strict secure // cookies are enabled. TEST_F(URLRequestTest, StrictSecureCookiesOnNonsecureOrigin) { EmbeddedTestServer http_server; RegisterDefaultHandlers(&http_server); EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); RegisterDefaultHandlers(&https_server); ASSERT_TRUE(http_server.Start()); ASSERT_TRUE(https_server.Start()); // Try to set a Secure cookie and a non-Secure cookie from a nonsecure origin. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), http_server.GetURL("a.test", "/set-cookie?nonsecure-origin=1;Secure&" "cookienotsecure=1"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } // Verify that the Secure cookie was not set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( default_context(), https_server.GetURL("a.test", "/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_EQ(d.data_received().find("nonsecure-origin=1"), std::string::npos); EXPECT_NE(d.data_received().find("cookienotsecure=1"), std::string::npos); EXPECT_EQ(0, default_network_delegate().blocked_annotate_cookies_count()); EXPECT_EQ(0, default_network_delegate().blocked_set_cookie_count()); } } // FixedDateNetworkDelegate swaps out the server's HTTP Date response header // value for the `fixed_date_` member. class FixedDateNetworkDelegate : public TestNetworkDelegate { public: explicit FixedDateNetworkDelegate(base::StringPiece fixed_date) : fixed_date_(fixed_date) {} FixedDateNetworkDelegate(const FixedDateNetworkDelegate&) = delete; FixedDateNetworkDelegate& operator=(const FixedDateNetworkDelegate&) = delete; ~FixedDateNetworkDelegate() override = default; void set_fixed_date(base::StringPiece fixed_date) { fixed_date_ = static_cast(fixed_date); } // NetworkDelegate implementation int OnHeadersReceived( URLRequest* request, CompletionOnceCallback callback, const HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, const IPEndPoint& endpoint, absl::optional* preserve_fragment_on_redirect_url) override; private: std::string fixed_date_; }; int FixedDateNetworkDelegate::OnHeadersReceived( URLRequest* request, CompletionOnceCallback callback, const HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, const IPEndPoint& endpoint, absl::optional* preserve_fragment_on_redirect_url) { *override_response_headers = base::MakeRefCounted( original_response_headers->raw_headers()); (*override_response_headers)->SetHeader("Date", fixed_date_); return TestNetworkDelegate::OnHeadersReceived( request, std::move(callback), original_response_headers, override_response_headers, endpoint, preserve_fragment_on_redirect_url); } // Test that cookie expiration times are adjusted for server/client clock // skew and that we handle incorrect timezone specifier "UTC" in HTTP Date // headers by defaulting to GMT. (crbug.com/135131) TEST_F(URLRequestTest, AcceptClockSkewCookieWithWrongDateTimezone) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique("04-Jan-2004 04:09:25 UTC")); auto context = context_builder->Build(); // Set up an expired cookie. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( *context, test_server.GetURL( "/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"), &d); req->Start(); d.RunUntilComplete(); } // Verify that the cookie is not set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( *context, test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("StillGood=1") == std::string::npos); } // Set up a cookie with clock skew and "UTC" HTTP Date timezone specifier. { TestDelegate d; network_delegate.set_fixed_date("18-Apr-1977 22:49:13 UTC"); std::unique_ptr req = CreateFirstPartyRequest( *context, test_server.GetURL( "/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"), &d); req->Start(); d.RunUntilComplete(); } // Verify that the cookie is set. { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest( *context, test_server.GetURL("/echoheader?Cookie"), &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("StillGood=1") != std::string::npos); } } // Check that it is impossible to change the referrer in the extra headers of // an URLRequest. TEST_F(URLRequestTest, DoNotOverrideReferrer) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); // If extra headers contain referer and the request contains a referer, // only the latter shall be respected. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer("http://foo.com/"); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kReferer, "http://bar.com/"); req->SetExtraRequestHeaders(headers); req->Start(); d.RunUntilComplete(); EXPECT_EQ("http://foo.com/", d.data_received()); } // If extra headers contain a referer but the request does not, no referer // shall be sent in the header. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( test_server.GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kReferer, "http://bar.com/"); req->SetExtraRequestHeaders(headers); req->SetLoadFlags(LOAD_VALIDATE_CACHE); req->Start(); d.RunUntilComplete(); EXPECT_EQ("None", d.data_received()); } } class URLRequestTestHTTP : public URLRequestTest { public: const url::Origin origin1_; const url::Origin origin2_; const IsolationInfo isolation_info1_; const IsolationInfo isolation_info2_; URLRequestTestHTTP() : origin1_(url::Origin::Create(GURL("https://foo.test/"))), origin2_(url::Origin::Create(GURL("https://bar.test/"))), isolation_info1_(IsolationInfo::CreateForInternalRequest(origin1_)), isolation_info2_(IsolationInfo::CreateForInternalRequest(origin2_)), test_server_(base::FilePath(kTestFilePath)) { // Needed for NetworkIsolationKey to make it down to the socket layer, for // the PKP violation report test. feature_list_.InitAndEnableFeature( net::features::kPartitionConnectionsByNetworkIsolationKey); } protected: // ProtocolHandler for the scheme that's unsafe to redirect to. class NET_EXPORT UnsafeRedirectProtocolHandler : public URLRequestJobFactory::ProtocolHandler { public: UnsafeRedirectProtocolHandler() = default; UnsafeRedirectProtocolHandler(const UnsafeRedirectProtocolHandler&) = delete; UnsafeRedirectProtocolHandler& operator=( const UnsafeRedirectProtocolHandler&) = delete; ~UnsafeRedirectProtocolHandler() override = default; // URLRequestJobFactory::ProtocolHandler implementation: std::unique_ptr CreateJob( URLRequest* request) const override { NOTREACHED(); return nullptr; } bool IsSafeRedirectTarget(const GURL& location) const override { return false; } }; // URLRequestTest interface: void SetUpContextBuilder(URLRequestContextBuilder& builder) override { // Add support for an unsafe scheme to the default URLRequestContext. builder.SetProtocolHandler( "unsafe", std::make_unique()); } // Requests |redirect_url|, which must return a HTTP 3xx redirect. // |request_method| is the method to use for the initial request. // |redirect_method| is the method that is expected to be used for the second // request, after redirection. // If |include_data| is true, data is uploaded with the request. The // response body is expected to match it exactly, if and only if // |request_method| == |redirect_method|. void HTTPRedirectMethodTest(const GURL& redirect_url, const std::string& request_method, const std::string& redirect_method, bool include_data) { static const char kData[] = "hello world"; TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest(default_context(), redirect_url, &d); req->set_method(request_method); if (include_data) { req->set_upload(CreateSimpleUploadData(kData)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kContentLength, base::NumberToString(std::size(kData) - 1)); headers.SetHeader(HttpRequestHeaders::kContentType, "text/plain"); req->SetExtraRequestHeaders(headers); } req->Start(); d.RunUntilComplete(); EXPECT_EQ(redirect_method, req->method()); EXPECT_EQ(OK, d.request_status()); if (include_data) { if (request_method == redirect_method) { EXPECT_TRUE(req->extra_request_headers().HasHeader( HttpRequestHeaders::kContentLength)); EXPECT_TRUE(req->extra_request_headers().HasHeader( HttpRequestHeaders::kContentType)); EXPECT_EQ(kData, d.data_received()); } else { EXPECT_FALSE(req->extra_request_headers().HasHeader( HttpRequestHeaders::kContentLength)); EXPECT_FALSE(req->extra_request_headers().HasHeader( HttpRequestHeaders::kContentType)); EXPECT_NE(kData, d.data_received()); } } if (HasFailure()) LOG(WARNING) << "Request method was: " << request_method; } // Requests |redirect_url|, which must return a HTTP 3xx redirect. It's also // used as the initial origin. // |request_method| is the method to use for the initial request. // |redirect_method| is the method that is expected to be used for the second // request, after redirection. // |expected_origin_value| is the expected value for the Origin header after // redirection. If empty, expects that there will be no Origin header. void HTTPRedirectOriginHeaderTest(const GURL& redirect_url, const std::string& request_method, const std::string& redirect_method, const std::string& expected_origin_value) { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest(default_context(), redirect_url, &d); req->set_method(request_method); req->SetExtraRequestHeaderByName( HttpRequestHeaders::kOrigin, redirect_url.DeprecatedGetOriginAsURL().spec(), false); req->Start(); d.RunUntilComplete(); EXPECT_EQ(redirect_method, req->method()); // Note that there is no check for request success here because, for // purposes of testing, the request very well may fail. For example, if the // test redirects to an HTTPS server from an HTTP origin, thus it is cross // origin, there is not an HTTPS server in this unit test framework, so the // request would fail. However, that's fine, as long as the request headers // are in order and pass the checks below. if (expected_origin_value.empty()) { EXPECT_FALSE( req->extra_request_headers().HasHeader(HttpRequestHeaders::kOrigin)); } else { std::string origin_header; EXPECT_TRUE(req->extra_request_headers().GetHeader( HttpRequestHeaders::kOrigin, &origin_header)); EXPECT_EQ(expected_origin_value, origin_header); } } void HTTPUploadDataOperationTest(const std::string& method) { const int kMsgSize = 20000; // multiple of 10 const int kIterations = 50; auto uploadBytes = std::make_unique(kMsgSize + 1); char* ptr = uploadBytes.get(); char marker = 'a'; for (int idx = 0; idx < kMsgSize / 10; idx++) { memcpy(ptr, "----------", 10); ptr += 10; if (idx % 100 == 0) { ptr--; *ptr++ = marker; if (++marker > 'z') marker = 'a'; } } uploadBytes[kMsgSize] = '\0'; for (int i = 0; i < kIterations; ++i) { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( test_server_.GetURL("/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_method(method.c_str()); r->set_upload(CreateSimpleUploadData(uploadBytes.get())); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); ASSERT_EQ(1, d.response_started_count()) << "request failed. Error: " << d.request_status(); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(base::StringPiece(uploadBytes.get(), kMsgSize), d.data_received()); } } HttpTestServer* http_test_server() { return &test_server_; } private: base::test::ScopedFeatureList feature_list_; HttpTestServer test_server_; }; namespace { std::unique_ptr HandleRedirectConnect( const test_server::HttpRequest& request) { if (request.headers.find("Host") == request.headers.end() || request.headers.at("Host") != "www.redirect.com" || request.method != test_server::METHOD_CONNECT) { return nullptr; } auto http_response = std::make_unique(); http_response->set_code(HTTP_FOUND); http_response->AddCustomHeader("Location", "http://www.destination.com/foo.js"); return std::move(http_response); } } // namespace // In this unit test, we're using the HTTPTestServer as a proxy server and // issuing a CONNECT request with the magic host name "www.redirect.com". // The EmbeddedTestServer will return a 302 response, which we should not // follow. TEST_F(URLRequestTestHTTP, ProxyTunnelRedirectTest) { http_test_server()->RegisterRequestHandler( base::BindRepeating(&HandleRedirectConnect)); ASSERT_TRUE(http_test_server()->Start()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_proxy_resolution_service( CreateFixedProxyResolutionService( http_test_server()->host_port_pair().ToString())); auto context = context_builder->Build(); TestDelegate d; { std::unique_ptr r(context->CreateRequest( GURL("https://www.redirect.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); // The proxy server should be set before failure. EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, http_test_server()->host_port_pair()), r->proxy_server()); EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, d.request_status()); EXPECT_EQ(1, d.response_started_count()); // We should not have followed the redirect. EXPECT_EQ(0, d.received_redirect_count()); } } // This is the same as the previous test, but checks that the network delegate // registers the error. TEST_F(URLRequestTestHTTP, NetworkDelegateTunnelConnectionFailed) { ASSERT_TRUE(http_test_server()->Start()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_proxy_resolution_service( CreateFixedProxyResolutionService( http_test_server()->host_port_pair().ToString())); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; { std::unique_ptr r(context->CreateRequest( GURL("https://www.redirect.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); // The proxy server should be set before failure. EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, http_test_server()->host_port_pair()), r->proxy_server()); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, d.request_status()); // We should not have followed the redirect. EXPECT_EQ(0, d.received_redirect_count()); EXPECT_EQ(1, network_delegate.error_count()); EXPECT_THAT(network_delegate.last_error(), IsError(ERR_TUNNEL_CONNECTION_FAILED)); } } // Tests that we can block and asynchronously return OK in various stages. TEST_F(URLRequestTestHTTP, NetworkDelegateBlockAsynchronously) { static const BlockingNetworkDelegate::Stage blocking_stages[] = { BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, BlockingNetworkDelegate::ON_HEADERS_RECEIVED}; ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::USER_CALLBACK)); network_delegate.set_block_on( BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST | BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS | BlockingNetworkDelegate::ON_HEADERS_RECEIVED); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); for (auto stage : blocking_stages) { network_delegate.RunUntilBlocked(); EXPECT_EQ(stage, network_delegate.stage_blocked_for_callback()); network_delegate.DoCallback(OK); } d.RunUntilComplete(); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Tests that the network delegate can block and cancel a request. TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_proxy_resolution_service( CreateFixedProxyResolutionService( http_test_server()->host_port_pair().ToString())); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::AUTO_CALLBACK)); auto context = context_builder->Build(); network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); network_delegate.set_retval(ERR_EMPTY_RESPONSE); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); // The proxy server is not set before cancellation. EXPECT_FALSE(r->proxy_server().is_valid()); EXPECT_EQ(ERR_EMPTY_RESPONSE, d.request_status()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Helper function for NetworkDelegateCancelRequestAsynchronously and // NetworkDelegateCancelRequestSynchronously. Sets up a blocking network // delegate operating in |block_mode| and a request for |url|. It blocks the // request in |stage| and cancels it with ERR_BLOCKED_BY_CLIENT. void NetworkDelegateCancelRequest(BlockingNetworkDelegate::BlockMode block_mode, BlockingNetworkDelegate::Stage stage, const GURL& url) { TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique(block_mode)); network_delegate.set_retval(ERR_BLOCKED_BY_CLIENT); network_delegate.set_block_on(stage); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); // The proxy server is not set before cancellation. if (stage == BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST || stage == BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS) { EXPECT_FALSE(r->proxy_server().is_valid()); } else if (stage == BlockingNetworkDelegate::ON_HEADERS_RECEIVED) { EXPECT_TRUE(r->proxy_server().is_direct()); } else { NOTREACHED(); } EXPECT_EQ(ERR_BLOCKED_BY_CLIENT, d.request_status()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // The following 3 tests check that the network delegate can cancel a request // synchronously in various stages of the request. TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously1) { ASSERT_TRUE(http_test_server()->Start()); NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, http_test_server()->GetURL("/")); } TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously2) { ASSERT_TRUE(http_test_server()->Start()); NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, http_test_server()->GetURL("/")); } TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestSynchronously3) { ASSERT_TRUE(http_test_server()->Start()); NetworkDelegateCancelRequest(BlockingNetworkDelegate::SYNCHRONOUS, BlockingNetworkDelegate::ON_HEADERS_RECEIVED, http_test_server()->GetURL("/")); } // The following 3 tests check that the network delegate can cancel a request // asynchronously in various stages of the request. TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously1) { ASSERT_TRUE(http_test_server()->Start()); NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, http_test_server()->GetURL("/")); } TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously2) { ASSERT_TRUE(http_test_server()->Start()); NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, http_test_server()->GetURL("/")); } TEST_F(URLRequestTestHTTP, NetworkDelegateCancelRequestAsynchronously3) { ASSERT_TRUE(http_test_server()->Start()); NetworkDelegateCancelRequest(BlockingNetworkDelegate::AUTO_CALLBACK, BlockingNetworkDelegate::ON_HEADERS_RECEIVED, http_test_server()->GetURL("/")); } // Tests that the network delegate can block and redirect a request to a new // URL. TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_proxy_resolution_service( CreateFixedProxyResolutionService( http_test_server()->host_port_pair().ToString())); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::AUTO_CALLBACK)); auto context = context_builder->Build(); GURL redirect_url("http://does.not.resolve.test/simple.html"); network_delegate.set_redirect_url(redirect_url); { GURL original_url("http://does.not.resolve.test/defaultresponse"); std::unique_ptr r(context->CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Quit after hitting the redirect, so can check the headers. r->Start(); d.RunUntilRedirect(); // Check headers from URLRequestJob. EXPECT_EQ(307, r->GetResponseCode()); EXPECT_EQ(307, r->response_headers()->response_code()); std::string location; ASSERT_TRUE( r->response_headers()->EnumerateHeader(nullptr, "Location", &location)); EXPECT_EQ(redirect_url, GURL(location)); // Let the request finish. r->FollowDeferredRedirect(absl::nullopt /* removed_headers */, absl::nullopt /* modified_headers */); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, http_test_server()->host_port_pair()), r->proxy_server()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(redirect_url, r->url()); EXPECT_EQ(original_url, r->original_url()); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Tests that the network delegate can block and redirect a request to a new // URL by setting a redirect_url and returning in OnBeforeURLRequest directly. TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestSynchronously) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_proxy_resolution_service( CreateFixedProxyResolutionService( http_test_server()->host_port_pair().ToString())); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS)); auto context = context_builder->Build(); GURL redirect_url("http://does.not.resolve.test/simple.html"); network_delegate.set_redirect_url(redirect_url); { GURL original_url("http://does.not.resolve.test/defaultresponse"); std::unique_ptr r(context->CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Quit after hitting the redirect, so can check the headers. r->Start(); d.RunUntilRedirect(); // Check headers from URLRequestJob. EXPECT_EQ(307, r->GetResponseCode()); EXPECT_EQ(307, r->response_headers()->response_code()); std::string location; ASSERT_TRUE( r->response_headers()->EnumerateHeader(nullptr, "Location", &location)); EXPECT_EQ(redirect_url, GURL(location)); // Let the request finish. r->FollowDeferredRedirect(absl::nullopt /* removed_headers */, absl::nullopt /* modified_headers */); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, http_test_server()->host_port_pair()), r->proxy_server()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(redirect_url, r->url()); EXPECT_EQ(original_url, r->original_url()); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Tests that redirects caused by the network delegate preserve POST data. TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestPost) { ASSERT_TRUE(http_test_server()->Start()); const char kData[] = "hello world"; TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::AUTO_CALLBACK)); network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); GURL redirect_url(http_test_server()->GetURL("/echo")); network_delegate.set_redirect_url(redirect_url); auto context = context_builder->Build(); { GURL original_url(http_test_server()->GetURL("/defaultresponse")); std::unique_ptr r(context->CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_method("POST"); r->set_upload(CreateSimpleUploadData(kData)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kContentLength, base::NumberToString(std::size(kData) - 1)); r->SetExtraRequestHeaders(headers); // Quit after hitting the redirect, so can check the headers. r->Start(); d.RunUntilRedirect(); // Check headers from URLRequestJob. EXPECT_EQ(307, r->GetResponseCode()); EXPECT_EQ(307, r->response_headers()->response_code()); std::string location; ASSERT_TRUE( r->response_headers()->EnumerateHeader(nullptr, "Location", &location)); EXPECT_EQ(redirect_url, GURL(location)); // Let the request finish. r->FollowDeferredRedirect(absl::nullopt /* removed_headers */, absl::nullopt /* modified_headers */); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(redirect_url, r->url()); EXPECT_EQ(original_url, r->original_url()); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); EXPECT_EQ("POST", r->method()); EXPECT_EQ(kData, d.data_received()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Tests that the network delegate can block and redirect a request to a new // URL during OnHeadersReceived. TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestOnHeadersReceived) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_proxy_resolution_service( CreateFixedProxyResolutionService( http_test_server()->host_port_pair().ToString())); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::AUTO_CALLBACK)); auto context = context_builder->Build(); network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED); GURL redirect_url("http://does.not.resolve.test/simple.html"); network_delegate.set_redirect_on_headers_received_url(redirect_url); { GURL original_url("http://does.not.resolve.test/defaultresponse"); std::unique_ptr r(context->CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, http_test_server()->host_port_pair()), r->proxy_server()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(redirect_url, r->url()); EXPECT_EQ(original_url, r->original_url()); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(2, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Tests that the network delegate can synchronously complete OnAuthRequired // by taking no action. This indicates that the NetworkDelegate does not want to // handle the challenge, and is passing the buck along to the // URLRequest::Delegate. TEST_F(URLRequestTestHTTP, NetworkDelegateOnAuthRequiredSyncNoAction) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS)); auto context = context_builder->Build(); d.set_credentials(AuthCredentials(kUser, kSecret)); { GURL url(http_test_server()->GetURL("/auth-basic")); std::unique_ptr r(context->CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_TRUE(d.auth_required_called()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Tests that NetworkDelegate header overrides from the 401 response do not // affect the 200 response. This is a regression test for // https://crbug.com/801237. TEST_F(URLRequestTestHTTP, NetworkDelegateOverrideHeadersWithAuth) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; d.set_credentials(AuthCredentials(kUser, kSecret)); default_network_delegate().set_add_header_to_first_response(true); { GURL url(http_test_server()->GetURL("/auth-basic")); std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_TRUE(d.auth_required_called()); EXPECT_FALSE(r->response_headers()->HasHeader("X-Network-Delegate")); } { GURL url(http_test_server()->GetURL("/defaultresponse")); std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); // Check that set_add_header_to_first_response normally adds a header. EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_TRUE(r->response_headers()->HasHeader("X-Network-Delegate")); } } // Tests that we can handle when a network request was canceled while we were // waiting for the network delegate. // Part 1: Request is cancelled while waiting for OnBeforeURLRequest callback. TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting1) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::USER_CALLBACK)); network_delegate.set_block_on(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); network_delegate.RunUntilBlocked(); EXPECT_EQ(BlockingNetworkDelegate::ON_BEFORE_URL_REQUEST, network_delegate.stage_blocked_for_callback()); EXPECT_EQ(0, network_delegate.completed_requests()); // Cancel before callback. r->Cancel(); // Ensure that network delegate is notified. EXPECT_EQ(1, network_delegate.completed_requests()); EXPECT_EQ(1, network_delegate.canceled_requests()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Tests that we can handle when a network request was canceled while we were // waiting for the network delegate. // Part 2: Request is cancelled while waiting for OnBeforeStartTransaction // callback. TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting2) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::USER_CALLBACK)); network_delegate.set_block_on( BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); network_delegate.RunUntilBlocked(); EXPECT_EQ(BlockingNetworkDelegate::ON_BEFORE_SEND_HEADERS, network_delegate.stage_blocked_for_callback()); EXPECT_EQ(0, network_delegate.completed_requests()); // Cancel before callback. r->Cancel(); // Ensure that network delegate is notified. EXPECT_EQ(1, network_delegate.completed_requests()); EXPECT_EQ(1, network_delegate.canceled_requests()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } // Tests that we can handle when a network request was canceled while we were // waiting for the network delegate. // Part 3: Request is cancelled while waiting for OnHeadersReceived callback. TEST_F(URLRequestTestHTTP, NetworkDelegateCancelWhileWaiting3) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::USER_CALLBACK)); network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); network_delegate.RunUntilBlocked(); EXPECT_EQ(BlockingNetworkDelegate::ON_HEADERS_RECEIVED, network_delegate.stage_blocked_for_callback()); EXPECT_EQ(0, network_delegate.completed_requests()); // Cancel before callback. r->Cancel(); // Ensure that network delegate is notified. EXPECT_EQ(1, network_delegate.completed_requests()); EXPECT_EQ(1, network_delegate.canceled_requests()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); } namespace { std::unique_ptr HandleServerAuthConnect( const test_server::HttpRequest& request) { if (request.headers.find("Host") == request.headers.end() || request.headers.at("Host") != "www.server-auth.com" || request.method != test_server::METHOD_CONNECT) { return nullptr; } auto http_response = std::make_unique(); http_response->set_code(HTTP_UNAUTHORIZED); http_response->AddCustomHeader("WWW-Authenticate", "Basic realm=\"WallyWorld\""); return http_response; } } // namespace // In this unit test, we're using the EmbeddedTestServer as a proxy server and // issuing a CONNECT request with the magic host name "www.server-auth.com". // The EmbeddedTestServer will return a 401 response, which we should balk at. TEST_F(URLRequestTestHTTP, UnexpectedServerAuthTest) { http_test_server()->RegisterRequestHandler( base::BindRepeating(&HandleServerAuthConnect)); ASSERT_TRUE(http_test_server()->Start()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_proxy_resolution_service( CreateFixedProxyResolutionService( http_test_server()->host_port_pair().ToString())); auto context = context_builder->Build(); TestDelegate d; { std::unique_ptr r(context->CreateRequest( GURL("https://www.server-auth.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); // The proxy server should be set before failure. EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP, http_test_server()->host_port_pair()), r->proxy_server()); EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, d.request_status()); } } TEST_F(URLRequestTestHTTP, GetTest_NoCache) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_NE(0, d.bytes_received()); EXPECT_EQ(http_test_server()->host_port_pair().host(), r->GetResponseRemoteEndpoint().ToStringWithoutPort()); EXPECT_EQ(http_test_server()->host_port_pair().port(), r->GetResponseRemoteEndpoint().port()); // TODO(eroman): Add back the NetLog tests... } } TEST_F(URLRequestTestHTTP, GetTest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_NE(0, d.bytes_received()); EXPECT_EQ(http_test_server()->host_port_pair().host(), r->GetResponseRemoteEndpoint().ToStringWithoutPort()); EXPECT_EQ(http_test_server()->host_port_pair().port(), r->GetResponseRemoteEndpoint().port()); } } TEST_F(URLRequestTestHTTP, GetTestLoadTiming) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); LoadTimingInfo load_timing_info; r->GetLoadTimingInfo(&load_timing_info); TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_NE(0, d.bytes_received()); EXPECT_EQ(http_test_server()->host_port_pair().host(), r->GetResponseRemoteEndpoint().ToStringWithoutPort()); EXPECT_EQ(http_test_server()->host_port_pair().port(), r->GetResponseRemoteEndpoint().port()); } } namespace { // Sends the correct Content-Length matching the compressed length. const char kZippedContentLengthCompressed[] = "C"; // Sends an incorrect Content-Length matching the uncompressed length. const char kZippedContentLengthUncompressed[] = "U"; // Sends an incorrect Content-Length shorter than the compressed length. const char kZippedContentLengthShort[] = "S"; // Sends an incorrect Content-Length between the compressed and uncompressed // lengths. const char kZippedContentLengthMedium[] = "M"; // Sends an incorrect Content-Length larger than both compressed and // uncompressed lengths. const char kZippedContentLengthLong[] = "L"; // Sends |compressed_content| which, when decoded with deflate, should have // length |uncompressed_length|. The Content-Length header will be sent based on // which of the constants above is sent in the query string. std::unique_ptr HandleZippedRequest( const std::string& compressed_content, size_t uncompressed_length, const test_server::HttpRequest& request) { GURL url = request.GetURL(); if (url.path_piece() != "/compressedfiles/BullRunSpeech.txt") return nullptr; size_t length; if (url.query_piece() == kZippedContentLengthCompressed) { length = compressed_content.size(); } else if (url.query_piece() == kZippedContentLengthUncompressed) { length = uncompressed_length; } else if (url.query_piece() == kZippedContentLengthShort) { length = compressed_content.size() / 2; } else if (url.query_piece() == kZippedContentLengthMedium) { length = (compressed_content.size() + uncompressed_length) / 2; } else if (url.query_piece() == kZippedContentLengthLong) { length = compressed_content.size() + uncompressed_length; } else { return nullptr; } std::string headers = "HTTP/1.1 200 OK\r\n"; headers += "Content-Encoding: deflate\r\n"; base::StringAppendF(&headers, "Content-Length: %zu\r\n", length); return std::make_unique(headers, compressed_content); } } // namespace TEST_F(URLRequestTestHTTP, GetZippedTest) { base::FilePath file_path; base::PathService::Get(base::DIR_SOURCE_ROOT, &file_path); file_path = file_path.Append(kTestFilePath); std::string expected_content, compressed_content; ASSERT_TRUE(base::ReadFileToString( file_path.Append(FILE_PATH_LITERAL("BullRunSpeech.txt")), &expected_content)); // This file is the output of the Python zlib.compress function on // |expected_content|. ASSERT_TRUE(base::ReadFileToString( file_path.Append(FILE_PATH_LITERAL("BullRunSpeech.txt.deflate")), &compressed_content)); http_test_server()->RegisterRequestHandler(base::BindRepeating( &HandleZippedRequest, compressed_content, expected_content.size())); ASSERT_TRUE(http_test_server()->Start()); static const struct { const char* parameter; bool expect_success; } kTests[] = { // Sending the compressed Content-Length is correct. {kZippedContentLengthCompressed, true}, // Sending the uncompressed Content-Length is incorrect, but we accept it // to workaround some broken servers. {kZippedContentLengthUncompressed, true}, // Sending too long of Content-Length is rejected. {kZippedContentLengthLong, false}, {kZippedContentLengthMedium, false}, // Sending too short of Content-Length successfully fetches a response // body, but it will be truncated. {kZippedContentLengthShort, true}, }; for (const auto& test : kTests) { SCOPED_TRACE(test.parameter); TestDelegate d; std::string test_file = base::StringPrintf( "/compressedfiles/BullRunSpeech.txt?%s", test.parameter); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL(test_file), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); VLOG(1) << " Received " << d.bytes_received() << " bytes" << " error = " << d.request_status(); if (test.expect_success) { EXPECT_EQ(OK, d.request_status()) << " Parameter = \"" << test_file << "\""; if (strcmp(test.parameter, kZippedContentLengthShort) == 0) { // When content length is smaller than both compressed length and // uncompressed length, HttpStreamParser might not read the full // response body. EXPECT_EQ(expected_content.substr(0, d.data_received().size()), d.data_received()); } else { EXPECT_EQ(expected_content, d.data_received()); } } else { EXPECT_EQ(ERR_CONTENT_LENGTH_MISMATCH, d.request_status()) << " Parameter = \"" << test_file << "\""; } } } TEST_F(URLRequestTestHTTP, RedirectLoadTiming) { ASSERT_TRUE(http_test_server()->Start()); GURL destination_url = http_test_server()->GetURL("/"); GURL original_url = http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(destination_url, req->url()); EXPECT_EQ(original_url, req->original_url()); ASSERT_EQ(2U, req->url_chain().size()); EXPECT_EQ(original_url, req->url_chain()[0]); EXPECT_EQ(destination_url, req->url_chain()[1]); LoadTimingInfo load_timing_info_before_redirect; EXPECT_TRUE(default_network_delegate().GetLoadTimingInfoBeforeRedirect( &load_timing_info_before_redirect)); TestLoadTimingNotReused(load_timing_info_before_redirect, CONNECT_TIMING_HAS_DNS_TIMES); LoadTimingInfo load_timing_info; req->GetLoadTimingInfo(&load_timing_info); TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); // Check that a new socket was used on redirect, since the server does not // supposed keep-alive sockets, and that the times before the redirect are // before the ones recorded for the second request. EXPECT_NE(load_timing_info_before_redirect.socket_log_id, load_timing_info.socket_log_id); EXPECT_LE(load_timing_info_before_redirect.receive_headers_end, load_timing_info.connect_timing.connect_start); } TEST_F(URLRequestTestHTTP, MultipleRedirectTest) { ASSERT_TRUE(http_test_server()->Start()); GURL destination_url = http_test_server()->GetURL("/"); GURL middle_redirect_url = http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); GURL original_url = http_test_server()->GetURL("/server-redirect?" + middle_redirect_url.spec()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(2, d.received_redirect_count()); EXPECT_EQ(destination_url, req->url()); EXPECT_EQ(original_url, req->original_url()); ASSERT_EQ(3U, req->url_chain().size()); EXPECT_EQ(original_url, req->url_chain()[0]); EXPECT_EQ(middle_redirect_url, req->url_chain()[1]); EXPECT_EQ(destination_url, req->url_chain()[2]); } // This is a regression test for https://crbug.com/942073. TEST_F(URLRequestTestHTTP, RedirectEscaping) { ASSERT_TRUE(http_test_server()->Start()); // Assemble the destination URL as a string so it is not escaped by GURL. GURL destination_base = http_test_server()->GetURL("/defaultresponse"); // Add a URL fragment of U+2603 unescaped, U+2603 escaped, and then a UTF-8 // encoding error. std::string destination_url = destination_base.spec() + "#\xE2\x98\x83_%E2%98%83_\xE0\xE0"; // Redirect resolution should percent-escape bytes and preserve the UTF-8 // error at the end. std::string destination_escaped = destination_base.spec() + "#%E2%98%83_%E2%98%83_%E0%E0"; GURL original_url = http_test_server()->GetURL( "/server-redirect?" + base::EscapeQueryParamValue(destination_url, false)); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(destination_escaped, req->url().spec()); EXPECT_EQ(original_url, req->original_url()); ASSERT_EQ(2U, req->url_chain().size()); EXPECT_EQ(original_url, req->url_chain()[0]); EXPECT_EQ(destination_escaped, req->url_chain()[1].spec()); } // First and second pieces of information logged by delegates to URLRequests. const char kFirstDelegateInfo[] = "Wonderful delegate"; const char16_t kFirstDelegateInfo16[] = u"Wonderful delegate"; const char kSecondDelegateInfo[] = "Exciting delegate"; const char16_t kSecondDelegateInfo16[] = u"Exciting delegate"; // Logs delegate information to a URLRequest. The first string is logged // synchronously on Start(), using DELEGATE_INFO_DEBUG_ONLY. The second is // logged asynchronously, using DELEGATE_INFO_DISPLAY_TO_USER. Then // another asynchronous call is used to clear the delegate information // before calling a callback. The object then deletes itself. class AsyncDelegateLogger : public base::RefCounted { public: using Callback = base::OnceCallback; AsyncDelegateLogger(const AsyncDelegateLogger&) = delete; AsyncDelegateLogger& operator=(const AsyncDelegateLogger&) = delete; // Each time delegate information is added to the URLRequest, the resulting // load state is checked. The expected load state after each request is // passed in as an argument. static void Run(URLRequest* url_request, LoadState expected_first_load_state, LoadState expected_second_load_state, LoadState expected_third_load_state, Callback callback) { // base::MakeRefCounted is unavailable here, since the // constructor of AsyncDelegateLogger is private. auto logger = base::WrapRefCounted(new AsyncDelegateLogger( url_request, expected_first_load_state, expected_second_load_state, expected_third_load_state, std::move(callback))); logger->Start(); } // Checks that the log entries, starting with log_position, contain the // DELEGATE_INFO NetLog events that an AsyncDelegateLogger should have // recorded. Returns the index of entry after the expected number of // events this logged, or entries.size() if there aren't enough entries. static size_t CheckDelegateInfo(const std::vector& entries, size_t log_position) { // There should be 4 DELEGATE_INFO events: Two begins and two ends. if (log_position + 3 >= entries.size()) { ADD_FAILURE() << "Not enough log entries"; return entries.size(); } std::string delegate_info; EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::BEGIN, entries[log_position].phase); EXPECT_EQ( kFirstDelegateInfo, GetStringValueFromParams(entries[log_position], "delegate_blocked_by")); ++log_position; EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); ++log_position; EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::BEGIN, entries[log_position].phase); EXPECT_EQ( kSecondDelegateInfo, GetStringValueFromParams(entries[log_position], "delegate_blocked_by")); ++log_position; EXPECT_EQ(NetLogEventType::DELEGATE_INFO, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); return log_position + 1; } private: friend class base::RefCounted; AsyncDelegateLogger(URLRequest* url_request, LoadState expected_first_load_state, LoadState expected_second_load_state, LoadState expected_third_load_state, Callback callback) : url_request_(url_request), expected_first_load_state_(expected_first_load_state), expected_second_load_state_(expected_second_load_state), expected_third_load_state_(expected_third_load_state), callback_(std::move(callback)) {} ~AsyncDelegateLogger() = default; void Start() { url_request_->LogBlockedBy(kFirstDelegateInfo); LoadStateWithParam load_state = url_request_->GetLoadState(); EXPECT_EQ(expected_first_load_state_, load_state.state); EXPECT_NE(kFirstDelegateInfo16, load_state.param); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&AsyncDelegateLogger::LogSecondDelegate, this)); } void LogSecondDelegate() { url_request_->LogAndReportBlockedBy(kSecondDelegateInfo); LoadStateWithParam load_state = url_request_->GetLoadState(); EXPECT_EQ(expected_second_load_state_, load_state.state); if (expected_second_load_state_ == LOAD_STATE_WAITING_FOR_DELEGATE) { EXPECT_EQ(kSecondDelegateInfo16, load_state.param); } else { EXPECT_NE(kSecondDelegateInfo16, load_state.param); } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&AsyncDelegateLogger::LogComplete, this)); } void LogComplete() { url_request_->LogUnblocked(); LoadStateWithParam load_state = url_request_->GetLoadState(); EXPECT_EQ(expected_third_load_state_, load_state.state); if (expected_second_load_state_ == LOAD_STATE_WAITING_FOR_DELEGATE) EXPECT_EQ(std::u16string(), load_state.param); std::move(callback_).Run(); } raw_ptr url_request_; const int expected_first_load_state_; const int expected_second_load_state_; const int expected_third_load_state_; Callback callback_; }; // NetworkDelegate that logs delegate information before a request is started, // before headers are sent, when headers are read, and when auth information // is requested. Uses AsyncDelegateLogger. class AsyncLoggingNetworkDelegate : public TestNetworkDelegate { public: AsyncLoggingNetworkDelegate() = default; AsyncLoggingNetworkDelegate(const AsyncLoggingNetworkDelegate&) = delete; AsyncLoggingNetworkDelegate& operator=(const AsyncLoggingNetworkDelegate&) = delete; ~AsyncLoggingNetworkDelegate() override = default; // NetworkDelegate implementation. int OnBeforeURLRequest(URLRequest* request, CompletionOnceCallback callback, GURL* new_url) override { // TestNetworkDelegate always completes synchronously. CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeURLRequest( request, base::NullCallback(), new_url)); return RunCallbackAsynchronously(request, std::move(callback)); } int OnBeforeStartTransaction( URLRequest* request, const HttpRequestHeaders& headers, OnBeforeStartTransactionCallback callback) override { // TestNetworkDelegate always completes synchronously. CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeStartTransaction( request, headers, base::NullCallback())); return RunCallbackAsynchronously( request, base::BindOnce( [](OnBeforeStartTransactionCallback callback, int result) { std::move(callback).Run(result, absl::nullopt); }, std::move(callback))); } int OnHeadersReceived( URLRequest* request, CompletionOnceCallback callback, const HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, const IPEndPoint& endpoint, absl::optional* preserve_fragment_on_redirect_url) override { // TestNetworkDelegate always completes synchronously. CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnHeadersReceived( request, base::NullCallback(), original_response_headers, override_response_headers, endpoint, preserve_fragment_on_redirect_url)); return RunCallbackAsynchronously(request, std::move(callback)); } private: static int RunCallbackAsynchronously(URLRequest* request, CompletionOnceCallback callback) { AsyncDelegateLogger::Run(request, LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, base::BindOnce(std::move(callback), OK)); return ERR_IO_PENDING; } }; // URLRequest::Delegate that logs delegate information when the headers // are received, when each read completes, and during redirects. Uses // AsyncDelegateLogger. Can optionally cancel a request in any phase. // // Inherits from TestDelegate to reuse the TestDelegate code to handle // advancing to the next step in most cases, as well as cancellation. class AsyncLoggingUrlRequestDelegate : public TestDelegate { public: enum CancelStage { NO_CANCEL = 0, CANCEL_ON_RECEIVED_REDIRECT, CANCEL_ON_RESPONSE_STARTED, CANCEL_ON_READ_COMPLETED }; explicit AsyncLoggingUrlRequestDelegate(CancelStage cancel_stage) : cancel_stage_(cancel_stage) { if (cancel_stage == CANCEL_ON_RECEIVED_REDIRECT) set_cancel_in_received_redirect(true); else if (cancel_stage == CANCEL_ON_RESPONSE_STARTED) set_cancel_in_response_started(true); else if (cancel_stage == CANCEL_ON_READ_COMPLETED) set_cancel_in_received_data(true); } AsyncLoggingUrlRequestDelegate(const AsyncLoggingUrlRequestDelegate&) = delete; AsyncLoggingUrlRequestDelegate& operator=( const AsyncLoggingUrlRequestDelegate&) = delete; ~AsyncLoggingUrlRequestDelegate() override = default; // URLRequest::Delegate implementation: void OnReceivedRedirect(URLRequest* request, const RedirectInfo& redirect_info, bool* defer_redirect) override { *defer_redirect = true; AsyncDelegateLogger::Run( request, LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, base::BindOnce( &AsyncLoggingUrlRequestDelegate::OnReceivedRedirectLoggingComplete, base::Unretained(this), request, redirect_info)); } void OnResponseStarted(URLRequest* request, int net_error) override { AsyncDelegateLogger::Run( request, LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, base::BindOnce( &AsyncLoggingUrlRequestDelegate::OnResponseStartedLoggingComplete, base::Unretained(this), request, net_error)); } void OnReadCompleted(URLRequest* request, int bytes_read) override { AsyncDelegateLogger::Run( request, LOAD_STATE_IDLE, LOAD_STATE_IDLE, LOAD_STATE_IDLE, base::BindOnce( &AsyncLoggingUrlRequestDelegate::AfterReadCompletedLoggingComplete, base::Unretained(this), request, bytes_read)); } private: void OnReceivedRedirectLoggingComplete(URLRequest* request, const RedirectInfo& redirect_info) { bool defer_redirect = false; TestDelegate::OnReceivedRedirect(request, redirect_info, &defer_redirect); // FollowDeferredRedirect should not be called after cancellation. if (cancel_stage_ == CANCEL_ON_RECEIVED_REDIRECT) return; if (!defer_redirect) { request->FollowDeferredRedirect(absl::nullopt /* removed_headers */, absl::nullopt /* modified_headers */); } } void OnResponseStartedLoggingComplete(URLRequest* request, int net_error) { // The parent class continues the request. TestDelegate::OnResponseStarted(request, net_error); } void AfterReadCompletedLoggingComplete(URLRequest* request, int bytes_read) { // The parent class continues the request. TestDelegate::OnReadCompleted(request, bytes_read); } const CancelStage cancel_stage_; }; // Tests handling of delegate info before a request starts. TEST_F(URLRequestTestHTTP, DelegateInfoBeforeStart) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate request_delegate; auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_net_log(NetLog::Get()); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); LoadStateWithParam load_state = r->GetLoadState(); EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); EXPECT_EQ(std::u16string(), load_state.param); AsyncDelegateLogger::Run( r.get(), LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_WAITING_FOR_DELEGATE, LOAD_STATE_IDLE, base::BindOnce(&URLRequest::Start, base::Unretained(r.get()))); request_delegate.RunUntilComplete(); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_EQ(OK, request_delegate.request_status()); } auto entries = net_log_observer_.GetEntries(); size_t log_position = ExpectLogContainsSomewhereAfter( entries, 0, NetLogEventType::DELEGATE_INFO, NetLogEventPhase::BEGIN); log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position); // Nothing else should add any delegate info to the request. EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, NetLogEventType::DELEGATE_INFO)); } // Tests handling of delegate info from a network delegate. TEST_F(URLRequestTestHTTP, NetworkDelegateInfo) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate request_delegate; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); context_builder->set_net_log(NetLog::Get()); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/simple.html"), DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); LoadStateWithParam load_state = r->GetLoadState(); EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); EXPECT_EQ(std::u16string(), load_state.param); r->Start(); request_delegate.RunUntilComplete(); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_EQ(OK, request_delegate.request_status()); EXPECT_EQ(1, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); size_t log_position = 0; auto entries = net_log_observer_.GetEntries(); static const NetLogEventType kExpectedEvents[] = { NetLogEventType::NETWORK_DELEGATE_BEFORE_URL_REQUEST, NetLogEventType::NETWORK_DELEGATE_BEFORE_START_TRANSACTION, NetLogEventType::NETWORK_DELEGATE_HEADERS_RECEIVED, }; for (NetLogEventType event : kExpectedEvents) { SCOPED_TRACE(NetLogEventTypeToString(event)); log_position = ExpectLogContainsSomewhereAfter( entries, log_position + 1, event, NetLogEventPhase::BEGIN); log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); ASSERT_LT(log_position, entries.size()); EXPECT_EQ(event, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); } EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, NetLogEventType::DELEGATE_INFO)); } // Tests handling of delegate info from a network delegate in the case of an // HTTP redirect. TEST_F(URLRequestTestHTTP, NetworkDelegateInfoRedirect) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate request_delegate; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/server-redirect?simple.html"), DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); LoadStateWithParam load_state = r->GetLoadState(); EXPECT_EQ(LOAD_STATE_IDLE, load_state.state); EXPECT_EQ(std::u16string(), load_state.param); r->Start(); request_delegate.RunUntilComplete(); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_EQ(OK, request_delegate.request_status()); EXPECT_EQ(2, network_delegate.created_requests()); EXPECT_EQ(0, network_delegate.destroyed_requests()); } EXPECT_EQ(1, network_delegate.destroyed_requests()); size_t log_position = 0; auto entries = net_log_observer_.GetEntries(); static const NetLogEventType kExpectedEvents[] = { NetLogEventType::NETWORK_DELEGATE_BEFORE_URL_REQUEST, NetLogEventType::NETWORK_DELEGATE_BEFORE_START_TRANSACTION, NetLogEventType::NETWORK_DELEGATE_HEADERS_RECEIVED, }; for (NetLogEventType event : kExpectedEvents) { SCOPED_TRACE(NetLogEventTypeToString(event)); log_position = ExpectLogContainsSomewhereAfter( entries, log_position + 1, event, NetLogEventPhase::BEGIN); log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); ASSERT_LT(log_position, entries.size()); EXPECT_EQ(event, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); } // The URLRequest::Delegate then gets informed about the redirect. log_position = ExpectLogContainsSomewhereAfter( entries, log_position + 1, NetLogEventType::URL_REQUEST_DELEGATE_RECEIVED_REDIRECT, NetLogEventPhase::BEGIN); // The NetworkDelegate logged information in the same three events as before. for (NetLogEventType event : kExpectedEvents) { SCOPED_TRACE(NetLogEventTypeToString(event)); log_position = ExpectLogContainsSomewhereAfter( entries, log_position + 1, event, NetLogEventPhase::BEGIN); log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); ASSERT_LT(log_position, entries.size()); EXPECT_EQ(event, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); } EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, NetLogEventType::DELEGATE_INFO)); } // Tests handling of delegate info from a URLRequest::Delegate. TEST_F(URLRequestTestHTTP, URLRequestDelegateInfo) { ASSERT_TRUE(http_test_server()->Start()); AsyncLoggingUrlRequestDelegate request_delegate( AsyncLoggingUrlRequestDelegate::NO_CANCEL); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_net_log(NetLog::Get()); auto context = context_builder->Build(); { // A chunked response with delays between chunks is used to make sure that // attempts by the URLRequest delegate to log information while reading the // body are ignored. Since they are ignored, this test is robust against // the possibility of multiple reads being combined in the unlikely event // that it occurs. std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/chunked?waitBetweenChunks=20"), DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); LoadStateWithParam load_state = r->GetLoadState(); r->Start(); request_delegate.RunUntilComplete(); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_EQ(OK, request_delegate.request_status()); } auto entries = net_log_observer_.GetEntries(); size_t log_position = 0; // The delegate info should only have been logged on header complete. Other // times it should silently be ignored. log_position = ExpectLogContainsSomewhereAfter( entries, log_position + 1, NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED, NetLogEventPhase::BEGIN); log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); ASSERT_LT(log_position, entries.size()); EXPECT_EQ(NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, NetLogEventType::DELEGATE_INFO)); EXPECT_FALSE(LogContainsEntryWithTypeAfter( entries, log_position + 1, NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED)); } // Tests handling of delegate info from a URLRequest::Delegate in the case of // an HTTP redirect. TEST_F(URLRequestTestHTTP, URLRequestDelegateInfoOnRedirect) { ASSERT_TRUE(http_test_server()->Start()); AsyncLoggingUrlRequestDelegate request_delegate( AsyncLoggingUrlRequestDelegate::NO_CANCEL); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_net_log(NetLog::Get()); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/server-redirect?simple.html"), DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); LoadStateWithParam load_state = r->GetLoadState(); r->Start(); request_delegate.RunUntilComplete(); EXPECT_EQ(200, r->GetResponseCode()); EXPECT_EQ(OK, request_delegate.request_status()); } auto entries = net_log_observer_.GetEntries(); // Delegate info should only have been logged in OnReceivedRedirect and // OnResponseStarted. size_t log_position = 0; static const NetLogEventType kExpectedEvents[] = { NetLogEventType::URL_REQUEST_DELEGATE_RECEIVED_REDIRECT, NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED, }; for (NetLogEventType event : kExpectedEvents) { SCOPED_TRACE(NetLogEventTypeToString(event)); log_position = ExpectLogContainsSomewhereAfter(entries, log_position, event, NetLogEventPhase::BEGIN); log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); ASSERT_LT(log_position, entries.size()); EXPECT_EQ(event, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); } EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, NetLogEventType::DELEGATE_INFO)); } // Tests handling of delegate info from a URLRequest::Delegate in the case of // an HTTP redirect, with cancellation at various points. TEST_F(URLRequestTestHTTP, URLRequestDelegateOnRedirectCancelled) { ASSERT_TRUE(http_test_server()->Start()); const AsyncLoggingUrlRequestDelegate::CancelStage kCancelStages[] = { AsyncLoggingUrlRequestDelegate::CANCEL_ON_RECEIVED_REDIRECT, AsyncLoggingUrlRequestDelegate::CANCEL_ON_RESPONSE_STARTED, AsyncLoggingUrlRequestDelegate::CANCEL_ON_READ_COMPLETED, }; for (auto cancel_stage : kCancelStages) { AsyncLoggingUrlRequestDelegate request_delegate(cancel_stage); RecordingNetLogObserver net_log_observer; auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_net_log(NetLog::Get()); auto context = context_builder->Build(); { std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/server-redirect?simple.html"), DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); LoadStateWithParam load_state = r->GetLoadState(); r->Start(); request_delegate.RunUntilComplete(); EXPECT_EQ(ERR_ABORTED, request_delegate.request_status()); // Spin the message loop to run AsyncDelegateLogger task(s) posted after // the |request_delegate| completion task. base::RunLoop().RunUntilIdle(); } auto entries = net_log_observer.GetEntries(); // Delegate info is always logged in both OnReceivedRedirect and // OnResponseStarted. In the CANCEL_ON_RECEIVED_REDIRECT, the // OnResponseStarted delegate call is after cancellation, but logging is // still currently supported in that call. size_t log_position = 0; static const NetLogEventType kExpectedEvents[] = { NetLogEventType::URL_REQUEST_DELEGATE_RECEIVED_REDIRECT, NetLogEventType::URL_REQUEST_DELEGATE_RESPONSE_STARTED, }; for (NetLogEventType event : kExpectedEvents) { SCOPED_TRACE(NetLogEventTypeToString(event)); log_position = ExpectLogContainsSomewhereAfter( entries, log_position, event, NetLogEventPhase::BEGIN); log_position = AsyncDelegateLogger::CheckDelegateInfo(entries, log_position + 1); ASSERT_LT(log_position, entries.size()); EXPECT_EQ(event, entries[log_position].type); EXPECT_EQ(NetLogEventPhase::END, entries[log_position].phase); } EXPECT_FALSE(LogContainsEntryWithTypeAfter(entries, log_position + 1, NetLogEventType::DELEGATE_INFO)); } } namespace { const char kExtraHeader[] = "Allow-Snafu"; const char kExtraValue[] = "fubar"; class RedirectWithAdditionalHeadersDelegate : public TestDelegate { void OnReceivedRedirect(URLRequest* request, const RedirectInfo& redirect_info, bool* defer_redirect) override { TestDelegate::OnReceivedRedirect(request, redirect_info, defer_redirect); request->SetExtraRequestHeaderByName(kExtraHeader, kExtraValue, false); } }; } // namespace TEST_F(URLRequestTestHTTP, RedirectWithAdditionalHeadersTest) { ASSERT_TRUE(http_test_server()->Start()); GURL destination_url = http_test_server()->GetURL("/echoheader?" + std::string(kExtraHeader)); GURL original_url = http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); RedirectWithAdditionalHeadersDelegate d; std::unique_ptr req(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); std::string value; const HttpRequestHeaders& headers = req->extra_request_headers(); EXPECT_TRUE(headers.GetHeader(kExtraHeader, &value)); EXPECT_EQ(kExtraValue, value); EXPECT_FALSE(req->is_pending()); EXPECT_FALSE(req->is_redirecting()); EXPECT_EQ(kExtraValue, d.data_received()); } namespace { const char kExtraHeaderToRemove[] = "To-Be-Removed"; class RedirectWithHeaderRemovalDelegate : public TestDelegate { void OnReceivedRedirect(URLRequest* request, const RedirectInfo& redirect_info, bool* defer_redirect) override { TestDelegate::OnReceivedRedirect(request, redirect_info, defer_redirect); request->RemoveRequestHeaderByName(kExtraHeaderToRemove); } }; } // namespace TEST_F(URLRequestTestHTTP, RedirectWithHeaderRemovalTest) { ASSERT_TRUE(http_test_server()->Start()); GURL destination_url = http_test_server()->GetURL( "/echoheader?" + std::string(kExtraHeaderToRemove)); GURL original_url = http_test_server()->GetURL("/server-redirect?" + destination_url.spec()); RedirectWithHeaderRemovalDelegate d; std::unique_ptr req(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetExtraRequestHeaderByName(kExtraHeaderToRemove, "dummy", false); req->Start(); d.RunUntilComplete(); std::string value; const HttpRequestHeaders& headers = req->extra_request_headers(); EXPECT_FALSE(headers.GetHeader(kExtraHeaderToRemove, &value)); EXPECT_FALSE(req->is_pending()); EXPECT_FALSE(req->is_redirecting()); EXPECT_EQ("None", d.data_received()); } TEST_F(URLRequestTestHTTP, CancelAfterStart) { TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( GURL("http://www.google.com/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); r->Cancel(); d.RunUntilComplete(); // We expect to receive OnResponseStarted even though the request has been // cancelled. EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(0, d.bytes_received()); EXPECT_FALSE(d.received_data_before_response()); } } TEST_F(URLRequestTestHTTP, CancelInResponseStarted) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); d.set_cancel_in_response_started(true); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(0, d.bytes_received()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(ERR_ABORTED, d.request_status()); } } TEST_F(URLRequestTestHTTP, CancelOnDataReceived) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); d.set_cancel_in_received_data(true); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_NE(0, d.received_bytes_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(ERR_ABORTED, d.request_status()); } } TEST_F(URLRequestTestHTTP, CancelDuringEofRead) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { // This returns an empty response (With headers). std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); d.set_cancel_in_received_data(true); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(0, d.received_bytes_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(ERR_ABORTED, d.request_status()); } } TEST_F(URLRequestTestHTTP, CancelByDestroyingAfterStart) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); // The request will be implicitly canceled when it is destroyed. The // test delegate must not post a quit message when this happens because // this test doesn't actually have a message loop. The quit message would // get put on this thread's message queue and the next test would exit // early, causing problems. d.set_on_complete(base::DoNothing()); } // expect things to just cleanup properly. // we won't actually get a received response here because we've never run the // message loop EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); } TEST_F(URLRequestTestHTTP, CancelWhileReadingFromCache) { ASSERT_TRUE(http_test_server()->Start()); // populate cache { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); } // cancel read from cache (see bug 990242) { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); r->Cancel(); d.RunUntilComplete(); EXPECT_EQ(ERR_ABORTED, d.request_status()); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(0, d.bytes_received()); EXPECT_FALSE(d.received_data_before_response()); } } TEST_F(URLRequestTestHTTP, PostTest) { ASSERT_TRUE(http_test_server()->Start()); HTTPUploadDataOperationTest("POST"); } TEST_F(URLRequestTestHTTP, PutTest) { ASSERT_TRUE(http_test_server()->Start()); HTTPUploadDataOperationTest("PUT"); } TEST_F(URLRequestTestHTTP, PostEmptyTest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_method("POST"); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); ASSERT_EQ(1, d.response_started_count()) << "request failed. Error: " << d.request_status(); EXPECT_FALSE(d.received_data_before_response()); EXPECT_TRUE(d.data_received().empty()); } } TEST_F(URLRequestTestHTTP, PostFileTest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_method("POST"); base::FilePath dir; base::PathService::Get(base::DIR_EXE, &dir); base::SetCurrentDirectory(dir); std::vector> element_readers; base::FilePath path; base::PathService::Get(base::DIR_SOURCE_ROOT, &path); path = path.Append(kTestFilePath); path = path.Append(FILE_PATH_LITERAL("with-headers.html")); element_readers.push_back(std::make_unique( base::ThreadTaskRunnerHandle::Get().get(), path, 0, std::numeric_limits::max(), base::Time())); r->set_upload(std::make_unique( std::move(element_readers), 0)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); int64_t size64 = 0; ASSERT_EQ(true, base::GetFileSize(path, &size64)); ASSERT_LE(size64, std::numeric_limits::max()); int size = static_cast(size64); auto buf = std::make_unique(size); ASSERT_EQ(size, base::ReadFile(path, buf.get(), size)); ASSERT_EQ(1, d.response_started_count()) << "request failed. Error: " << d.request_status(); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(size, d.bytes_received()); EXPECT_EQ(std::string(&buf[0], size), d.data_received()); } } TEST_F(URLRequestTestHTTP, PostUnreadableFileTest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_method("POST"); std::vector> element_readers; element_readers.push_back(std::make_unique( base::ThreadTaskRunnerHandle::Get().get(), base::FilePath(FILE_PATH_LITERAL( "c:\\path\\to\\non\\existant\\file.randomness.12345")), 0, std::numeric_limits::max(), base::Time())); r->set_upload(std::make_unique( std::move(element_readers), 0)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_TRUE(d.request_failed()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); EXPECT_EQ(ERR_FILE_NOT_FOUND, d.request_status()); } } namespace { // Adds a standard set of data to an upload for chunked upload integration // tests. void AddDataToUpload(ChunkedUploadDataStream::Writer* writer) { writer->AppendData("a", 1, false); writer->AppendData("bcd", 3, false); writer->AppendData("this is a longer chunk than before.", 35, false); writer->AppendData("\r\n\r\n", 4, false); writer->AppendData("0", 1, false); writer->AppendData("2323", 4, true); } // Checks that the upload data added in AddChunksToUpload() was echoed back from // the server. void VerifyReceivedDataMatchesChunks(URLRequest* r, TestDelegate* d) { // This should match the chunks sent by AddChunksToUpload(). const std::string expected_data = "abcdthis is a longer chunk than before.\r\n\r\n02323"; ASSERT_EQ(1, d->response_started_count()) << "request failed. Error: " << d->request_status(); EXPECT_FALSE(d->received_data_before_response()); EXPECT_EQ(expected_data.size(), static_cast(d->bytes_received())); EXPECT_EQ(expected_data, d->data_received()); } } // namespace TEST_F(URLRequestTestHTTP, TestPostChunkedDataBeforeStart) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); auto upload_data_stream = std::make_unique(0); std::unique_ptr writer = upload_data_stream->CreateWriter(); r->set_upload(std::move(upload_data_stream)); r->set_method("POST"); AddDataToUpload(writer.get()); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); VerifyReceivedDataMatchesChunks(r.get(), &d); } } TEST_F(URLRequestTestHTTP, TestPostChunkedDataJustAfterStart) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); auto upload_data_stream = std::make_unique(0); std::unique_ptr writer = upload_data_stream->CreateWriter(); r->set_upload(std::move(upload_data_stream)); r->set_method("POST"); r->Start(); EXPECT_TRUE(r->is_pending()); AddDataToUpload(writer.get()); d.RunUntilComplete(); VerifyReceivedDataMatchesChunks(r.get(), &d); } } TEST_F(URLRequestTestHTTP, TestPostChunkedDataAfterStart) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); auto upload_data_stream = std::make_unique(0); std::unique_ptr writer = upload_data_stream->CreateWriter(); r->set_upload(std::move(upload_data_stream)); r->set_method("POST"); r->Start(); EXPECT_TRUE(r->is_pending()); // Pump messages until we start sending headers.. base::RunLoop().RunUntilIdle(); // And now wait for completion. base::RunLoop run_loop; d.set_on_complete(run_loop.QuitClosure()); AddDataToUpload(writer.get()); run_loop.Run(); VerifyReceivedDataMatchesChunks(r.get(), &d); } } TEST_F(URLRequestTestHTTP, ResponseHeadersTest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/with-headers.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); const HttpResponseHeaders* headers = req->response_headers(); // Simple sanity check that response_info() accesses the same data. EXPECT_EQ(headers, req->response_info().headers.get()); std::string header; EXPECT_TRUE(headers->GetNormalizedHeader("cache-control", &header)); EXPECT_EQ("private", header); header.clear(); EXPECT_TRUE(headers->GetNormalizedHeader("content-type", &header)); EXPECT_EQ("text/html; charset=ISO-8859-1", header); // The response has two "X-Multiple-Entries" headers. // This verfies our output has them concatenated together. header.clear(); EXPECT_TRUE(headers->GetNormalizedHeader("x-multiple-entries", &header)); EXPECT_EQ("a, b", header); } // TODO(svaldez): iOS tests are flaky with EmbeddedTestServer and transport // security state. (see http://crbug.com/550977). #if !BUILDFLAG(IS_IOS) TEST_F(URLRequestTestHTTP, ProcessSTS) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.SetSSLConfig( net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); std::string test_server_hostname = https_test_server.GetURL("/").host(); TestDelegate d; std::unique_ptr request(default_context().CreateRequest( https_test_server.GetURL("/hsts-headers.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); TransportSecurityState* security_state = default_context().transport_security_state(); TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; EXPECT_TRUE( security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); EXPECT_FALSE( security_state->GetDynamicPKPState(test_server_hostname, &pkp_state)); EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS, sts_state.upgrade_mode); EXPECT_TRUE(sts_state.include_subdomains); EXPECT_FALSE(pkp_state.include_subdomains); #if BUILDFLAG(IS_ANDROID) // Android's CertVerifyProc does not (yet) handle pins. #else EXPECT_FALSE(pkp_state.HasPublicKeyPins()); #endif } TEST_F(URLRequestTestHTTP, STSNotProcessedOnIP) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); // Make sure this test fails if the test server is changed to not // listen on an IP by default. ASSERT_TRUE(https_test_server.GetURL("/").HostIsIPAddress()); std::string test_server_hostname = https_test_server.GetURL("/").host(); TestDelegate d; std::unique_ptr request(default_context().CreateRequest( https_test_server.GetURL("/hsts-headers.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); TransportSecurityState* security_state = default_context().transport_security_state(); TransportSecurityState::STSState sts_state; EXPECT_FALSE( security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); } namespace { const char kExpectCTStaticHostname[] = "expect-ct.preloaded.test"; const char kPKPReportUri[] = "http://report-uri.preloaded.test/pkp"; const char kPKPHost[] = "with-report-uri-pkp.preloaded.test"; } // namespace // Tests that reports get sent on PKP violations when a report-uri is set. TEST_F(URLRequestTestHTTP, ProcessPKPAndSendReport) { base::test::ScopedFeatureList scoped_feature_list_; scoped_feature_list_.InitAndEnableFeature( net::features::kStaticKeyPinningEnforcement); GURL report_uri(kPKPReportUri); EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.SetSSLConfig( net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); std::string test_server_hostname = kPKPHost; SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); // Set up a MockCertVerifier to trigger a violation of the previously // set pin. scoped_refptr cert = https_test_server.GetCertificate(); ASSERT_TRUE(cert); CertVerifyResult verify_result; verify_result.verified_cert = cert; verify_result.is_issued_by_known_root = true; HashValue hash3; ASSERT_TRUE( hash3.FromString("sha256/3333333333333333333333333333333333333333333=")); verify_result.public_key_hashes.push_back(hash3); auto cert_verifier = std::make_unique(); cert_verifier->AddResultForCert(cert.get(), verify_result, OK); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier(std::move(cert_verifier)); auto context = context_builder->Build(); MockCertificateReportSender mock_report_sender; context->transport_security_state()->EnableStaticPinsForTesting(); context->transport_security_state()->SetPinningListAlwaysTimelyForTesting( true); context->transport_security_state()->SetReportSender(&mock_report_sender); IsolationInfo isolation_info = IsolationInfo::CreateTransient(); // Now send a request to trigger the violation. TestDelegate d; std::unique_ptr violating_request(context->CreateRequest( https_test_server.GetURL(test_server_hostname, "/simple.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); violating_request->set_isolation_info(isolation_info); violating_request->Start(); d.RunUntilComplete(); // Check that a report was sent. EXPECT_EQ(report_uri, mock_report_sender.latest_report_uri()); ASSERT_FALSE(mock_report_sender.latest_report().empty()); EXPECT_EQ("application/json; charset=utf-8", mock_report_sender.latest_content_type()); std::unique_ptr value( base::JSONReader::ReadDeprecated(mock_report_sender.latest_report())); ASSERT_TRUE(value); base::Value::Dict* report_dict = value->GetIfDict(); ASSERT_TRUE(report_dict); std::string* report_hostname = report_dict->FindString("hostname"); ASSERT_TRUE(report_hostname); EXPECT_EQ(test_server_hostname, *report_hostname); EXPECT_EQ(isolation_info.network_isolation_key(), mock_report_sender.latest_network_isolation_key()); } // Tests that reports do not get sent on requests to static pkp hosts that // don't have pin violations. TEST_F(URLRequestTestHTTP, ProcessPKPWithNoViolation) { base::test::ScopedFeatureList scoped_feature_list_; scoped_feature_list_.InitAndEnableFeature( net::features::kStaticKeyPinningEnforcement); EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.SetSSLConfig( net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); std::string test_server_hostname = kPKPHost; SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); scoped_refptr cert = https_test_server.GetCertificate(); ASSERT_TRUE(cert); CertVerifyResult verify_result; verify_result.verified_cert = cert; verify_result.is_issued_by_known_root = true; HashValue hash; // The expected value of GoodPin1 used by |test_default::kHSTSSource|. ASSERT_TRUE( hash.FromString("sha256/Nn8jk5By4Vkq6BeOVZ7R7AC6XUUBZsWmUbJR1f1Y5FY=")); verify_result.public_key_hashes.push_back(hash); auto mock_cert_verifier = std::make_unique(); mock_cert_verifier->AddResultForCert(cert.get(), verify_result, OK); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier(std::move(mock_cert_verifier)); auto context = context_builder->Build(); MockCertificateReportSender mock_report_sender; context->transport_security_state()->EnableStaticPinsForTesting(); context->transport_security_state()->SetPinningListAlwaysTimelyForTesting( true); context->transport_security_state()->SetReportSender(&mock_report_sender); // Now send a request that does not trigger the violation. TestDelegate d; std::unique_ptr request(context->CreateRequest( https_test_server.GetURL(test_server_hostname, "/simple.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->set_isolation_info(IsolationInfo::CreateTransient()); request->Start(); d.RunUntilComplete(); // Check that the request succeeded, a report was not sent and the pkp was // not bypassed. EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(GURL(), mock_report_sender.latest_report_uri()); EXPECT_EQ(std::string(), mock_report_sender.latest_report()); EXPECT_EQ(NetworkIsolationKey(), mock_report_sender.latest_network_isolation_key()); TransportSecurityState::PKPState pkp_state; EXPECT_TRUE(context->transport_security_state()->GetStaticPKPState( test_server_hostname, &pkp_state)); EXPECT_TRUE(pkp_state.HasPublicKeyPins()); EXPECT_FALSE(request->ssl_info().pkp_bypassed); } TEST_F(URLRequestTestHTTP, PKPBypassRecorded) { base::test::ScopedFeatureList scoped_feature_list_; scoped_feature_list_.InitAndEnableFeature( net::features::kStaticKeyPinningEnforcement); EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.SetSSLConfig( net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); // Set up a MockCertVerifier to be a local root that violates the pin scoped_refptr cert = https_test_server.GetCertificate(); ASSERT_TRUE(cert); CertVerifyResult verify_result; verify_result.verified_cert = cert; verify_result.is_issued_by_known_root = false; HashValue hash; ASSERT_TRUE( hash.FromString("sha256/1111111111111111111111111111111111111111111=")); verify_result.public_key_hashes.push_back(hash); auto cert_verifier = std::make_unique(); cert_verifier->AddResultForCert(cert.get(), verify_result, OK); std::string test_server_hostname = kPKPHost; SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier(std::move(cert_verifier)); auto context = context_builder->Build(); MockCertificateReportSender mock_report_sender; context->transport_security_state()->EnableStaticPinsForTesting(); context->transport_security_state()->SetPinningListAlwaysTimelyForTesting( true); context->transport_security_state()->SetReportSender(&mock_report_sender); TestDelegate d; std::unique_ptr request(context->CreateRequest( https_test_server.GetURL(test_server_hostname, "/simple.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->set_isolation_info(IsolationInfo::CreateTransient()); request->Start(); d.RunUntilComplete(); // Check that the request succeeded, a report was not sent and the PKP was // bypassed. EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(GURL(), mock_report_sender.latest_report_uri()); EXPECT_EQ(std::string(), mock_report_sender.latest_report()); EXPECT_EQ(NetworkIsolationKey(), mock_report_sender.latest_network_isolation_key()); TransportSecurityState::PKPState pkp_state; EXPECT_TRUE(context->transport_security_state()->GetStaticPKPState( test_server_hostname, &pkp_state)); EXPECT_TRUE(pkp_state.HasPublicKeyPins()); EXPECT_TRUE(request->ssl_info().pkp_bypassed); } TEST_F(URLRequestTestHTTP, ProcessSTSOnce) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.SetSSLConfig( net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); std::string test_server_hostname = https_test_server.GetURL("/").host(); TestDelegate d; std::unique_ptr request(default_context().CreateRequest( https_test_server.GetURL("/hsts-multiple-headers.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); // We should have set parameters from the first header, not the second. TransportSecurityState* security_state = default_context().transport_security_state(); TransportSecurityState::STSState sts_state; EXPECT_TRUE( security_state->GetDynamicSTSState(test_server_hostname, &sts_state)); EXPECT_EQ(TransportSecurityState::STSState::MODE_FORCE_HTTPS, sts_state.upgrade_mode); EXPECT_FALSE(sts_state.include_subdomains); EXPECT_FALSE(sts_state.include_subdomains); } // An ExpectCTReporter that records the number of times OnExpectCTFailed() was // called. class MockExpectCTReporter : public TransportSecurityState::ExpectCTReporter { public: MockExpectCTReporter() = default; ~MockExpectCTReporter() override = default; void OnExpectCTFailed( const HostPortPair& host_port_pair, const GURL& report_uri, base::Time expiration, const X509Certificate* validated_certificate_chain, const X509Certificate* served_certificate_chain, const SignedCertificateTimestampAndStatusList& signed_certificate_timestamps, const NetworkIsolationKey& network_isolation_key) override { num_failures_++; } uint32_t num_failures() { return num_failures_; } private: uint32_t num_failures_ = 0; }; // A CTPolicyEnforcer that returns a default CTPolicyCompliance value // for every certificate. class MockCTPolicyEnforcer : public CTPolicyEnforcer { public: MockCTPolicyEnforcer() = default; ~MockCTPolicyEnforcer() override = default; ct::CTPolicyCompliance CheckCompliance( X509Certificate* cert, const ct::SCTList& verified_scts, const NetLogWithSource& net_log) override { return default_result_; } void set_default_result(ct::CTPolicyCompliance default_result) { default_result_ = default_result; } private: ct::CTPolicyCompliance default_result_ = ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS; }; // Tests that Expect CT headers for the preload list are processed correctly. TEST_F(URLRequestTestHTTP, PreloadExpectCTHeader) { SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.SetSSLConfig( net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); // Set up a MockCertVerifier to accept the certificate that the server sends. scoped_refptr cert = https_test_server.GetCertificate(); ASSERT_TRUE(cert); CertVerifyResult verify_result; verify_result.verified_cert = cert; verify_result.is_issued_by_known_root = true; auto cert_verifier = std::make_unique(); cert_verifier->AddResultForCert(cert.get(), verify_result, OK); // Set up a MockCTPolicyEnforcer to trigger an Expect CT violation. auto ct_policy_enforcer = std::make_unique(); ct_policy_enforcer->set_default_result( ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS); // Use a MockHostResolver, so that the request can be sent to a site on the // Expect CT preload list. auto host_resolver = std::make_unique(); host_resolver->rules()->AddRule(kExpectCTStaticHostname, https_test_server.GetIPLiteralString()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_host_resolver(std::move(host_resolver)); context_builder->SetCertVerifier(std::move(cert_verifier)); context_builder->set_ct_policy_enforcer(std::move(ct_policy_enforcer)); context_builder->set_hsts_policy_bypass_list({}); auto context = context_builder->Build(); MockExpectCTReporter expect_ct_reporter; context->transport_security_state()->enable_static_expect_ct_ = true; context->transport_security_state()->SetExpectCTReporter(&expect_ct_reporter); context->transport_security_state()->ct_log_list_last_update_time_ = base::Time::Now(); // Now send a request to trigger the violation. TestDelegate d; GURL url = https_test_server.GetURL("/expect-ct-header-preload.html"); GURL::Replacements replace_host; replace_host.SetHostStr(kExpectCTStaticHostname); url = url.ReplaceComponents(replace_host); std::unique_ptr violating_request(context->CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); violating_request->Start(); d.RunUntilComplete(); EXPECT_EQ(1u, expect_ct_reporter.num_failures()); } // Tests that Expect CT HTTP headers are processed correctly. TEST_F(URLRequestTestHTTP, ExpectCTHeader) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( TransportSecurityState::kDynamicExpectCTFeature); EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.SetSSLConfig( net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); // Set up a MockCertVerifier to accept the certificate that the server sends. scoped_refptr cert = https_test_server.GetCertificate(); ASSERT_TRUE(cert); CertVerifyResult verify_result; verify_result.verified_cert = cert; verify_result.is_issued_by_known_root = true; auto cert_verifier = std::make_unique(); cert_verifier->AddResultForCert(cert.get(), verify_result, OK); // Set up a MockCTPolicyEnforcer to simulate CT compliance. auto ct_policy_enforcer = std::make_unique(); ct_policy_enforcer->set_default_result( ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier(std::move(cert_verifier)); context_builder->set_ct_policy_enforcer(std::move(ct_policy_enforcer)); auto context = context_builder->Build(); MockExpectCTReporter expect_ct_reporter; context->transport_security_state()->SetExpectCTReporter(&expect_ct_reporter); // Now send a request to trigger the header processing. TestDelegate d; GURL url = https_test_server.GetURL("/expect-ct-header.html"); std::unique_ptr request(context->CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); TransportSecurityState::ExpectCTState state; ASSERT_TRUE(context->transport_security_state()->GetDynamicExpectCTState( url.host(), NetworkIsolationKey(), &state)); EXPECT_TRUE(state.enforce); EXPECT_EQ(GURL("https://example.test"), state.report_uri); } // Tests that if multiple Expect CT HTTP headers are sent, they are all // processed. TEST_F(URLRequestTestHTTP, MultipleExpectCTHeaders) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( TransportSecurityState::kDynamicExpectCTFeature); EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.SetSSLConfig( net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); // Set up a MockCertVerifier to accept the certificate that the server sends. scoped_refptr cert = https_test_server.GetCertificate(); ASSERT_TRUE(cert); CertVerifyResult verify_result; verify_result.verified_cert = cert; verify_result.is_issued_by_known_root = true; auto cert_verifier = std::make_unique(); cert_verifier->AddResultForCert(cert.get(), verify_result, OK); // Set up a MockCTPolicyEnforcer to simulate CT compliance. DoNothingCTVerifier ct_verifier; auto ct_policy_enforcer = std::make_unique(); ct_policy_enforcer->set_default_result( ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier(std::move(cert_verifier)); context_builder->set_ct_policy_enforcer(std::move(ct_policy_enforcer)); auto context = context_builder->Build(); MockExpectCTReporter expect_ct_reporter; context->transport_security_state()->SetExpectCTReporter(&expect_ct_reporter); // Now send a request to trigger the header processing. TestDelegate d; GURL url = https_test_server.GetURL("/expect-ct-header-multiple.html"); std::unique_ptr request(context->CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); TransportSecurityState::ExpectCTState state; ASSERT_TRUE(context->transport_security_state()->GetDynamicExpectCTState( url.host(), NetworkIsolationKey(), &state)); EXPECT_TRUE(state.enforce); EXPECT_EQ(GURL("https://example.test"), state.report_uri); } #endif // !BUILDFLAG(IS_IOS) #if BUILDFLAG(ENABLE_REPORTING) TEST_F(URLRequestTestHTTP, NetworkErrorLogging_DontReportIfNetworkNotAccessed) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&https_test_server); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/cachetime"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); // Populate the cache. TestDelegate d; std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->set_isolation_info(isolation_info1_); request->Start(); d.RunUntilComplete(); ASSERT_EQ(1u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error = nel_service.errors()[0]; EXPECT_EQ(request_url, error.uri); EXPECT_EQ(200, error.status_code); EXPECT_EQ(OK, error.type); request = context->CreateRequest(request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS); request->set_isolation_info(isolation_info1_); request->Start(); d.RunUntilComplete(); EXPECT_FALSE(request->response_info().network_accessed); EXPECT_TRUE(request->response_info().was_cached); // No additional NEL report was generated. EXPECT_EQ(1u, nel_service.errors().size()); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_BasicSuccess) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/simple.html"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); ASSERT_EQ(1u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error = nel_service.errors()[0]; EXPECT_EQ(request_url, error.uri); EXPECT_EQ(200, error.status_code); EXPECT_EQ(OK, error.type); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_BasicError) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&https_test_server); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/close-socket"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); ASSERT_EQ(1u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error = nel_service.errors()[0]; EXPECT_EQ(request_url, error.uri); EXPECT_EQ(0, error.status_code); EXPECT_EQ(ERR_EMPTY_RESPONSE, error.type); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_Redirect) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/redirect-test.html"); GURL redirect_url = https_test_server.GetURL("/with-headers.html"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); ASSERT_EQ(2u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error1 = nel_service.errors()[0]; EXPECT_EQ(request_url, error1.uri); EXPECT_EQ(302, error1.status_code); EXPECT_EQ(OK, error1.type); const TestNetworkErrorLoggingService::RequestDetails& error2 = nel_service.errors()[1]; EXPECT_EQ(redirect_url, error2.uri); EXPECT_EQ(200, error2.status_code); EXPECT_EQ(OK, error2.type); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_RedirectWithoutLocationHeader) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/308-without-location-header"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); ASSERT_EQ(1u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error = nel_service.errors()[0]; EXPECT_EQ(request_url, error.uri); EXPECT_EQ(308, error.status_code); // The body of the response was successfully read. EXPECT_EQ(OK, error.type); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_Auth) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&https_test_server); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/auth-basic"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; d.set_credentials(AuthCredentials(kUser, kSecret)); std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); ASSERT_EQ(2u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error1 = nel_service.errors()[0]; EXPECT_EQ(request_url, error1.uri); EXPECT_EQ(401, error1.status_code); EXPECT_EQ(OK, error1.type); const TestNetworkErrorLoggingService::RequestDetails& error2 = nel_service.errors()[1]; EXPECT_EQ(request_url, error2.uri); EXPECT_EQ(200, error2.status_code); EXPECT_EQ(OK, error2.type); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_304Response) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&https_test_server); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/auth-basic"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); // populate the cache { TestDelegate d; d.set_credentials(AuthCredentials(kUser, kSecret)); std::unique_ptr r(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_isolation_info(isolation_info1_); r->Start(); d.RunUntilComplete(); } ASSERT_EQ(2u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error1 = nel_service.errors()[0]; EXPECT_EQ(request_url, error1.uri); EXPECT_EQ(401, error1.status_code); EXPECT_EQ(OK, error1.type); const TestNetworkErrorLoggingService::RequestDetails& error2 = nel_service.errors()[1]; EXPECT_EQ(request_url, error2.uri); EXPECT_EQ(200, error2.status_code); EXPECT_EQ(OK, error2.type); // repeat request with end-to-end validation. since auth-basic results in a // cachable page, we expect this test to result in a 304. in which case, the // response should be fetched from the cache. { TestDelegate d; d.set_credentials(AuthCredentials(kUser, kSecret)); std::unique_ptr r(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetLoadFlags(LOAD_VALIDATE_CACHE); r->set_isolation_info(isolation_info1_); r->Start(); d.RunUntilComplete(); // Should be the same cached document. EXPECT_TRUE(r->was_cached()); } ASSERT_EQ(3u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error3 = nel_service.errors()[2]; EXPECT_EQ(request_url, error3.uri); EXPECT_EQ(304, error3.status_code); EXPECT_EQ(OK, error3.type); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_CancelInResponseStarted) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/simple.html"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; d.set_cancel_in_response_started(true); std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); ASSERT_EQ(1u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error = nel_service.errors()[0]; EXPECT_EQ(request_url, error.uri); EXPECT_EQ(200, error.status_code); // Headers were received and the body should have been read but was not. EXPECT_EQ(ERR_ABORTED, error.type); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_CancelOnDataReceived) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/simple.html"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; d.set_cancel_in_received_data(true); std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); ASSERT_EQ(1u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error = nel_service.errors()[0]; EXPECT_EQ(request_url, error.uri); EXPECT_EQ(200, error.status_code); // Data was received but the body was not completely read. EXPECT_EQ(ERR_ABORTED, error.type); } TEST_F(URLRequestTestHTTP, NetworkErrorLogging_CancelRedirect) { EmbeddedTestServer https_test_server(net::EmbeddedTestServer::TYPE_HTTPS); https_test_server.ServeFilesFromSourceDirectory( base::FilePath(kTestFilePath)); ASSERT_TRUE(https_test_server.Start()); GURL request_url = https_test_server.GetURL("/redirect-test.html"); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_network_error_logging_enabled(true); auto& nel_service = *context_builder->SetNetworkErrorLoggingServiceForTesting( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; d.set_cancel_in_received_redirect(true); std::unique_ptr request(context->CreateRequest( request_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); ASSERT_EQ(1u, nel_service.errors().size()); const TestNetworkErrorLoggingService::RequestDetails& error = nel_service.errors()[0]; EXPECT_EQ(request_url, error.uri); EXPECT_EQ(302, error.status_code); // A valid HTTP response was received, even though the request was cancelled. EXPECT_EQ(OK, error.type); } #endif // BUILDFLAG(ENABLE_REPORTING) TEST_F(URLRequestTestHTTP, ContentTypeNormalizationTest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/content-type-normalization.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); std::string mime_type; req->GetMimeType(&mime_type); EXPECT_EQ("text/html", mime_type); std::string charset; req->GetCharset(&charset); EXPECT_EQ("utf-8", charset); req->Cancel(); } TEST_F(URLRequestTestHTTP, FileRedirect) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/redirect-to-file.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, d.request_status()); EXPECT_EQ(1, d.received_redirect_count()); } TEST_F(URLRequestTestHTTP, DataRedirect) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/redirect-to-data.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, d.request_status()); EXPECT_EQ(1, d.received_redirect_count()); } TEST_F(URLRequestTestHTTP, RestrictUnsafeRedirect) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL( "/server-redirect?unsafe://here-there-be-dragons"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(ERR_UNSAFE_REDIRECT, d.request_status()); // The redirect should have been rejected before reporting it to the // caller. See https://crbug.com/723796 EXPECT_EQ(0, d.received_redirect_count()); } // Test that redirects to invalid URLs are rejected. See // https://crbug.com/462272. TEST_F(URLRequestTestHTTP, RedirectToInvalidURL) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/redirect-to-invalid-url.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(ERR_INVALID_REDIRECT, d.request_status()); // The redirect should have been rejected before reporting it to the caller. EXPECT_EQ(0, d.received_redirect_count()); } // Make sure redirects are cached, despite not reading their bodies. TEST_F(URLRequestTestHTTP, CacheRedirect) { ASSERT_TRUE(http_test_server()->Start()); GURL redirect_url = http_test_server()->GetURL("/redirect302-to-echo-cacheable"); { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(isolation_info1_); req->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(http_test_server()->GetURL("/echo"), req->url()); } { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(isolation_info1_); req->Start(); d.RunUntilRedirect(); EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(0, d.response_started_count()); EXPECT_TRUE(req->was_cached()); req->FollowDeferredRedirect(absl::nullopt /* removed_headers */, absl::nullopt /* modified_headers */); d.RunUntilComplete(); EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(http_test_server()->GetURL("/echo"), req->url()); } } // Make sure a request isn't cached when a NetworkDelegate forces a redirect // when the headers are read, since the body won't have been read. TEST_F(URLRequestTestHTTP, NoCacheOnNetworkDelegateRedirect) { ASSERT_TRUE(http_test_server()->Start()); // URL that is normally cached. GURL initial_url = http_test_server()->GetURL("/cachetime"); { // Set up the TestNetworkDelegate tp force a redirect. GURL redirect_to_url = http_test_server()->GetURL("/echo"); default_network_delegate().set_redirect_on_headers_received_url( redirect_to_url); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( initial_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(redirect_to_url, req->url()); } { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( initial_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_FALSE(req->was_cached()); EXPECT_EQ(0, d.received_redirect_count()); EXPECT_EQ(initial_url, req->url()); } } // Check that |preserve_fragment_on_redirect_url| is respected. TEST_F(URLRequestTestHTTP, PreserveFragmentOnRedirectUrl) { ASSERT_TRUE(http_test_server()->Start()); GURL original_url(http_test_server()->GetURL("/original#fragment1")); GURL preserve_fragement_url(http_test_server()->GetURL("/echo")); default_network_delegate().set_redirect_on_headers_received_url( preserve_fragement_url); default_network_delegate().set_preserve_fragment_on_redirect_url( preserve_fragement_url); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(original_url, r->original_url()); EXPECT_EQ(preserve_fragement_url, r->url()); } } // Check that |preserve_fragment_on_redirect_url| has no effect when it doesn't // match the URL being redirected to. TEST_F(URLRequestTestHTTP, PreserveFragmentOnRedirectUrlMismatch) { ASSERT_TRUE(http_test_server()->Start()); GURL original_url(http_test_server()->GetURL("/original#fragment1")); GURL preserve_fragement_url(http_test_server()->GetURL("/echo#fragment2")); GURL redirect_url(http_test_server()->GetURL("/echo")); GURL expected_url(http_test_server()->GetURL("/echo#fragment1")); default_network_delegate().set_redirect_on_headers_received_url(redirect_url); default_network_delegate().set_preserve_fragment_on_redirect_url( preserve_fragement_url); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(original_url, r->original_url()); EXPECT_EQ(expected_url, r->url()); } } // When a URLRequestRedirectJob is created, the redirection must be followed and // the reference fragment of the target URL must not be modified. TEST_F(URLRequestTestHTTP, RedirectJobWithReferenceFragment) { ASSERT_TRUE(http_test_server()->Start()); GURL original_url( http_test_server()->GetURL("/original#should-not-be-appended")); GURL redirect_url(http_test_server()->GetURL("/echo")); TestDelegate d; std::unique_ptr r(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); std::unique_ptr job = std::make_unique( r.get(), redirect_url, RedirectUtil::ResponseCode::REDIRECT_302_FOUND, "Very Good Reason"); TestScopedURLInterceptor interceptor(r->url(), std::move(job)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(original_url, r->original_url()); EXPECT_EQ(redirect_url, r->url()); } TEST_F(URLRequestTestHTTP, UnsupportedReferrerScheme) { ASSERT_TRUE(http_test_server()->Start()); const std::string referrer("foobar://totally.legit.referrer"); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer(referrer); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string("None"), d.data_received()); } TEST_F(URLRequestTestHTTP, NoUserPassInReferrer) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer("http://user:pass@foo.com/"); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string("http://foo.com/"), d.data_received()); } TEST_F(URLRequestTestHTTP, NoFragmentInReferrer) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer("http://foo.com/test#fragment"); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string("http://foo.com/test"), d.data_received()); } TEST_F(URLRequestTestHTTP, EmptyReferrerAfterValidReferrer) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer("http://foo.com/test#fragment"); req->SetReferrer(""); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string("None"), d.data_received()); } TEST_F(URLRequestTestHTTP, CapRefererHeaderLength) { ASSERT_TRUE(http_test_server()->Start()); // Verify that referrers over 4k are stripped to an origin, and referrers at // or under 4k are unmodified. { std::string original_header = "http://example.com/"; original_header.resize(4097, 'a'); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer(original_header); req->Start(); d.RunUntilComplete(); // The request's referrer will be stripped since (1) there will be a // mismatch between the request's referrer and the output of // URLRequestJob::ComputeReferrerForPolicy and (2) the delegate, when // offered the opportunity to cancel the request for this reason, will // decline. EXPECT_EQ("None", d.data_received()); } { std::string original_header = "http://example.com/"; original_header.resize(4096, 'a'); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer(original_header); req->Start(); d.RunUntilComplete(); EXPECT_EQ(original_header, d.data_received()); } { std::string original_header = "http://example.com/"; original_header.resize(4095, 'a'); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetReferrer(original_header); req->Start(); d.RunUntilComplete(); EXPECT_EQ(original_header, d.data_received()); } } TEST_F(URLRequestTestHTTP, CancelRedirect) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { d.set_cancel_in_received_redirect(true); std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/redirect-test.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(0, d.bytes_received()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(ERR_ABORTED, d.request_status()); } } TEST_F(URLRequestTestHTTP, DeferredRedirect) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { GURL test_url(http_test_server()->GetURL("/redirect-test.html")); std::unique_ptr req(default_context().CreateRequest( test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilRedirect(); EXPECT_EQ(1, d.received_redirect_count()); req->FollowDeferredRedirect(absl::nullopt /* removed_headers */, absl::nullopt /* modified_headers */); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(OK, d.request_status()); base::FilePath path; base::PathService::Get(base::DIR_SOURCE_ROOT, &path); path = path.Append(kTestFilePath); path = path.Append(FILE_PATH_LITERAL("with-headers.html")); std::string contents; EXPECT_TRUE(base::ReadFileToString(path, &contents)); EXPECT_EQ(contents, d.data_received()); } } TEST_F(URLRequestTestHTTP, DeferredRedirect_ModifiedHeaders) { test_server::HttpRequest http_request; int num_observed_requests = 0; http_test_server()->RegisterRequestMonitor( base::BindLambdaForTesting([&](const test_server::HttpRequest& request) { http_request = request; ++num_observed_requests; })); ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { GURL test_url(http_test_server()->GetURL("/redirect-test.html")); std::unique_ptr req(default_context().CreateRequest( test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Set initial headers for the request. req->SetExtraRequestHeaderByName("Header1", "Value1", true /* overwrite */); req->SetExtraRequestHeaderByName("Header2", "Value2", true /* overwrite */); req->Start(); d.RunUntilRedirect(); // Initial request should only have initial headers. EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(1, num_observed_requests); EXPECT_EQ("Value1", http_request.headers["Header1"]); EXPECT_EQ("Value2", http_request.headers["Header2"]); EXPECT_EQ(0u, http_request.headers.count("Header3")); // Overwrite Header2 and add Header3. net::HttpRequestHeaders modified_headers; modified_headers.SetHeader("Header2", ""); modified_headers.SetHeader("Header3", "Value3"); req->FollowDeferredRedirect(absl::nullopt /* removed_headers */, modified_headers); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(OK, d.request_status()); // Redirected request should also have modified headers. EXPECT_EQ(2, num_observed_requests); EXPECT_EQ("Value1", http_request.headers["Header1"]); EXPECT_EQ(1u, http_request.headers.count("Header2")); EXPECT_EQ("", http_request.headers["Header2"]); EXPECT_EQ("Value3", http_request.headers["Header3"]); } } TEST_F(URLRequestTestHTTP, DeferredRedirect_RemovedHeaders) { test_server::HttpRequest http_request; int num_observed_requests = 0; http_test_server()->RegisterRequestMonitor( base::BindLambdaForTesting([&](const test_server::HttpRequest& request) { http_request = request; ++num_observed_requests; })); ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { GURL test_url(http_test_server()->GetURL("/redirect-test.html")); std::unique_ptr req(default_context().CreateRequest( test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Set initial headers for the request. req->SetExtraRequestHeaderByName("Header1", "Value1", true /* overwrite */); req->SetExtraRequestHeaderByName("Header2", "Value2", true /* overwrite */); req->Start(); d.RunUntilRedirect(); // Initial request should have initial headers. EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(1, num_observed_requests); EXPECT_EQ("Value1", http_request.headers["Header1"]); EXPECT_EQ("Value2", http_request.headers["Header2"]); // Keep Header1 and remove Header2. std::vector removed_headers({"Header2"}); req->FollowDeferredRedirect(removed_headers, absl::nullopt /* modified_headers */); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(OK, d.request_status()); // Redirected request should have modified headers. EXPECT_EQ(2, num_observed_requests); EXPECT_EQ("Value1", http_request.headers["Header1"]); EXPECT_EQ(0u, http_request.headers.count("Header2")); } } TEST_F(URLRequestTestHTTP, CancelDeferredRedirect) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; { std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/redirect-test.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilRedirect(); EXPECT_EQ(1, d.received_redirect_count()); req->Cancel(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(0, d.bytes_received()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(ERR_ABORTED, d.request_status()); } } TEST_F(URLRequestTestHTTP, VaryHeader) { ASSERT_TRUE(http_test_server()->Start()); // Populate the cache. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; headers.SetHeader("foo", "1"); req->SetExtraRequestHeaders(headers); req->set_isolation_info(isolation_info1_); req->Start(); d.RunUntilComplete(); LoadTimingInfo load_timing_info; req->GetLoadTimingInfo(&load_timing_info); TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); } // Expect a cache hit. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; headers.SetHeader("foo", "1"); req->SetExtraRequestHeaders(headers); req->set_isolation_info(isolation_info1_); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(req->was_cached()); LoadTimingInfo load_timing_info; req->GetLoadTimingInfo(&load_timing_info); TestLoadTimingCacheHitNoNetwork(load_timing_info); } // Expect a cache miss. { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheadercache?foo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; headers.SetHeader("foo", "2"); req->SetExtraRequestHeaders(headers); req->set_isolation_info(isolation_info1_); req->Start(); d.RunUntilComplete(); EXPECT_FALSE(req->was_cached()); LoadTimingInfo load_timing_info; req->GetLoadTimingInfo(&load_timing_info); TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); } } TEST_F(URLRequestTestHTTP, BasicAuth) { ASSERT_TRUE(http_test_server()->Start()); // populate the cache { TestDelegate d; d.set_credentials(AuthCredentials(kUser, kSecret)); std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_isolation_info(isolation_info1_); r->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); } // repeat request with end-to-end validation. since auth-basic results in a // cachable page, we expect this test to result in a 304. in which case, the // response should be fetched from the cache. { TestDelegate d; d.set_credentials(AuthCredentials(kUser, kSecret)); std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetLoadFlags(LOAD_VALIDATE_CACHE); r->set_isolation_info(isolation_info1_); r->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); // Should be the same cached document. EXPECT_TRUE(r->was_cached()); } } // Check that Set-Cookie headers in 401 responses are respected. // http://crbug.com/6450 TEST_F(URLRequestTestHTTP, BasicAuthWithCookies) { ASSERT_TRUE(http_test_server()->Start()); GURL url_requiring_auth = http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); // Request a page that will give a 401 containing a Set-Cookie header. // Verify that when the transaction is restarted, it includes the new cookie. TestDelegate d; d.set_credentials(AuthCredentials(kUser, kSecret)); std::unique_ptr r = CreateFirstPartyRequest(default_context(), url_requiring_auth, &d); r->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); // Make sure we sent the cookie in the restarted transaction. EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true") != std::string::npos); } // Same test as above, except this time the restart is initiated earlier // (without user intervention since identity is embedded in the URL). TEST_F(URLRequestTestHTTP, BasicAuthWithCredentialsWithCookies) { ASSERT_TRUE(http_test_server()->Start()); GURL url_requiring_auth = http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); GURL::Replacements replacements; replacements.SetUsernameStr("user2"); replacements.SetPasswordStr("secret"); GURL url_with_identity = url_requiring_auth.ReplaceComponents(replacements); TestDelegate d; std::unique_ptr r = CreateFirstPartyRequest(default_context(), url_with_identity, &d); r->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("user2/secret") != std::string::npos); // Make sure we sent the cookie in the restarted transaction. EXPECT_TRUE(d.data_received().find("Cookie: got_challenged=true") != std::string::npos); } TEST_F(URLRequestTestHTTP, BasicAuthWithCookiesCancelAuth) { ASSERT_TRUE(http_test_server()->Start()); GURL url_requiring_auth = http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); // Request a page that will give a 401 containing a Set-Cookie header. // Verify that cookies are set before credentials are provided, and then // cancelling auth does not result in setting the cookies again. TestDelegate d; EXPECT_TRUE(GetAllCookies(&default_context()).empty()); std::unique_ptr r = CreateFirstPartyRequest(default_context(), url_requiring_auth, &d); r->Start(); d.RunUntilAuthRequired(); // Cookie should have been set. EXPECT_EQ(1, default_network_delegate().set_cookie_count()); CookieList cookies = GetAllCookies(&default_context()); ASSERT_EQ(1u, cookies.size()); EXPECT_EQ("got_challenged", cookies[0].Name()); EXPECT_EQ("true", cookies[0].Value()); // Delete cookie. default_context().cookie_store()->DeleteAllAsync( CookieStore::DeleteCallback()); // Cancel auth and continue the request. r->CancelAuth(); d.RunUntilComplete(); ASSERT_TRUE(r->response_headers()); EXPECT_EQ(401, r->response_headers()->response_code()); // Cookie should not have been set again. EXPECT_TRUE(GetAllCookies(&default_context()).empty()); EXPECT_EQ(1, default_network_delegate().set_cookie_count()); } // Tests the IsolationInfo is updated approiately on redirect. TEST_F(URLRequestTestHTTP, IsolationInfoUpdatedOnRedirect) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( net::features::kSplitCacheByNetworkIsolationKey); ASSERT_TRUE(http_test_server()->Start()); GURL redirect_url = http_test_server()->GetURL("redirected.test", "/cachetime"); GURL original_url = http_test_server()->GetURL( "original.test", "/server-redirect?" + redirect_url.spec()); url::Origin original_origin = url::Origin::Create(original_url); url::Origin redirect_origin = url::Origin::Create(redirect_url); // Since transient IsolationInfos use opaque origins, need to create a single // consistent transient origin one for be used as the original and updated // info in the same test case. IsolationInfo transient_isolation_info = IsolationInfo::CreateTransient(); const struct { IsolationInfo info_before_redirect; IsolationInfo expected_info_after_redirect; } kTestCases[] = { {IsolationInfo(), IsolationInfo()}, {IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, original_origin, original_origin, SiteForCookies()), IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, redirect_origin, redirect_origin, SiteForCookies::FromOrigin(redirect_origin))}, {IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, original_origin, original_origin, SiteForCookies::FromOrigin(original_origin)), IsolationInfo::Create(IsolationInfo::RequestType::kSubFrame, original_origin, redirect_origin, SiteForCookies::FromOrigin(original_origin))}, {IsolationInfo::Create(IsolationInfo::RequestType::kOther, original_origin, original_origin, SiteForCookies()), IsolationInfo::Create(IsolationInfo::RequestType::kOther, original_origin, original_origin, SiteForCookies())}, {transient_isolation_info, transient_isolation_info}, }; for (const auto& test_case : kTestCases) { // Populate the cache, using the expected final IsolationInfo. { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_isolation_info(test_case.expected_info_after_redirect); r->Start(); d.RunUntilComplete(); EXPECT_THAT(d.request_status(), IsOk()); } // Send a request using the initial IsolationInfo that should be redirected // to the cached url, and should use the cached entry if the NIK was // updated, except in the case the IsolationInfo's NIK was empty. { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_isolation_info(test_case.info_before_redirect); r->Start(); d.RunUntilComplete(); EXPECT_THAT(d.request_status(), IsOk()); EXPECT_EQ(redirect_url, r->url()); EXPECT_EQ(!test_case.expected_info_after_redirect.network_isolation_key() .IsTransient(), r->was_cached()); EXPECT_EQ(test_case.expected_info_after_redirect.request_type(), r->isolation_info().request_type()); EXPECT_EQ(test_case.expected_info_after_redirect.top_frame_origin(), r->isolation_info().top_frame_origin()); EXPECT_EQ(test_case.expected_info_after_redirect.frame_origin(), r->isolation_info().frame_origin()); EXPECT_EQ(test_case.expected_info_after_redirect.network_isolation_key(), r->isolation_info().network_isolation_key()); EXPECT_TRUE(test_case.expected_info_after_redirect.site_for_cookies() .IsEquivalent(r->isolation_info().site_for_cookies())); } } } // Tests that |key_auth_cache_by_network_isolation_key| is respected. TEST_F(URLRequestTestHTTP, AuthWithNetworkIsolationKey) { ASSERT_TRUE(http_test_server()->Start()); for (bool key_auth_cache_by_network_isolation_key : {false, true}) { auto context_builder = CreateTestURLRequestContextBuilder(); HttpNetworkSessionParams network_session_params; network_session_params .key_auth_cache_server_entries_by_network_isolation_key = key_auth_cache_by_network_isolation_key; context_builder->set_http_network_session_params(network_session_params); auto context = context_builder->Build(); // Populate the auth cache using one NetworkIsolationKey. { TestDelegate d; GURL url(base::StringPrintf( "http://%s:%s@%s/auth-basic", base::UTF16ToASCII(kUser).c_str(), base::UTF16ToASCII(kSecret).c_str(), http_test_server()->host_port_pair().ToString().c_str())); std::unique_ptr r(context->CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetLoadFlags(LOAD_BYPASS_CACHE); r->set_isolation_info(isolation_info1_); r->Start(); d.RunUntilComplete(); EXPECT_THAT(d.request_status(), IsOk()); ASSERT_TRUE(r->response_headers()); EXPECT_EQ(200, r->response_headers()->response_code()); EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); } // Make a request with another NetworkIsolationKey. This may or may not use // the cached auth credentials, depending on whether or not the // HttpAuthCache is configured to respect the NetworkIsolationKey. { TestDelegate d; std::unique_ptr r(context->CreateRequest( http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetLoadFlags(LOAD_BYPASS_CACHE); r->set_isolation_info(isolation_info2_); r->Start(); d.RunUntilComplete(); EXPECT_THAT(d.request_status(), IsOk()); ASSERT_TRUE(r->response_headers()); if (key_auth_cache_by_network_isolation_key) { EXPECT_EQ(401, r->response_headers()->response_code()); } else { EXPECT_EQ(200, r->response_headers()->response_code()); } EXPECT_EQ(!key_auth_cache_by_network_isolation_key, d.data_received().find("user/secret") != std::string::npos); } } } TEST_F(URLRequestTest, ReportCookieActivity) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); RecordingNetLogObserver net_log_observer; auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); network_delegate.SetCookieFilter("not_stored_cookie"); network_delegate.set_block_annotate_cookies(); context_builder->set_net_log(net::NetLog::Get()); auto context = context_builder->Build(); // Make sure cookies blocked from being stored are caught, and those that are // accepted are reported as well. GURL set_cookie_test_url = test_server.GetURL( "/set-cookie?not_stored_cookie=true&" "stored_cookie=tasty" "&path_cookie=narrow;path=/set-cookie"); { TestDelegate d; std::unique_ptr req = CreateFirstPartyRequest(*context, set_cookie_test_url, &d); req->Start(); d.RunUntilComplete(); ASSERT_EQ(3u, req->maybe_stored_cookies().size()); EXPECT_EQ("not_stored_cookie", req->maybe_stored_cookies()[0].cookie->Name()); EXPECT_TRUE(req->maybe_stored_cookies()[0] .access_result.status.HasExactlyExclusionReasonsForTesting( {CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); EXPECT_EQ("stored_cookie", req->maybe_stored_cookies()[1].cookie->Name()); EXPECT_TRUE( req->maybe_stored_cookies()[1].access_result.status.IsInclude()); EXPECT_EQ("stored_cookie", req->maybe_stored_cookies()[1].cookie->Name()); EXPECT_TRUE( req->maybe_stored_cookies()[2].access_result.status.IsInclude()); EXPECT_EQ("path_cookie", req->maybe_stored_cookies()[2].cookie->Name()); auto entries = net_log_observer.GetEntriesWithType( NetLogEventType::COOKIE_INCLUSION_STATUS); EXPECT_EQ(3u, entries.size()); EXPECT_EQ("{\"domain\":\"" + set_cookie_test_url.host() + "\",\"name\":\"not_stored_cookie\",\"operation\":\"store\"," "\"path\":\"/\",\"status\":\"EXCLUDE_USER_PREFERENCES, " "DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[0].params)); EXPECT_EQ("{\"domain\":\"" + set_cookie_test_url.host() + "\",\"name\":\"stored_cookie\",\"operation\":\"store\"," "\"path\":\"/\",\"status\":\"INCLUDE, DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[1].params)); EXPECT_EQ( "{\"domain\":\"" + set_cookie_test_url.host() + "\",\"name\":\"path_cookie\",\"operation\":\"store\"," "\"path\":\"/set-cookie\",\"status\":\"INCLUDE, DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[2].params)); net_log_observer.Clear(); } { TestDelegate d; // Make sure cookies blocked from being sent are caught. GURL test_url = test_server.GetURL("/echoheader?Cookie"); std::unique_ptr req = CreateFirstPartyRequest(*context, test_url, &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("stored_cookie=tasty") == std::string::npos); ASSERT_EQ(2u, req->maybe_sent_cookies().size()); EXPECT_EQ("path_cookie", req->maybe_sent_cookies()[0].cookie.Name()); EXPECT_TRUE( req->maybe_sent_cookies()[0] .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_NOT_ON_PATH, net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); EXPECT_EQ("stored_cookie", req->maybe_sent_cookies()[1].cookie.Name()); EXPECT_TRUE( req->maybe_sent_cookies()[1] .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); auto entries = net_log_observer.GetEntriesWithType( NetLogEventType::COOKIE_INCLUSION_STATUS); EXPECT_EQ(2u, entries.size()); EXPECT_EQ("{\"domain\":\"" + set_cookie_test_url.host() + "\",\"name\":\"path_cookie\",\"operation\":\"send\",\"path\":" "\"/set-cookie\",\"status\":\"EXCLUDE_NOT_ON_PATH, " "EXCLUDE_USER_PREFERENCES, DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[0].params)); EXPECT_EQ( "{\"domain\":\"" + set_cookie_test_url.host() + "\",\"name\":\"stored_cookie\",\"operation\":\"send\",\"path\":\"/" "\",\"status\":\"EXCLUDE_USER_PREFERENCES, DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[1].params)); net_log_observer.Clear(); } { TestDelegate d; // Ensure that the log does not contain cookie names when not set to collect // sensitive data. net_log_observer.SetObserverCaptureMode(NetLogCaptureMode::kDefault); GURL test_url = test_server.GetURL("/echoheader?Cookie"); std::unique_ptr req = CreateFirstPartyRequest(*context, test_url, &d); req->Start(); d.RunUntilComplete(); auto entries = net_log_observer.GetEntriesWithType( NetLogEventType::COOKIE_INCLUSION_STATUS); EXPECT_EQ(2u, entries.size()); // Ensure that the potentially-sensitive |name|, |domain|, and |path| fields // are omitted, but other fields are logged as expected. EXPECT_EQ( "{\"operation\":\"send\",\"status\":\"EXCLUDE_NOT_ON_PATH, " "EXCLUDE_USER_PREFERENCES, DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[0].params)); EXPECT_EQ( "{\"operation\":\"send\",\"status\":\"EXCLUDE_USER_PREFERENCES, " "DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[1].params)); net_log_observer.Clear(); net_log_observer.SetObserverCaptureMode( NetLogCaptureMode::kIncludeSensitive); } network_delegate.unset_block_annotate_cookies(); { // Now with sending cookies re-enabled, it should actually be sent. TestDelegate d; GURL test_url = test_server.GetURL("/echoheader?Cookie"); std::unique_ptr req = CreateFirstPartyRequest(*context, test_url, &d); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("stored_cookie=tasty") != std::string::npos); ASSERT_EQ(2u, req->maybe_sent_cookies().size()); EXPECT_EQ("path_cookie", req->maybe_sent_cookies()[0].cookie.Name()); EXPECT_TRUE(req->maybe_sent_cookies()[0] .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_NOT_ON_PATH})); EXPECT_EQ("stored_cookie", req->maybe_sent_cookies()[1].cookie.Name()); EXPECT_TRUE(req->maybe_sent_cookies()[1].access_result.status.IsInclude()); auto entries = net_log_observer.GetEntriesWithType( NetLogEventType::COOKIE_INCLUSION_STATUS); EXPECT_EQ(2u, entries.size()); EXPECT_EQ( "{\"domain\":\"" + set_cookie_test_url.host() + "\",\"name\":\"path_cookie\",\"operation\":\"send\",\"path\":\"/" "set-cookie\",\"status\":\"EXCLUDE_NOT_ON_PATH, DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[0].params)); EXPECT_EQ("{\"domain\":\"" + set_cookie_test_url.host() + "\",\"name\":\"stored_cookie\",\"operation\":\"send\"," "\"path\":\"/\",\"status\":\"INCLUDE, DO_NOT_WARN\"}", SerializeNetLogValueToJson(entries[1].params)); net_log_observer.Clear(); } } // Test that the SameSite-by-default CookieInclusionStatus warnings do not get // set if the cookie would have been rejected for other reasons. // Regression test for https://crbug.com/1027318. TEST_F(URLRequestTest, NoCookieInclusionStatusWarningIfWouldBeExcludedAnyway) { HttpTestServer test_server; ASSERT_TRUE(test_server.Start()); auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); network_delegate.SetCookieFilter("blockeduserpreference"); context_builder->SetCookieStore(std::make_unique( nullptr, nullptr, /*first_party_sets_enabled=*/false)); auto context = context_builder->Build(); auto& cm = *static_cast(context->cookie_store()); // Set cookies { // Attempt to set some cookies in a cross-site context without a SameSite // attribute. They should all be blocked. Only the one that would have been // included had it not been for the new SameSite features should have a // warning attached. TestDelegate d; GURL test_url = test_server.GetURL("this.example", "/set-cookie?blockeduserpreference=true&" "unspecifiedsamesite=1&" "invalidsecure=1;Secure"); GURL cross_site_url = test_server.GetURL("other.example", "/"); std::unique_ptr req(context->CreateRequest( test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_site_for_cookies( net::SiteForCookies::FromUrl(cross_site_url)); // cross-site context req->Start(); d.RunUntilComplete(); ASSERT_EQ(3u, req->maybe_stored_cookies().size()); // Cookie blocked by user preferences is not warned about. EXPECT_EQ("blockeduserpreference", req->maybe_stored_cookies()[0].cookie->Name()); // It doesn't pick up the EXCLUDE_UNSPECIFIED_TREATED_AS_LAX because it // doesn't even make it to the cookie store (it is filtered out beforehand). EXPECT_TRUE(req->maybe_stored_cookies()[0] .access_result.status.HasExactlyExclusionReasonsForTesting( {CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); EXPECT_FALSE( req->maybe_stored_cookies()[0].access_result.status.ShouldWarn()); // Cookie that would be included had it not been for the new SameSite rules // is warned about. EXPECT_EQ("unspecifiedsamesite", req->maybe_stored_cookies()[1].cookie->Name()); EXPECT_TRUE(req->maybe_stored_cookies()[1] .access_result.status.HasExactlyExclusionReasonsForTesting( {CookieInclusionStatus:: EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); EXPECT_TRUE(req->maybe_stored_cookies()[1] .access_result.status.HasExactlyWarningReasonsForTesting( {CookieInclusionStatus:: WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT})); // Cookie that is blocked because of invalid Secure attribute is not warned // about. EXPECT_EQ("invalidsecure", req->maybe_stored_cookies()[2].cookie->Name()); EXPECT_TRUE(req->maybe_stored_cookies()[2] .access_result.status.HasExactlyExclusionReasonsForTesting( {CookieInclusionStatus::EXCLUDE_SECURE_ONLY, CookieInclusionStatus:: EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); EXPECT_FALSE( req->maybe_stored_cookies()[2].access_result.status.ShouldWarn()); } // Get cookies (blocked by user preference) network_delegate.set_block_annotate_cookies(); { GURL url = test_server.GetURL("/"); auto cookie1 = CanonicalCookie::Create( url, "cookienosamesite=1", base::Time::Now(), absl::nullopt, absl::nullopt /* cookie_partition_key */); base::RunLoop run_loop; CookieAccessResult access_result; cm.SetCanonicalCookieAsync( std::move(cookie1), url, CookieOptions::MakeAllInclusive(), base::BindLambdaForTesting([&](CookieAccessResult result) { access_result = result; run_loop.Quit(); })); run_loop.Run(); EXPECT_TRUE(access_result.status.IsInclude()); TestDelegate d; GURL test_url = test_server.GetURL("/echoheader?Cookie"); std::unique_ptr req(context->CreateRequest( test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); GURL cross_site_url = test_server.GetURL("other.example", "/"); req->set_site_for_cookies( net::SiteForCookies::FromUrl(cross_site_url)); // cross-site context req->Start(); d.RunUntilComplete(); // No cookies were sent with the request because getting cookies is blocked. EXPECT_EQ("None", d.data_received()); ASSERT_EQ(1u, req->maybe_sent_cookies().size()); EXPECT_EQ("cookienosamesite", req->maybe_sent_cookies()[0].cookie.Name()); EXPECT_TRUE(req->maybe_sent_cookies()[0] .access_result.status.HasExactlyExclusionReasonsForTesting( {CookieInclusionStatus::EXCLUDE_USER_PREFERENCES, CookieInclusionStatus:: EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); // Cookie should not be warned about because it was blocked because of user // preferences. EXPECT_FALSE( req->maybe_sent_cookies()[0].access_result.status.ShouldWarn()); } network_delegate.unset_block_annotate_cookies(); // Get cookies { GURL url = test_server.GetURL("/"); auto cookie2 = CanonicalCookie::Create( url, "cookiewithpath=1;path=/foo", base::Time::Now(), absl::nullopt, absl::nullopt /* cookie_partition_key */); base::RunLoop run_loop; // Note: cookie1 from the previous testcase is still in the cookie store. CookieAccessResult access_result; cm.SetCanonicalCookieAsync( std::move(cookie2), url, CookieOptions::MakeAllInclusive(), base::BindLambdaForTesting([&](CookieAccessResult result) { access_result = result; run_loop.Quit(); })); run_loop.Run(); EXPECT_TRUE(access_result.status.IsInclude()); TestDelegate d; GURL test_url = test_server.GetURL("/echoheader?Cookie"); std::unique_ptr req(context->CreateRequest( test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); GURL cross_site_url = test_server.GetURL("other.example", "/"); req->set_site_for_cookies( net::SiteForCookies::FromUrl(cross_site_url)); // cross-site context req->Start(); d.RunUntilComplete(); // No cookies were sent with the request because they don't specify SameSite // and the request is cross-site. EXPECT_EQ("None", d.data_received()); ASSERT_EQ(2u, req->maybe_sent_cookies().size()); // Cookie excluded for other reasons is not warned about. // Note: this cookie is first because the cookies are sorted by path length // with longest first. See CookieSorter() in cookie_monster.cc. EXPECT_EQ("cookiewithpath", req->maybe_sent_cookies()[0].cookie.Name()); EXPECT_TRUE(req->maybe_sent_cookies()[0] .access_result.status.HasExactlyExclusionReasonsForTesting( {CookieInclusionStatus::EXCLUDE_NOT_ON_PATH, CookieInclusionStatus:: EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); EXPECT_FALSE( req->maybe_sent_cookies()[0].access_result.status.ShouldWarn()); // Cookie that was only blocked because of unspecified SameSite should be // warned about. EXPECT_EQ("cookienosamesite", req->maybe_sent_cookies()[1].cookie.Name()); EXPECT_TRUE(req->maybe_sent_cookies()[1] .access_result.status.HasExactlyExclusionReasonsForTesting( {CookieInclusionStatus:: EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); EXPECT_TRUE(req->maybe_sent_cookies()[1] .access_result.status.HasExactlyWarningReasonsForTesting( {CookieInclusionStatus:: WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT})); } } TEST_F(URLRequestTestHTTP, AuthChallengeCancelCookieCollect) { ASSERT_TRUE(http_test_server()->Start()); GURL url_requiring_auth = http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); auto context_builder = CreateTestURLRequestContextBuilder(); auto filtering_network_delegate = std::make_unique(); filtering_network_delegate->SetCookieFilter("got_challenged"); context_builder->set_network_delegate(std::move(filtering_network_delegate)); auto context = context_builder->Build(); TestDelegate delegate; std::unique_ptr request = CreateFirstPartyRequest(*context, url_requiring_auth, &delegate); request->Start(); delegate.RunUntilAuthRequired(); ASSERT_EQ(1u, request->maybe_stored_cookies().size()); EXPECT_TRUE(request->maybe_stored_cookies()[0] .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); EXPECT_EQ("got_challenged=true", request->maybe_stored_cookies()[0].cookie_string); // This shouldn't DCHECK-fail. request->CancelAuth(); delegate.RunUntilComplete(); } TEST_F(URLRequestTestHTTP, AuthChallengeWithFilteredCookies) { ASSERT_TRUE(http_test_server()->Start()); GURL url_requiring_auth = http_test_server()->GetURL("/auth-basic?set-cookie-if-challenged"); GURL url_requiring_auth_wo_cookies = http_test_server()->GetURL("/auth-basic"); // Check maybe_stored_cookies is populated first round trip, and cleared on // the second. { auto context_builder = CreateTestURLRequestContextBuilder(); auto& filtering_network_delegate = *context_builder->set_network_delegate( std::make_unique()); filtering_network_delegate.SetCookieFilter("got_challenged"); auto context = context_builder->Build(); TestDelegate delegate; std::unique_ptr request = CreateFirstPartyRequest(*context, url_requiring_auth, &delegate); request->Start(); delegate.RunUntilAuthRequired(); // Make sure it was blocked once. EXPECT_EQ(1, filtering_network_delegate.blocked_set_cookie_count()); // The number of cookies blocked from the most recent round trip. ASSERT_EQ(1u, request->maybe_stored_cookies().size()); EXPECT_TRUE( request->maybe_stored_cookies() .front() .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); // Now check the second round trip request->SetAuth(AuthCredentials(kUser, kSecret)); delegate.RunUntilComplete(); EXPECT_THAT(delegate.request_status(), IsOk()); // There are DCHECKs in URLRequestHttpJob that would fail if // maybe_sent_cookies and maybe_stored_cookies were not cleared properly. // Make sure the cookie was actually filtered and not sent. EXPECT_EQ(std::string::npos, delegate.data_received().find("Cookie: got_challenged=true")); // The number of cookies that most recent round trip tried to set. ASSERT_EQ(0u, request->maybe_stored_cookies().size()); } // Check maybe_sent_cookies on first round trip (and cleared for the second). { auto context_builder = CreateTestURLRequestContextBuilder(); auto& filtering_network_delegate = *context_builder->set_network_delegate( std::make_unique()); filtering_network_delegate.set_block_annotate_cookies(); context_builder->SetCookieStore(std::make_unique( nullptr, nullptr, /*first_party_sets_enabled=*/false)); auto context = context_builder->Build(); auto* cm = static_cast(context->cookie_store()); auto another_cookie = CanonicalCookie::Create( url_requiring_auth_wo_cookies, "another_cookie=true", base::Time::Now(), absl::nullopt /* server_time */, absl::nullopt /* cookie_partition_key */); cm->SetCanonicalCookieAsync(std::move(another_cookie), url_requiring_auth_wo_cookies, net::CookieOptions::MakeAllInclusive(), CookieStore::SetCookiesCallback()); TestDelegate delegate; std::unique_ptr request = CreateFirstPartyRequest( *context, url_requiring_auth_wo_cookies, &delegate); request->Start(); delegate.RunUntilAuthRequired(); ASSERT_EQ(1u, request->maybe_sent_cookies().size()); EXPECT_EQ("another_cookie", request->maybe_sent_cookies().front().cookie.Name()); EXPECT_EQ("true", request->maybe_sent_cookies().front().cookie.Value()); EXPECT_TRUE( request->maybe_sent_cookies() .front() .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); // Check maybe_sent_cookies on second roundtrip. request->set_maybe_sent_cookies({}); cm->DeleteAllAsync(CookieStore::DeleteCallback()); auto one_more_cookie = CanonicalCookie::Create( url_requiring_auth_wo_cookies, "one_more_cookie=true", base::Time::Now(), absl::nullopt /* server_time */, absl::nullopt /* cookie_partition_key */); cm->SetCanonicalCookieAsync(std::move(one_more_cookie), url_requiring_auth_wo_cookies, net::CookieOptions::MakeAllInclusive(), CookieStore::SetCookiesCallback()); request->SetAuth(AuthCredentials(kUser, kSecret)); delegate.RunUntilComplete(); EXPECT_THAT(delegate.request_status(), IsOk()); // There are DCHECKs in URLRequestHttpJob that would fail if // maybe_sent_cookies and maybe_stored_cookies were not cleared properly. // Make sure the cookie was actually filtered. EXPECT_EQ(std::string::npos, delegate.data_received().find("Cookie: one_more_cookie=true")); // got_challenged was set after the first request and blocked on the second, // so it should only have been blocked this time EXPECT_EQ(2, filtering_network_delegate.blocked_annotate_cookies_count()); // // The number of cookies blocked from the most recent round trip. ASSERT_EQ(1u, request->maybe_sent_cookies().size()); EXPECT_EQ("one_more_cookie", request->maybe_sent_cookies().front().cookie.Name()); EXPECT_TRUE( request->maybe_sent_cookies() .front() .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); } } // Tests that load timing works as expected with auth and the cache. TEST_F(URLRequestTestHTTP, BasicAuthLoadTiming) { ASSERT_TRUE(http_test_server()->Start()); // populate the cache { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_isolation_info(isolation_info1_); r->Start(); d.RunUntilAuthRequired(); LoadTimingInfo load_timing_info_before_auth; r->GetLoadTimingInfo(&load_timing_info_before_auth); TestLoadTimingNotReused(load_timing_info_before_auth, CONNECT_TIMING_HAS_DNS_TIMES); r->SetAuth(AuthCredentials(kUser, kSecret)); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); LoadTimingInfo load_timing_info; r->GetLoadTimingInfo(&load_timing_info); // The test server does not support keep alive sockets, so the second // request with auth should use a new socket. TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); EXPECT_NE(load_timing_info_before_auth.socket_log_id, load_timing_info.socket_log_id); EXPECT_LE(load_timing_info_before_auth.receive_headers_end, load_timing_info.connect_timing.connect_start); } // Repeat request with end-to-end validation. Since auth-basic results in a // cachable page, we expect this test to result in a 304. In which case, the // response should be fetched from the cache. { TestDelegate d; d.set_credentials(AuthCredentials(kUser, kSecret)); std::unique_ptr r(default_context().CreateRequest( http_test_server()->GetURL("/auth-basic"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetLoadFlags(LOAD_VALIDATE_CACHE); r->set_isolation_info(isolation_info1_); r->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.data_received().find("user/secret") != std::string::npos); // Should be the same cached document. EXPECT_TRUE(r->was_cached()); // Since there was a request that went over the wire, the load timing // information should include connection times. LoadTimingInfo load_timing_info; r->GetLoadTimingInfo(&load_timing_info); TestLoadTimingNotReused(load_timing_info, CONNECT_TIMING_HAS_DNS_TIMES); } } // In this test, we do a POST which the server will 302 redirect. // The subsequent transaction should use GET, and should not send the // Content-Type header. // http://code.google.com/p/chromium/issues/detail?id=843 TEST_F(URLRequestTestHTTP, Post302RedirectGet) { ASSERT_TRUE(http_test_server()->Start()); const char kData[] = "hello world"; TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/redirect-to-echoall"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_method("POST"); req->set_upload(CreateSimpleUploadData(kData)); // Set headers (some of which are specific to the POST). HttpRequestHeaders headers; headers.SetHeader("Content-Type", "multipart/form-data;" "boundary=----WebKitFormBoundaryAADeAA+NAAWMAAwZ"); headers.SetHeader("Accept", "text/xml,application/xml,application/xhtml+xml," "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); headers.SetHeader("Accept-Language", "en-US,en"); headers.SetHeader("Accept-Charset", "ISO-8859-1,*,utf-8"); headers.SetHeader("Content-Length", "11"); headers.SetHeader("Origin", "http://localhost:1337/"); req->SetExtraRequestHeaders(headers); req->Start(); d.RunUntilComplete(); std::string mime_type; req->GetMimeType(&mime_type); EXPECT_EQ("text/html", mime_type); const std::string& data = d.data_received(); // Check that the post-specific headers were stripped: EXPECT_FALSE(ContainsString(data, "Content-Length:")); EXPECT_FALSE(ContainsString(data, "Content-Type:")); EXPECT_FALSE(ContainsString(data, "Origin:")); // These extra request headers should not have been stripped. EXPECT_TRUE(ContainsString(data, "Accept:")); EXPECT_TRUE(ContainsString(data, "Accept-Language:")); EXPECT_TRUE(ContainsString(data, "Accept-Charset:")); } // The following tests check that we handle mutating the request for HTTP // redirects as expected. // See https://crbug.com/56373, https://crbug.com/102130, and // https://crbug.com/465517. TEST_F(URLRequestTestHTTP, Redirect301Tests) { ASSERT_TRUE(http_test_server()->Start()); const GURL url = http_test_server()->GetURL("/redirect301-to-echo"); const GURL https_redirect_url = http_test_server()->GetURL("/redirect301-to-https"); HTTPRedirectMethodTest(url, "POST", "GET", true); HTTPRedirectMethodTest(url, "PUT", "PUT", true); HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", std::string()); HTTPRedirectOriginHeaderTest(url, "PUT", "PUT", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "PUT", "null"); } TEST_F(URLRequestTestHTTP, Redirect302Tests) { ASSERT_TRUE(http_test_server()->Start()); const GURL url = http_test_server()->GetURL("/redirect302-to-echo"); const GURL https_redirect_url = http_test_server()->GetURL("/redirect302-to-https"); HTTPRedirectMethodTest(url, "POST", "GET", true); HTTPRedirectMethodTest(url, "PUT", "PUT", true); HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", std::string()); HTTPRedirectOriginHeaderTest(url, "PUT", "PUT", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "PUT", "null"); } TEST_F(URLRequestTestHTTP, Redirect303Tests) { ASSERT_TRUE(http_test_server()->Start()); const GURL url = http_test_server()->GetURL("/redirect303-to-echo"); const GURL https_redirect_url = http_test_server()->GetURL("/redirect303-to-https"); HTTPRedirectMethodTest(url, "POST", "GET", true); HTTPRedirectMethodTest(url, "PUT", "GET", true); HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); HTTPRedirectOriginHeaderTest(url, "CONNECT", "GET", std::string()); HTTPRedirectOriginHeaderTest(https_redirect_url, "CONNECT", "GET", std::string()); HTTPRedirectOriginHeaderTest(url, "DELETE", "GET", std::string()); HTTPRedirectOriginHeaderTest(https_redirect_url, "DELETE", "GET", std::string()); HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); HTTPRedirectOriginHeaderTest(url, "HEAD", "HEAD", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "HEAD", "HEAD", "null"); HTTPRedirectOriginHeaderTest(url, "OPTIONS", "GET", std::string()); HTTPRedirectOriginHeaderTest(https_redirect_url, "OPTIONS", "GET", std::string()); HTTPRedirectOriginHeaderTest(url, "POST", "GET", std::string()); HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "GET", std::string()); HTTPRedirectOriginHeaderTest(url, "PUT", "GET", std::string()); HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "GET", std::string()); } TEST_F(URLRequestTestHTTP, Redirect307Tests) { ASSERT_TRUE(http_test_server()->Start()); const GURL url = http_test_server()->GetURL("/redirect307-to-echo"); const GURL https_redirect_url = http_test_server()->GetURL("/redirect307-to-https"); HTTPRedirectMethodTest(url, "POST", "POST", true); HTTPRedirectMethodTest(url, "PUT", "PUT", true); HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); HTTPRedirectOriginHeaderTest(url, "POST", "POST", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "POST", "null"); HTTPRedirectOriginHeaderTest(url, "PUT", "PUT", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "PUT", "null"); } TEST_F(URLRequestTestHTTP, Redirect308Tests) { ASSERT_TRUE(http_test_server()->Start()); const GURL url = http_test_server()->GetURL("/redirect308-to-echo"); const GURL https_redirect_url = http_test_server()->GetURL("/redirect308-to-https"); HTTPRedirectMethodTest(url, "POST", "POST", true); HTTPRedirectMethodTest(url, "PUT", "PUT", true); HTTPRedirectMethodTest(url, "HEAD", "HEAD", false); HTTPRedirectOriginHeaderTest(url, "GET", "GET", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "GET", "GET", "null"); HTTPRedirectOriginHeaderTest(url, "POST", "POST", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "POST", "POST", "null"); HTTPRedirectOriginHeaderTest(url, "PUT", "PUT", url.DeprecatedGetOriginAsURL().spec()); HTTPRedirectOriginHeaderTest(https_redirect_url, "PUT", "PUT", "null"); } // Make sure that 308 responses without bodies are not treated as redirects. // Certain legacy apis that pre-date the response code expect this behavior // (Like Google Drive). TEST_F(URLRequestTestHTTP, NoRedirectOn308WithoutLocationHeader) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; const GURL url = http_test_server()->GetURL("/308-without-location-header"); std::unique_ptr request(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(0, d.received_redirect_count()); EXPECT_EQ(308, request->response_headers()->response_code()); EXPECT_EQ("This is not a redirect.", d.data_received()); } TEST_F(URLRequestTestHTTP, Redirect302PreserveReferenceFragment) { ASSERT_TRUE(http_test_server()->Start()); GURL original_url( http_test_server()->GetURL("/redirect302-to-echo#fragment")); GURL expected_url(http_test_server()->GetURL("/echo#fragment")); TestDelegate d; std::unique_ptr r(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(original_url, r->original_url()); EXPECT_EQ(expected_url, r->url()); } TEST_F(URLRequestTestHTTP, RedirectWithFilteredCookies) { ASSERT_TRUE(http_test_server()->Start()); // FilteringTestNetworkDelegate filters by name, so the names of the two // cookies have to be the same. The values have been set to different strings // (the value of the server-redirect cookies is "true" and set-cookie is // "other") to differentiate between the two round trips. GURL redirect_to( http_test_server()->GetURL("/set-cookie?server-redirect=other")); GURL original_url(http_test_server()->GetURL("/server-redirect-with-cookie?" + redirect_to.spec())); GURL original_url_wo_cookie( http_test_server()->GetURL("/server-redirect?" + redirect_to.spec())); // Check maybe_stored_cookies on first round trip. { auto context_builder = CreateTestURLRequestContextBuilder(); auto& filtering_network_delegate = *context_builder->set_network_delegate( std::make_unique()); filtering_network_delegate.SetCookieFilter( "server-redirect"); // Filter the cookie server-redirect sets. auto context = context_builder->Build(); TestDelegate delegate; std::unique_ptr request = CreateFirstPartyRequest(*context, original_url, &delegate); request->Start(); delegate.RunUntilRedirect(); // Make sure it was blocked once. EXPECT_EQ(1, filtering_network_delegate.blocked_set_cookie_count()); // The number of cookies blocked from the most recent round trip. ASSERT_EQ(1u, request->maybe_stored_cookies().size()); EXPECT_EQ("server-redirect", request->maybe_stored_cookies().front().cookie->Name()); EXPECT_EQ("true", request->maybe_stored_cookies().front().cookie->Value()); EXPECT_TRUE( request->maybe_stored_cookies() .front() .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); // Check maybe_stored_cookies on second round trip (and clearing from the // first). request->FollowDeferredRedirect(absl::nullopt, absl::nullopt); delegate.RunUntilComplete(); EXPECT_THAT(delegate.request_status(), IsOk()); // There are DCHECKs in URLRequestHttpJob that would fail if // maybe_sent_cookies and maybe_stored_cookies we not cleared properly. // Make sure it was blocked twice. EXPECT_EQ(2, filtering_network_delegate.blocked_set_cookie_count()); // The number of cookies blocked from the most recent round trip. ASSERT_EQ(1u, request->maybe_stored_cookies().size()); EXPECT_EQ("server-redirect", request->maybe_stored_cookies().front().cookie->Name()); EXPECT_EQ("other", request->maybe_stored_cookies().front().cookie->Value()); EXPECT_TRUE( request->maybe_stored_cookies() .front() .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); } // Check maybe_sent_cookies on first round trip. { auto context_builder = CreateTestURLRequestContextBuilder(); auto& filtering_network_delegate = *context_builder->set_network_delegate( std::make_unique()); filtering_network_delegate.set_block_annotate_cookies(); context_builder->SetCookieStore(std::make_unique( nullptr, nullptr, /*first_party_sets_enabled=*/false)); auto context = context_builder->Build(); auto* cm = static_cast(context->cookie_store()); auto another_cookie = CanonicalCookie::Create( original_url, "another_cookie=true", base::Time::Now(), absl::nullopt /* server_time */, absl::nullopt /* cookie_partition_key */); cm->SetCanonicalCookieAsync(std::move(another_cookie), original_url, net::CookieOptions::MakeAllInclusive(), CookieStore::SetCookiesCallback()); TestDelegate delegate; std::unique_ptr request = CreateFirstPartyRequest(*context, original_url_wo_cookie, &delegate); request->Start(); delegate.RunUntilRedirect(); ASSERT_EQ(1u, request->maybe_sent_cookies().size()); EXPECT_EQ("another_cookie", request->maybe_sent_cookies().front().cookie.Name()); EXPECT_TRUE( request->maybe_sent_cookies() .front() .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); // Check maybe_sent_cookies on second round trip request->set_maybe_sent_cookies({}); cm->DeleteAllAsync(CookieStore::DeleteCallback()); auto one_more_cookie = CanonicalCookie::Create( original_url_wo_cookie, "one_more_cookie=true", base::Time::Now(), absl::nullopt /* server_time */, absl::nullopt /* cookie_partition_key */); cm->SetCanonicalCookieAsync(std::move(one_more_cookie), original_url_wo_cookie, net::CookieOptions::MakeAllInclusive(), CookieStore::SetCookiesCallback()); request->FollowDeferredRedirect(absl::nullopt, absl::nullopt); delegate.RunUntilComplete(); EXPECT_THAT(delegate.request_status(), IsOk()); // There are DCHECKs in URLRequestHttpJob that would fail if // maybe_sent_cookies and maybe_stored_cookies we not cleared properly. EXPECT_EQ(2, filtering_network_delegate.blocked_annotate_cookies_count()); // The number of cookies blocked from the most recent round trip. ASSERT_EQ(1u, request->maybe_sent_cookies().size()); EXPECT_EQ("one_more_cookie", request->maybe_sent_cookies().front().cookie.Name()); EXPECT_EQ("true", request->maybe_sent_cookies().front().cookie.Value()); EXPECT_TRUE( request->maybe_sent_cookies() .front() .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES})); } } TEST_F(URLRequestTestHTTP, RedirectPreserveFirstPartyURL) { ASSERT_TRUE(http_test_server()->Start()); GURL url(http_test_server()->GetURL("/redirect302-to-echo")); GURL first_party_url("http://example.com"); TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_site_for_cookies(SiteForCookies::FromUrl(first_party_url)); r->Start(); d.RunUntilComplete(); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(OK, d.request_status()); EXPECT_TRUE(SiteForCookies::FromUrl(first_party_url) .IsEquivalent(r->site_for_cookies())); } TEST_F(URLRequestTestHTTP, RedirectUpdateFirstPartyURL) { ASSERT_TRUE(http_test_server()->Start()); GURL url(http_test_server()->GetURL("/redirect302-to-echo")); GURL original_first_party_url("http://example.com"); GURL expected_first_party_url(http_test_server()->GetURL("/echo")); TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_site_for_cookies(SiteForCookies::FromUrl(original_first_party_url)); r->set_first_party_url_policy( RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); r->Start(); d.RunUntilComplete(); EXPECT_EQ(2U, r->url_chain().size()); EXPECT_EQ(OK, d.request_status()); EXPECT_TRUE(SiteForCookies::FromUrl(expected_first_party_url) .IsEquivalent(r->site_for_cookies())); } TEST_F(URLRequestTestHTTP, InterceptPost302RedirectGet) { ASSERT_TRUE(http_test_server()->Start()); const char kData[] = "hello world"; TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_method("POST"); req->set_upload(CreateSimpleUploadData(kData)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kContentLength, base::NumberToString(std::size(kData) - 1)); req->SetExtraRequestHeaders(headers); std::unique_ptr job = std::make_unique( req.get(), http_test_server()->GetURL("/echo"), RedirectUtil::ResponseCode::REDIRECT_302_FOUND, "Very Good Reason"); TestScopedURLInterceptor interceptor(req->url(), std::move(job)); req->Start(); d.RunUntilComplete(); EXPECT_EQ("GET", req->method()); } TEST_F(URLRequestTestHTTP, InterceptPost307RedirectPost) { ASSERT_TRUE(http_test_server()->Start()); const char kData[] = "hello world"; TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_method("POST"); req->set_upload(CreateSimpleUploadData(kData)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kContentLength, base::NumberToString(std::size(kData) - 1)); req->SetExtraRequestHeaders(headers); std::unique_ptr job = std::make_unique( req.get(), http_test_server()->GetURL("/echo"), RedirectUtil::ResponseCode::REDIRECT_307_TEMPORARY_REDIRECT, "Very Good Reason"); TestScopedURLInterceptor interceptor(req->url(), std::move(job)); req->Start(); d.RunUntilComplete(); EXPECT_EQ("POST", req->method()); EXPECT_EQ(kData, d.data_received()); } // Check that default A-L header is sent. TEST_F(URLRequestTestHTTP, DefaultAcceptLanguage) { ASSERT_TRUE(http_test_server()->Start()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_http_user_agent_settings( std::make_unique("en", std::string())); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( http_test_server()->GetURL("/echoheader?Accept-Language"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ("en", d.data_received()); } // Check that an empty A-L header is not sent. http://crbug.com/77365. TEST_F(URLRequestTestHTTP, EmptyAcceptLanguage) { ASSERT_TRUE(http_test_server()->Start()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_http_user_agent_settings( std::make_unique(std::string(), std::string())); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( http_test_server()->GetURL("/echoheader?Accept-Language"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ("None", d.data_received()); } // Check that if request overrides the A-L header, the default is not appended. // See http://crbug.com/20894 TEST_F(URLRequestTestHTTP, OverrideAcceptLanguage) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Accept-Language"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kAcceptLanguage, "ru"); req->SetExtraRequestHeaders(headers); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string("ru"), d.data_received()); } // Check that default A-E header is sent. TEST_F(URLRequestTestHTTP, DefaultAcceptEncoding) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Accept-Encoding"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; req->SetExtraRequestHeaders(headers); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(ContainsString(d.data_received(), "gzip")); } // Check that it's possible to override the default A-E header. TEST_F(URLRequestTestHTTP, DefaultAcceptEncodingOverriden) { ASSERT_TRUE(http_test_server()->Start()); struct { base::flat_set accepted_types; const char* expected_accept_encoding; } tests[] = {{{net::SourceStream::SourceType::TYPE_DEFLATE}, "deflate"}, {{}, "None"}, {{net::SourceStream::SourceType::TYPE_GZIP}, "gzip"}, {{net::SourceStream::SourceType::TYPE_GZIP, net::SourceStream::SourceType::TYPE_DEFLATE}, "gzip, deflate"}}; for (auto test : tests) { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Accept-Encoding"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_accepted_stream_types(test.accepted_types); req->Start(); d.RunUntilComplete(); EXPECT_STRCASEEQ(d.data_received().c_str(), test.expected_accept_encoding); } } // Check that if request overrides the A-E header, the default is not appended. // See http://crbug.com/47381 TEST_F(URLRequestTestHTTP, OverrideAcceptEncoding) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Accept-Encoding"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kAcceptEncoding, "identity"); req->SetExtraRequestHeaders(headers); req->Start(); d.RunUntilComplete(); EXPECT_FALSE(ContainsString(d.data_received(), "gzip")); EXPECT_TRUE(ContainsString(d.data_received(), "identity")); } // Check that setting the A-C header sends the proper header. TEST_F(URLRequestTestHTTP, SetAcceptCharset) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?Accept-Charset"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kAcceptCharset, "koi-8r"); req->SetExtraRequestHeaders(headers); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string("koi-8r"), d.data_received()); } // Check that default User-Agent header is sent. TEST_F(URLRequestTestHTTP, DefaultUserAgent) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?User-Agent"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(default_context().http_user_agent_settings()->GetUserAgent(), d.data_received()); } // Check that if request overrides the User-Agent header, // the default is not appended. // TODO(crbug.com/564656) This test is flaky on iOS. #if BUILDFLAG(IS_IOS) #define MAYBE_OverrideUserAgent FLAKY_OverrideUserAgent #else #define MAYBE_OverrideUserAgent OverrideUserAgent #endif TEST_F(URLRequestTestHTTP, MAYBE_OverrideUserAgent) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/echoheader?User-Agent"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kUserAgent, "Lynx (textmode)"); req->SetExtraRequestHeaders(headers); req->Start(); d.RunUntilComplete(); EXPECT_EQ(std::string("Lynx (textmode)"), d.data_received()); } // Check that a NULL HttpUserAgentSettings causes the corresponding empty // User-Agent header to be sent but does not send the Accept-Language and // Accept-Charset headers. TEST_F(URLRequestTestHTTP, EmptyHttpUserAgentSettings) { ASSERT_TRUE(http_test_server()->Start()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_http_user_agent_settings(nullptr); auto context = context_builder->Build(); struct { const char* request; const char* expected_response; } tests[] = {{"/echoheader?Accept-Language", "None"}, {"/echoheader?Accept-Charset", "None"}, {"/echoheader?User-Agent", ""}}; for (const auto& test : tests) { TestDelegate d; std::unique_ptr req(context->CreateRequest( http_test_server()->GetURL(test.request), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(test.expected_response, d.data_received()) << " Request = \"" << test.request << "\""; } } // Make sure that URLRequest passes on its priority updates to // newly-created jobs after the first one. TEST_F(URLRequestTestHTTP, SetSubsequentJobPriority) { GURL initial_url("http://foo.test/"); GURL redirect_url("http://bar.test/"); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( initial_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_EQ(DEFAULT_PRIORITY, req->priority()); std::unique_ptr redirect_job = std::make_unique( req.get(), redirect_url, RedirectUtil::ResponseCode::REDIRECT_302_FOUND, "Very Good Reason"); auto interceptor = std::make_unique( initial_url, std::move(redirect_job)); req->SetPriority(LOW); req->Start(); EXPECT_TRUE(req->is_pending()); d.RunUntilRedirect(); interceptor.reset(); RequestPriority job_priority; std::unique_ptr job = std::make_unique(req.get(), &job_priority); interceptor = std::make_unique(redirect_url, std::move(job)); // Should trigger |job| to be started. req->FollowDeferredRedirect(absl::nullopt /* removed_headers */, absl::nullopt /* modified_headers */); d.RunUntilComplete(); EXPECT_EQ(LOW, job_priority); } // Check that creating a network request while entering/exiting suspend mode // fails as it should. This is the only case where an HttpTransactionFactory // does not return an HttpTransaction. TEST_F(URLRequestTestHTTP, NetworkSuspendTest) { auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCreateHttpTransactionFactoryCallback( base::BindOnce([](HttpNetworkSession* session) { // Create a new HttpNetworkLayer that thinks it's suspended. auto network_layer = std::make_unique(session); network_layer->OnSuspend(); std::unique_ptr factory = std::make_unique(std::move(network_layer), HttpCache::DefaultBackend::InMemory(0)); return factory; })); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://127.0.0.1/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(d.request_failed()); EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, d.request_status()); } namespace { // HttpTransactionFactory that synchronously fails to create transactions. class FailingHttpTransactionFactory : public HttpTransactionFactory { public: explicit FailingHttpTransactionFactory(HttpNetworkSession* network_session) : network_session_(network_session) {} FailingHttpTransactionFactory(const FailingHttpTransactionFactory&) = delete; FailingHttpTransactionFactory& operator=( const FailingHttpTransactionFactory&) = delete; ~FailingHttpTransactionFactory() override = default; // HttpTransactionFactory methods: int CreateTransaction(RequestPriority priority, std::unique_ptr* trans) override { return ERR_FAILED; } HttpCache* GetCache() override { return nullptr; } HttpNetworkSession* GetSession() override { return network_session_; } private: raw_ptr network_session_; }; } // namespace // Check that when a request that fails to create an HttpTransaction can be // cancelled while the failure notification is pending, and doesn't send two // failure notifications. // // This currently only happens when in suspend mode and there's no cache, but // just use a special HttpTransactionFactory, to avoid depending on those // behaviors. // // Flaky crash: https://crbug.com/1348418 #if BUILDFLAG(IS_CHROMEOS) #define MAYBE_NetworkCancelAfterCreateTransactionFailsTest \ DISABLED_NetworkCancelAfterCreateTransactionFailsTest #else #define MAYBE_NetworkCancelAfterCreateTransactionFailsTest \ NetworkCancelAfterCreateTransactionFailsTest #endif TEST_F(URLRequestTestHTTP, MAYBE_NetworkCancelAfterCreateTransactionFailsTest) { auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCreateHttpTransactionFactoryCallback( base::BindOnce([](HttpNetworkSession* session) { std::unique_ptr factory = std::make_unique(session); return factory; })); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req( context->CreateRequest(GURL("http://127.0.0.1/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Don't send cookies (Collecting cookies is asynchronous, and need request to // try to create an HttpNetworkTransaction synchronously on start). req->set_allow_credentials(false); req->Start(); req->Cancel(); d.RunUntilComplete(); // Run pending error task, if there is one. base::RunLoop().RunUntilIdle(); EXPECT_TRUE(d.request_failed()); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(ERR_ABORTED, d.request_status()); // NetworkDelegate should see the cancellation, but not the error. EXPECT_EQ(1, network_delegate.canceled_requests()); EXPECT_EQ(0, network_delegate.error_count()); } TEST_F(URLRequestTestHTTP, NetworkAccessedSetOnNetworkRequest) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; GURL test_url(http_test_server()->GetURL("/")); std::unique_ptr req(default_context().CreateRequest( test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(req->response_info().network_accessed); } TEST_F(URLRequestTestHTTP, NetworkAccessedClearOnCachedResponse) { ASSERT_TRUE(http_test_server()->Start()); // Populate the cache. TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info(isolation_info1_); req->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_TRUE(req->response_info().network_accessed); EXPECT_FALSE(req->response_info().was_cached); req = default_context().CreateRequest( http_test_server()->GetURL("/cachetime"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS); req->set_isolation_info(isolation_info1_); req->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_FALSE(req->response_info().network_accessed); EXPECT_TRUE(req->response_info().was_cached); } TEST_F(URLRequestTestHTTP, NetworkAccessedClearOnLoadOnlyFromCache) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; GURL test_url(http_test_server()->GetURL("/")); std::unique_ptr req(default_context().CreateRequest( test_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->SetLoadFlags(LOAD_ONLY_FROM_CACHE | LOAD_SKIP_CACHE_VALIDATION); req->Start(); d.RunUntilComplete(); EXPECT_FALSE(req->response_info().network_accessed); } // Test that a single job with a THROTTLED priority completes // correctly in the absence of contention. TEST_F(URLRequestTestHTTP, ThrottledPriority) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; GURL test_url(http_test_server()->GetURL("/")); std::unique_ptr req(default_context().CreateRequest( test_url, THROTTLED, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); } TEST_F(URLRequestTestHTTP, RawBodyBytesNoContentEncoding) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/simple.html"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(5, req->GetRawBodyBytes()); } TEST_F(URLRequestTestHTTP, RawBodyBytesGzipEncoding) { ASSERT_TRUE(http_test_server()->Start()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/gzip-encoded"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(30, req->GetRawBodyBytes()); } // Check that if NetworkDelegate::OnBeforeStartTransaction returns an error, // the delegate isn't called back synchronously. TEST_F(URLRequestTestHTTP, TesBeforeStartTransactionFails) { ASSERT_TRUE(http_test_server()->Start()); default_network_delegate().set_before_start_transaction_fails(); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); DCHECK(!d.response_completed()); d.RunUntilComplete(); DCHECK(d.response_completed()); EXPECT_EQ(ERR_FAILED, d.request_status()); } class URLRequestTestReferrerPolicy : public URLRequestTest { public: URLRequestTestReferrerPolicy() = default; void InstantiateSameOriginServers(net::EmbeddedTestServer::Type type) { origin_server_ = std::make_unique(type); RegisterDefaultHandlers(origin_server_.get()); ASSERT_TRUE(origin_server_->Start()); } void InstantiateCrossOriginServers(net::EmbeddedTestServer::Type origin_type, net::EmbeddedTestServer::Type dest_type) { origin_server_ = std::make_unique(origin_type); RegisterDefaultHandlers(origin_server_.get()); ASSERT_TRUE(origin_server_->Start()); destination_server_ = std::make_unique(dest_type); RegisterDefaultHandlers(destination_server_.get()); ASSERT_TRUE(destination_server_->Start()); } void VerifyReferrerAfterRedirect(ReferrerPolicy policy, const GURL& referrer, const GURL& expected) { // Create and execute the request: we'll only have a |destination_server_| // if the origins are meant to be distinct. Otherwise, we'll use the // |origin_server_| for both endpoints. GURL destination_url = destination_server_ ? destination_server_->GetURL("/echoheader?Referer") : origin_server_->GetURL("/echoheader?Referer"); GURL origin_url = origin_server_->GetURL("/server-redirect?" + destination_url.spec()); TestDelegate d; std::unique_ptr req(default_context().CreateRequest( origin_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_referrer_policy(policy); req->SetReferrer(referrer.spec()); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(1, d.received_redirect_count()); EXPECT_EQ(destination_url, req->url()); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(200, req->response_headers()->response_code()); EXPECT_EQ(expected.spec(), req->referrer()); if (expected.is_empty()) EXPECT_EQ("None", d.data_received()); else EXPECT_EQ(expected.spec(), d.data_received()); } EmbeddedTestServer* origin_server() const { return origin_server_.get(); } private: std::unique_ptr origin_server_; std::unique_ptr destination_server_; }; TEST_F(URLRequestTestReferrerPolicy, HTTPToSameOriginHTTP) { InstantiateSameOriginServers(net::EmbeddedTestServer::TYPE_HTTP); GURL referrer = origin_server()->GetURL("/path/to/file.html"); VerifyReferrerAfterRedirect( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, referrer); VerifyReferrerAfterRedirect( ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer); VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer); VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); } TEST_F(URLRequestTestReferrerPolicy, HTTPToCrossOriginHTTP) { InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTP, net::EmbeddedTestServer::TYPE_HTTP); GURL referrer = origin_server()->GetURL("/path/to/file.html"); VerifyReferrerAfterRedirect( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, referrer); VerifyReferrerAfterRedirect( ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL()); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); } TEST_F(URLRequestTestReferrerPolicy, HTTPSToSameOriginHTTPS) { InstantiateSameOriginServers(net::EmbeddedTestServer::TYPE_HTTPS); GURL referrer = origin_server()->GetURL("/path/to/file.html"); VerifyReferrerAfterRedirect( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, referrer); VerifyReferrerAfterRedirect( ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer); VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer); VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, referrer, referrer); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); } TEST_F(URLRequestTestReferrerPolicy, HTTPSToCrossOriginHTTPS) { InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTPS, net::EmbeddedTestServer::TYPE_HTTPS); GURL referrer = origin_server()->GetURL("/path/to/file.html"); VerifyReferrerAfterRedirect( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, referrer); VerifyReferrerAfterRedirect( ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, origin_server()->GetURL("/")); VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, origin_server()->GetURL("/")); VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL()); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); } TEST_F(URLRequestTestReferrerPolicy, HTTPToHTTPS) { InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTP, net::EmbeddedTestServer::TYPE_HTTPS); GURL referrer = origin_server()->GetURL("/path/to/file.html"); VerifyReferrerAfterRedirect( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, referrer); VerifyReferrerAfterRedirect( ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, origin_server()->GetURL("/")); VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, origin_server()->GetURL("/")); VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL()); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); } TEST_F(URLRequestTestReferrerPolicy, HTTPSToHTTP) { InstantiateCrossOriginServers(net::EmbeddedTestServer::TYPE_HTTPS, net::EmbeddedTestServer::TYPE_HTTP); GURL referrer = origin_server()->GetURL("/path/to/file.html"); VerifyReferrerAfterRedirect( ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer, GURL()); VerifyReferrerAfterRedirect( ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL()); VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, referrer, origin_server()->GetURL("/")); VerifyReferrerAfterRedirect(ReferrerPolicy::NEVER_CLEAR, referrer, referrer); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin; thus this test case just // checks that this policy doesn't cause the referrer to change when following // a redirect. VerifyReferrerAfterRedirect(ReferrerPolicy::ORIGIN, referrer.DeprecatedGetOriginAsURL(), referrer.DeprecatedGetOriginAsURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, referrer, GURL()); // The original referrer set on the request is expected to obey the referrer // policy and already be stripped to the origin, though it should be // subsequently cleared during the downgrading redirect. VerifyReferrerAfterRedirect( ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, referrer.DeprecatedGetOriginAsURL(), GURL()); VerifyReferrerAfterRedirect(ReferrerPolicy::NO_REFERRER, GURL(), GURL()); } class HTTPSRequestTest : public TestWithTaskEnvironment { public: HTTPSRequestTest() { auto context_builder = CreateTestURLRequestContextBuilder(); default_context_ = context_builder->Build(); } ~HTTPSRequestTest() override { SetTransportSecurityStateSourceForTesting(nullptr); } URLRequestContext& default_context() { return *default_context_; } private: std::unique_ptr default_context_; }; TEST_F(HTTPSRequestTest, HTTPSGetTest) { EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); TestDelegate d; { std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_NE(0, d.bytes_received()); CheckSSLInfo(r->ssl_info()); EXPECT_EQ(test_server.host_port_pair().host(), r->GetResponseRemoteEndpoint().ToStringWithoutPort()); EXPECT_EQ(test_server.host_port_pair().port(), r->GetResponseRemoteEndpoint().port()); } } TEST_F(HTTPSRequestTest, HTTPSMismatchedTest) { EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); bool err_allowed = true; for (int i = 0; i < 2; i++, err_allowed = !err_allowed) { TestDelegate d; { d.set_allow_certificate_errors(err_allowed); std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_TRUE(d.have_certificate_errors()); if (err_allowed) { EXPECT_NE(0, d.bytes_received()); CheckSSLInfo(r->ssl_info()); } else { EXPECT_EQ(0, d.bytes_received()); } } } } TEST_F(HTTPSRequestTest, HTTPSExpiredTest) { EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); // Iterate from false to true, just so that we do the opposite of the // previous test in order to increase test coverage. bool err_allowed = false; for (int i = 0; i < 2; i++, err_allowed = !err_allowed) { TestDelegate d; { d.set_allow_certificate_errors(err_allowed); std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_TRUE(d.have_certificate_errors()); if (err_allowed) { EXPECT_NE(0, d.bytes_received()); CheckSSLInfo(r->ssl_info()); } else { EXPECT_EQ(0, d.bytes_received()); } } } } // A TestDelegate used to test that an appropriate net error code is provided // when an SSL certificate error occurs. class SSLNetErrorTestDelegate : public TestDelegate { public: void OnSSLCertificateError(URLRequest* request, int net_error, const SSLInfo& ssl_info, bool fatal) override { net_error_ = net_error; on_ssl_certificate_error_called_ = true; TestDelegate::OnSSLCertificateError(request, net_error, ssl_info, fatal); } bool on_ssl_certificate_error_called() { return on_ssl_certificate_error_called_; } int net_error() { return net_error_; } private: bool on_ssl_certificate_error_called_ = false; int net_error_ = net::OK; }; // Tests that the URLRequest::Delegate receives an appropriate net error code // when an SSL certificate error occurs. TEST_F(HTTPSRequestTest, SSLNetErrorReportedToDelegate) { EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); SSLNetErrorTestDelegate d; std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_TRUE(d.on_ssl_certificate_error_called()); EXPECT_EQ(net::ERR_CERT_DATE_INVALID, d.net_error()); } // TODO(svaldez): iOS tests are flaky with EmbeddedTestServer and transport // security state. (see http://crbug.com/550977). #if !BUILDFLAG(IS_IOS) // This tests that a load of a domain with preloaded HSTS and HPKP with a // certificate error sets the |certificate_errors_are_fatal| flag correctly. // This flag will cause the interstitial to be fatal. TEST_F(HTTPSRequestTest, HTTPSPreloadedHSTSTest) { SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); test_server.ServeFilesFromSourceDirectory("net/data/ssl"); ASSERT_TRUE(test_server.Start()); // We require that the URL be hsts-hpkp-preloaded.test. This is a test domain // that has a preloaded HSTS+HPKP entry in the TransportSecurityState. This // means that we have to use a MockHostResolver in order to direct // hsts-hpkp-preloaded.test to the testserver. auto host_resolver = std::make_unique(); host_resolver->rules()->AddRule("hsts-hpkp-preloaded.test", test_server.GetIPLiteralString()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_host_resolver(std::move(host_resolver)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr r(context->CreateRequest( GURL(base::StringPrintf("https://hsts-hpkp-preloaded.test:%d", test_server.host_port_pair().port())), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_TRUE(d.have_certificate_errors()); EXPECT_TRUE(d.certificate_errors_are_fatal()); } // This tests that cached HTTPS page loads do not cause any updates to the // TransportSecurityState. TEST_F(HTTPSRequestTest, HTTPSErrorsNoClobberTSSTest) { base::test::ScopedFeatureList scoped_feature_list_; scoped_feature_list_.InitAndEnableFeature( net::features::kStaticKeyPinningEnforcement); SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); // The actual problem -- CERT_MISMATCHED_NAME in this case -- doesn't // matter. It just has to be any error. EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); test_server.ServeFilesFromSourceDirectory("net/data/ssl"); ASSERT_TRUE(test_server.Start()); // We require that the URL be hsts-hpkp-preloaded.test. This is a test domain // that has a preloaded HSTS+HPKP entry in the TransportSecurityState. This // means that we have to use a MockHostResolver in order to direct // hsts-hpkp-preloaded.test to the testserver. auto host_resolver = std::make_unique(); host_resolver->rules()->AddRule("hsts-hpkp-preloaded.test", test_server.GetIPLiteralString()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_host_resolver(std::move(host_resolver)); auto context = context_builder->Build(); ASSERT_TRUE(context->transport_security_state()); TransportSecurityState& transport_security_state = *context->transport_security_state(); transport_security_state.EnableStaticPinsForTesting(); transport_security_state.SetPinningListAlwaysTimelyForTesting(true); TransportSecurityState::STSState static_sts_state; TransportSecurityState::PKPState static_pkp_state; EXPECT_TRUE(transport_security_state.GetStaticSTSState( "hsts-hpkp-preloaded.test", &static_sts_state)); EXPECT_TRUE(transport_security_state.GetStaticPKPState( "hsts-hpkp-preloaded.test", &static_pkp_state)); TransportSecurityState::STSState dynamic_sts_state; TransportSecurityState::PKPState dynamic_pkp_state; EXPECT_FALSE(transport_security_state.GetDynamicSTSState( "hsts-hpkp-preloaded.test", &dynamic_sts_state)); EXPECT_FALSE(transport_security_state.GetDynamicPKPState( "hsts-hpkp-preloaded.test", &dynamic_pkp_state)); TestDelegate d; std::unique_ptr r(context->CreateRequest( GURL(base::StringPrintf("https://hsts-hpkp-preloaded.test:%d", test_server.host_port_pair().port())), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_TRUE(d.have_certificate_errors()); EXPECT_TRUE(d.certificate_errors_are_fatal()); // Get a fresh copy of the states, and check that they haven't changed. TransportSecurityState::STSState new_static_sts_state; TransportSecurityState::PKPState new_static_pkp_state; EXPECT_TRUE(transport_security_state.GetStaticSTSState( "hsts-hpkp-preloaded.test", &new_static_sts_state)); EXPECT_TRUE(transport_security_state.GetStaticPKPState( "hsts-hpkp-preloaded.test", &new_static_pkp_state)); TransportSecurityState::STSState new_dynamic_sts_state; TransportSecurityState::PKPState new_dynamic_pkp_state; EXPECT_FALSE(transport_security_state.GetDynamicSTSState( "hsts-hpkp-preloaded.test", &new_dynamic_sts_state)); EXPECT_FALSE(transport_security_state.GetDynamicPKPState( "hsts-hpkp-preloaded.test", &new_dynamic_pkp_state)); EXPECT_EQ(new_static_sts_state.upgrade_mode, static_sts_state.upgrade_mode); EXPECT_EQ(new_static_sts_state.include_subdomains, static_sts_state.include_subdomains); EXPECT_EQ(new_static_pkp_state.include_subdomains, static_pkp_state.include_subdomains); EXPECT_EQ(new_static_pkp_state.spki_hashes, static_pkp_state.spki_hashes); EXPECT_EQ(new_static_pkp_state.bad_spki_hashes, static_pkp_state.bad_spki_hashes); } // Make sure HSTS preserves a POST request's method and body. TEST_F(HTTPSRequestTest, HSTSPreservesPosts) { static const char kData[] = "hello world"; EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); // Per spec, TransportSecurityState expects a domain name, rather than an IP // address, so a MockHostResolver is needed to redirect www.somewhere.com to // the EmbeddedTestServer. auto host_resolver = std::make_unique(); host_resolver->rules()->AddRule("www.somewhere.com", test_server.GetIPLiteralString()); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_host_resolver(std::move(host_resolver)); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique()); auto context = context_builder->Build(); ASSERT_TRUE(context->transport_security_state()); TransportSecurityState& transport_security_state = *context->transport_security_state(); // Force https for www.somewhere.com. base::Time expiry = base::Time::Now() + base::Days(1000); bool include_subdomains = false; transport_security_state.AddHSTS("www.somewhere.com", expiry, include_subdomains); TestDelegate d; // Navigating to https://www.somewhere.com instead of https://127.0.0.1 will // cause a certificate error. Ignore the error. d.set_allow_certificate_errors(true); std::unique_ptr req(context->CreateRequest( GURL(base::StringPrintf("http://www.somewhere.com:%d/echo", test_server.host_port_pair().port())), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_method("POST"); req->set_upload(CreateSimpleUploadData(kData)); req->Start(); d.RunUntilComplete(); EXPECT_EQ("https", req->url().scheme()); EXPECT_EQ("POST", req->method()); EXPECT_EQ(kData, d.data_received()); LoadTimingInfo load_timing_info; network_delegate.GetLoadTimingInfoBeforeRedirect(&load_timing_info); // LoadTimingInfo of HSTS redirects is similar to that of network cache hits TestLoadTimingCacheHitNoNetwork(load_timing_info); } // Make sure that the CORS headers are added to cross-origin HSTS redirects. TEST_F(HTTPSRequestTest, HSTSCrossOriginAddHeaders) { static const char kOriginHeaderValue[] = "http://www.example.com"; EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); test_server.ServeFilesFromSourceDirectory("net/data/ssl"); ASSERT_TRUE(test_server.Start()); auto cert_verifier = std::make_unique(); cert_verifier->set_default_result(OK); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier(std::move(cert_verifier)); auto context = context_builder->Build(); ASSERT_TRUE(context->transport_security_state()); TransportSecurityState& transport_security_state = *context->transport_security_state(); base::Time expiry = base::Time::Now() + base::Days(1); bool include_subdomains = false; transport_security_state.AddHSTS("example.net", expiry, include_subdomains); GURL hsts_http_url(base::StringPrintf("http://example.net:%d/somehstssite", test_server.host_port_pair().port())); GURL::Replacements replacements; replacements.SetSchemeStr("https"); GURL hsts_https_url = hsts_http_url.ReplaceComponents(replacements); TestDelegate d; std::unique_ptr req(context->CreateRequest( hsts_http_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Set Origin header to simulate a cross-origin request. HttpRequestHeaders request_headers; request_headers.SetHeader("Origin", kOriginHeaderValue); req->SetExtraRequestHeaders(request_headers); req->Start(); d.RunUntilRedirect(); EXPECT_EQ(1, d.received_redirect_count()); const HttpResponseHeaders* headers = req->response_headers(); std::string redirect_location; EXPECT_TRUE( headers->EnumerateHeader(nullptr, "Location", &redirect_location)); EXPECT_EQ(hsts_https_url.spec(), redirect_location); std::string received_cors_header; EXPECT_TRUE(headers->EnumerateHeader(nullptr, "Access-Control-Allow-Origin", &received_cors_header)); EXPECT_EQ(kOriginHeaderValue, received_cors_header); std::string received_corp_header; EXPECT_TRUE(headers->EnumerateHeader(nullptr, "Cross-Origin-Resource-Policy", &received_corp_header)); EXPECT_EQ("Cross-Origin", received_corp_header); } namespace { class SSLClientAuthTestDelegate : public TestDelegate { public: SSLClientAuthTestDelegate() { set_on_complete(base::DoNothing()); } void OnCertificateRequested(URLRequest* request, SSLCertRequestInfo* cert_request_info) override { on_certificate_requested_count_++; std::move(on_certificate_requested_).Run(); } void RunUntilCertificateRequested() { base::RunLoop run_loop; on_certificate_requested_ = run_loop.QuitClosure(); run_loop.Run(); } int on_certificate_requested_count() { return on_certificate_requested_count_; } private: int on_certificate_requested_count_ = 0; base::OnceClosure on_certificate_requested_; }; class TestSSLPrivateKey : public SSLPrivateKey { public: explicit TestSSLPrivateKey(scoped_refptr key) : key_(std::move(key)) {} void set_fail_signing(bool fail_signing) { fail_signing_ = fail_signing; } int sign_count() const { return sign_count_; } std::string GetProviderName() override { return key_->GetProviderName(); } std::vector GetAlgorithmPreferences() override { return key_->GetAlgorithmPreferences(); } void Sign(uint16_t algorithm, base::span input, SignCallback callback) override { sign_count_++; if (fail_signing_) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(callback), ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED, std::vector())); } else { key_->Sign(algorithm, input, std::move(callback)); } } private: ~TestSSLPrivateKey() override = default; scoped_refptr key_; bool fail_signing_ = false; int sign_count_ = 0; }; } // namespace // TODO(davidben): Test the rest of the code. Specifically, // - Filtering which certificates to select. // - Getting a certificate request in an SSL renegotiation sending the // HTTP request. TEST_F(HTTPSRequestTest, ClientAuthNoCertificate) { EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; ssl_config.client_cert_type = SSLServerConfig::ClientCertType::OPTIONAL_CLIENT_CERT; test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); SSLClientAuthTestDelegate d; { std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilCertificateRequested(); EXPECT_TRUE(r->is_pending()); EXPECT_EQ(1, d.on_certificate_requested_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); // Send no certificate. // TODO(davidben): Get temporary client cert import (with keys) working on // all platforms so we can test sending a cert as well. r->ContinueWithCertificate(nullptr, nullptr); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_NE(0, d.bytes_received()); } } TEST_F(HTTPSRequestTest, ClientAuth) { std::unique_ptr identity = FakeClientCertIdentity::CreateFromCertAndKeyFiles( GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); ASSERT_TRUE(identity); scoped_refptr private_key = base::MakeRefCounted(identity->ssl_private_key()); EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; ssl_config.client_cert_type = SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); { SSLClientAuthTestDelegate d; std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilCertificateRequested(); EXPECT_TRUE(r->is_pending()); EXPECT_EQ(1, d.on_certificate_requested_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); // Send a certificate. r->ContinueWithCertificate(identity->certificate(), private_key); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_NE(0, d.bytes_received()); // The private key should have been used. EXPECT_EQ(1, private_key->sign_count()); } // Close all connections and clear the session cache to force a new handshake. default_context() .http_transaction_factory() ->GetSession() ->CloseAllConnections(ERR_FAILED, "Very good reason"); default_context() .http_transaction_factory() ->GetSession() ->ClearSSLSessionCache(); // Connecting again should not call OnCertificateRequested. The identity is // taken from the client auth cache. { SSLClientAuthTestDelegate d; std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(0, d.on_certificate_requested_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_NE(0, d.bytes_received()); // The private key should have been used. EXPECT_EQ(2, private_key->sign_count()); } } // Test that private keys that fail to sign anything get evicted from the cache. TEST_F(HTTPSRequestTest, ClientAuthFailSigning) { std::unique_ptr identity = FakeClientCertIdentity::CreateFromCertAndKeyFiles( GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); ASSERT_TRUE(identity); scoped_refptr private_key = base::MakeRefCounted(identity->ssl_private_key()); private_key->set_fail_signing(true); EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; ssl_config.client_cert_type = SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); { SSLClientAuthTestDelegate d; std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilCertificateRequested(); EXPECT_TRUE(r->is_pending()); EXPECT_EQ(1, d.on_certificate_requested_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); // Send a certificate. r->ContinueWithCertificate(identity->certificate(), private_key); d.RunUntilComplete(); // The private key cannot sign anything, so we report an error. EXPECT_EQ(ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED, d.request_status()); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); // The private key should have been used. EXPECT_EQ(1, private_key->sign_count()); } // Close all connections and clear the session cache to force a new handshake. default_context() .http_transaction_factory() ->GetSession() ->CloseAllConnections(ERR_FAILED, "Very good reason"); default_context() .http_transaction_factory() ->GetSession() ->ClearSSLSessionCache(); // The bad identity should have been evicted from the cache, so connecting // again should call OnCertificateRequested again. { SSLClientAuthTestDelegate d; std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilCertificateRequested(); EXPECT_TRUE(r->is_pending()); EXPECT_EQ(1, d.on_certificate_requested_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); // There should have been no additional uses of the private key. EXPECT_EQ(1, private_key->sign_count()); } } // Test that cached private keys that fail to sign anything trigger a // retry. This is so we handle unplugged smartcards // gracefully. https://crbug.com/813022. TEST_F(HTTPSRequestTest, ClientAuthFailSigningRetry) { std::unique_ptr identity = FakeClientCertIdentity::CreateFromCertAndKeyFiles( GetTestCertsDirectory(), "client_1.pem", "client_1.pk8"); ASSERT_TRUE(identity); scoped_refptr private_key = base::MakeRefCounted(identity->ssl_private_key()); EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS); net::SSLServerConfig ssl_config; ssl_config.client_cert_type = SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT; test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); // Connect with a client certificate to put it in the client auth cache. { SSLClientAuthTestDelegate d; std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilCertificateRequested(); EXPECT_TRUE(r->is_pending()); EXPECT_EQ(1, d.on_certificate_requested_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); r->ContinueWithCertificate(identity->certificate(), private_key); d.RunUntilComplete(); EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_NE(0, d.bytes_received()); // The private key should have been used. EXPECT_EQ(1, private_key->sign_count()); } // Close all connections and clear the session cache to force a new handshake. default_context() .http_transaction_factory() ->GetSession() ->CloseAllConnections(ERR_FAILED, "Very good reason"); default_context() .http_transaction_factory() ->GetSession() ->ClearSSLSessionCache(); // Cause the private key to fail. Connecting again should attempt to use it, // notice the failure, and then request a new identity via // OnCertificateRequested. private_key->set_fail_signing(true); { SSLClientAuthTestDelegate d; std::unique_ptr r(default_context().CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilCertificateRequested(); EXPECT_TRUE(r->is_pending()); // There was an additional signing call on the private key (the one which // failed). EXPECT_EQ(2, private_key->sign_count()); // That caused another OnCertificateRequested call. EXPECT_EQ(1, d.on_certificate_requested_count()); EXPECT_FALSE(d.received_data_before_response()); EXPECT_EQ(0, d.bytes_received()); } } TEST_F(HTTPSRequestTest, ResumeTest) { // Test that we attempt resume sessions when making two connections to the // same host. EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); const auto url = test_server.GetURL("/"); default_context() .http_transaction_factory() ->GetSession() ->ClearSSLSessionCache(); { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r->ssl_info().handshake_type); } reinterpret_cast(default_context().http_transaction_factory()) ->CloseAllConnections(ERR_FAILED, "Very good reason"); { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, r->ssl_info().handshake_type); } } // Test that sessions aren't resumed across URLRequestContexts. TEST_F(HTTPSRequestTest, SSLSessionCacheShardTest) { // Start a server. EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); const auto url = test_server.GetURL("/"); // Connect to the server once. This will add an entry to the session cache. { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r->ssl_info().handshake_type); } // Clear the socket pools and connect again. This should resume the previous // session. default_context() .http_transaction_factory() ->GetSession() ->CloseAllConnections(ERR_FAILED, /*net_log_reason_utf8=*/""); { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, r->ssl_info().handshake_type); } // Now fetch on a new URLRequestContext. This should not resume the session. auto context_builder = CreateTestURLRequestContextBuilder(); auto other_context = context_builder->Build(); { TestDelegate d; std::unique_ptr r(other_context->CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r->ssl_info().handshake_type); } } // Test that sessions started with privacy mode enabled cannot be resumed when // it is disabled, and vice versa. TEST_F(HTTPSRequestTest, NoSessionResumptionBetweenPrivacyModes) { // Start a server. EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); const auto url = test_server.GetURL("/"); auto ConnectAndCheckHandshake = [this, url](bool allow_credentials, auto expected_handshake) { // Construct request and indirectly set the privacy mode. TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_allow_credentials(allow_credentials); // Start the request and check the SSL handshake type. r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(expected_handshake, r->ssl_info().handshake_type); }; // Exhaustively check all pairs of privacy mode values. Note that we are using // allow_credentials to indirectly enable/disable privacy mode. const bool kAllowCredentialsValues[] = {false, true}; for (const auto allow_creds_1 : kAllowCredentialsValues) { for (const auto allow_creds_2 : kAllowCredentialsValues) { SCOPED_TRACE(base::StringPrintf("allow_creds_1=%d, allow_creds_2=%d", allow_creds_1, allow_creds_2)); // The session cache starts off empty, so we expect a full handshake. ConnectAndCheckHandshake(allow_creds_1, SSLInfo::HANDSHAKE_FULL); // The second handshake depends on whether we are using the same session // cache as the first request. ConnectAndCheckHandshake(allow_creds_2, allow_creds_1 == allow_creds_2 ? SSLInfo::HANDSHAKE_RESUME : SSLInfo::HANDSHAKE_FULL); // Flush both session caches. auto* network_session = default_context().http_transaction_factory()->GetSession(); network_session->ClearSSLSessionCache(); } } } class HTTPSFallbackTest : public TestWithTaskEnvironment { public: HTTPSFallbackTest() { auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_ssl_config_service( std::make_unique(SSLContextConfig())); context_ = context_builder->Build(); } ~HTTPSFallbackTest() override = default; protected: void DoFallbackTest(const SSLServerConfig& ssl_config) { DCHECK(!request_); delegate_.set_allow_certificate_errors(true); EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); test_server.SetSSLConfig(EmbeddedTestServer::CERT_OK, ssl_config); ASSERT_TRUE(test_server.Start()); request_ = context_->CreateRequest(test_server.GetURL("/"), DEFAULT_PRIORITY, &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); request_->Start(); delegate_.RunUntilComplete(); } void ExpectConnection(int version) { EXPECT_EQ(1, delegate_.response_started_count()); EXPECT_NE(0, delegate_.bytes_received()); EXPECT_EQ(version, SSLConnectionStatusToVersion( request_->ssl_info().connection_status)); } void ExpectFailure(int error) { EXPECT_EQ(1, delegate_.response_started_count()); EXPECT_EQ(error, delegate_.request_status()); } private: TestDelegate delegate_; std::unique_ptr context_; std::unique_ptr request_; }; // Tests the TLS 1.0 fallback doesn't happen. TEST_F(HTTPSFallbackTest, TLSv1NoFallback) { net::SSLServerConfig ssl_config; ssl_config.client_hello_callback_for_testing = base::BindRepeating([](const SSL_CLIENT_HELLO* client_hello) { // Reject ClientHellos with version >= TLS 1.1. return client_hello->version <= TLS1_VERSION; }); ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_config)); ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); } // Tests the TLS 1.1 fallback doesn't happen. TEST_F(HTTPSFallbackTest, TLSv1_1NoFallback) { net::SSLServerConfig ssl_config; ssl_config.client_hello_callback_for_testing = base::BindRepeating([](const SSL_CLIENT_HELLO* client_hello) { // Reject ClientHellos with version >= TLS 1.2. return client_hello->version <= TLS1_1_VERSION; }); ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_config)); ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); } // Tests the TLS 1.2 fallback doesn't happen. TEST_F(HTTPSFallbackTest, TLSv1_2NoFallback) { net::SSLServerConfig ssl_config; ssl_config.client_hello_callback_for_testing = base::BindRepeating([](const SSL_CLIENT_HELLO* client_hello) { // Reject ClientHellos with a supported_versions extension. TLS 1.3 is // signaled via an extension rather than the legacy version field. const uint8_t* data; size_t len; return !SSL_early_callback_ctx_extension_get( client_hello, TLSEXT_TYPE_supported_versions, &data, &len); }); ASSERT_NO_FATAL_FAILURE(DoFallbackTest(ssl_config)); ExpectFailure(ERR_SSL_VERSION_OR_CIPHER_MISMATCH); } class HTTPSSessionTest : public TestWithTaskEnvironment { public: HTTPSSessionTest() { auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier(std::make_unique()); default_context_ = context_builder->Build(); default_cert_verifier().set_default_result(OK); } ~HTTPSSessionTest() override = default; URLRequestContext& default_context() { return *default_context_; } MockCertVerifier& default_cert_verifier() { // This cast is safe because we provided a MockCertVerifier in the ctor. return *static_cast(default_context_->cert_verifier()); } private: std::unique_ptr default_context_; }; // Tests that session resumption is not attempted if an invalid certificate // is presented. TEST_F(HTTPSSessionTest, DontResumeSessionsForInvalidCertificates) { EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); const auto url = test_server.GetURL("/"); default_context() .http_transaction_factory() ->GetSession() ->ClearSSLSessionCache(); // Simulate the certificate being expired and attempt a connection. default_cert_verifier().set_default_result(ERR_CERT_DATE_INVALID); { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); } reinterpret_cast(default_context().http_transaction_factory()) ->CloseAllConnections(ERR_FAILED, "Very good reason"); // Now change the certificate to be acceptable (so that the response is // loaded), and ensure that no session id is presented to the peer. default_cert_verifier().set_default_result(OK); { TestDelegate d; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, r->ssl_info().handshake_type); } } // Interceptor to check that secure DNS has been disabled. Secure DNS should be // disabled for any network fetch triggered during certificate verification as // it could cause a deadlock. class SecureDnsInterceptor : public net::URLRequestInterceptor { public: SecureDnsInterceptor() = default; ~SecureDnsInterceptor() override = default; private: // URLRequestInterceptor implementation: std::unique_ptr MaybeInterceptRequest( net::URLRequest* request) const override { EXPECT_EQ(SecureDnsPolicy::kDisable, request->secure_dns_policy()); return nullptr; } }; class HTTPSCertNetFetchingTest : public HTTPSRequestTest { public: HTTPSCertNetFetchingTest() = default; void SetUp() override { auto context_builder = CreateTestURLRequestContextBuilder(); cert_net_fetcher_ = base::MakeRefCounted(); context_builder->SetCertVerifier( CertVerifier::CreateDefault(cert_net_fetcher_)); context_ = context_builder->Build(); net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( "http", "127.0.0.1", std::make_unique()); cert_net_fetcher_->SetURLRequestContext(context_.get()); context_->cert_verifier()->SetConfig(GetCertVerifierConfig()); } void TearDown() override { cert_net_fetcher_->Shutdown(); net::URLRequestFilter::GetInstance()->ClearHandlers(); } void DoConnectionWithDelegate( const EmbeddedTestServer::ServerCertificateConfig& cert_config, TestDelegate* delegate, SSLInfo* out_ssl_info) { // Always overwrite |out_ssl_info|. out_ssl_info->Reset(); EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); test_server.SetSSLConfig(cert_config); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); delegate->set_allow_certificate_errors(true); std::unique_ptr r( context_->CreateRequest(test_server.GetURL("/"), DEFAULT_PRIORITY, delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); delegate->RunUntilComplete(); EXPECT_EQ(1, delegate->response_started_count()); *out_ssl_info = r->ssl_info(); } void DoConnection( const EmbeddedTestServer::ServerCertificateConfig& cert_config, CertStatus* out_cert_status) { // Always overwrite |out_cert_status|. *out_cert_status = 0; TestDelegate d; SSLInfo ssl_info; ASSERT_NO_FATAL_FAILURE( DoConnectionWithDelegate(cert_config, &d, &ssl_info)); *out_cert_status = ssl_info.cert_status; } protected: // GetCertVerifierConfig() configures the URLRequestContext that will be used // for making connections to the testserver. This can be overridden in test // subclasses for different behaviour. virtual CertVerifier::Config GetCertVerifierConfig() { CertVerifier::Config config; return config; } scoped_refptr cert_net_fetcher_; std::unique_ptr cert_verifier_; std::unique_ptr context_; }; // The test EV policy OID used for generated certs. static const char kEVTestCertPolicy[] = "1.3.6.1.4.1.11129.2.4.1"; class HTTPSEVTest : public HTTPSCertNetFetchingTest { public: void SetUp() override { HTTPSCertNetFetchingTest::SetUp(); scoped_refptr root_cert = ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem"); ASSERT_TRUE(root_cert); ev_test_policy_ = std::make_unique( EVRootCAMetadata::GetInstance(), X509Certificate::CalculateFingerprint256(root_cert->cert_buffer()), kEVTestCertPolicy); } void TearDown() override { HTTPSCertNetFetchingTest::TearDown(); } private: std::unique_ptr ev_test_policy_; }; class HTTPSOCSPTest : public HTTPSCertNetFetchingTest { public: CertVerifier::Config GetCertVerifierConfig() override { CertVerifier::Config config; config.enable_rev_checking = true; return config; } }; static bool UsingBuiltinCertVerifier() { #if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) return true; #else #if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) if (base::FeatureList::IsEnabled(features::kCertVerifierBuiltinFeature)) return true; #endif #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) if (base::FeatureList::IsEnabled(features::kChromeRootStoreUsed)) return true; #endif return false; #endif } // SystemSupportsHardFailRevocationChecking returns true iff the current // operating system supports revocation checking and can distinguish between // situations where a given certificate lacks any revocation information (eg: // no CRLDistributionPoints and no OCSP Responder AuthorityInfoAccess) and when // revocation information cannot be obtained (eg: the CRL was unreachable). // If it does not, then tests which rely on 'hard fail' behaviour should be // skipped. static bool SystemSupportsHardFailRevocationChecking() { if (UsingBuiltinCertVerifier()) return true; #if BUILDFLAG(IS_WIN) return true; #else return false; #endif } // SystemUsesChromiumEVMetadata returns true iff the current operating system // uses Chromium's EV metadata (i.e. EVRootCAMetadata). If it does not, then // several tests are effected because our testing EV certificate won't be // recognised as EV. static bool SystemUsesChromiumEVMetadata() { if (UsingBuiltinCertVerifier()) return true; #if defined(PLATFORM_USES_CHROMIUM_EV_METADATA) return true; #else return false; #endif } static bool SystemSupportsOCSP() { #if BUILDFLAG(IS_ANDROID) // Unsupported, see http://crbug.com/117478. return false; #else return true; #endif } static bool SystemSupportsOCSPStapling() { if (UsingBuiltinCertVerifier()) return true; #if BUILDFLAG(IS_ANDROID) return false; #else return true; #endif } static bool SystemSupportsCRLSets() { if (UsingBuiltinCertVerifier()) return true; #if BUILDFLAG(IS_ANDROID) return false; #else return true; #endif } TEST_F(HTTPSEVTest, EVCheckNoOCSP) { if (!SystemUsesChromiumEVMetadata()) { LOG(WARNING) << "Skipping test because system doesn't support EV"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.policy_oids = {kEVTestCertPolicy}; CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_EQ(SystemUsesChromiumEVMetadata(), static_cast(cert_status & CERT_STATUS_IS_EV)); EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, Valid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, Revoked) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, Invalid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponse); CertStatus cert_status; DoConnection(cert_config, &cert_status); // Without a positive OCSP response, we shouldn't show the EV status, but also // should not show any revocation checking errors. EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, IntermediateValid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, IntermediateResponseOldButStillValid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); // Use an OCSP response for the intermediate that would be too old for a leaf // cert, but is still valid for an intermediate. cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); if (UsingBuiltinCertVerifier()) { EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); } else { #if BUILDFLAG(IS_WIN) // TODO(mattm): Seems to be flaky on Windows. Either returns // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION (which gets masked off due to // soft-fail), or CERT_STATUS_REVOKED. EXPECT_THAT(cert_status & CERT_STATUS_ALL_ERRORS, AnyOf(0u, CERT_STATUS_REVOKED)); #else EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); #endif } EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, IntermediateResponseTooOld) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLonger}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); if (UsingBuiltinCertVerifier()) { EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); } else { #if BUILDFLAG(IS_WIN) // TODO(mattm): Seems to be flaky on Windows. Either returns // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION (which gets masked off due to // soft-fail), or CERT_STATUS_REVOKED. EXPECT_THAT(cert_status & CERT_STATUS_ALL_ERRORS, AnyOf(0u, CERT_STATUS_REVOKED)); #else EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); #endif } EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, IntermediateRevoked) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); if (UsingBuiltinCertVerifier()) { EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); } else { #if BUILDFLAG(IS_WIN) // TODO(mattm): Seems to be flaky on Windows. Either returns // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION (which gets masked off due to // soft-fail), or CERT_STATUS_REVOKED. EXPECT_THAT(cert_status & CERT_STATUS_ALL_ERRORS, AnyOf(0u, CERT_STATUS_REVOKED)); #else EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); #endif } EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, ValidStapled) { if (!SystemSupportsOCSPStapling()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP stapling"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; // AIA OCSP url is included, but does not return a successful ocsp response. cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, RevokedStapled) { if (!SystemSupportsOCSPStapling()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP stapling"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; // AIA OCSP url is included, but does not return a successful ocsp response. cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, OldStapledAndInvalidAIA) { if (!SystemSupportsOCSPStapling()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP stapling"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; // Stapled response indicates good, but is too old. cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}); // AIA OCSP url is included, but does not return a successful ocsp response. cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSOCSPTest, OldStapledButValidAIA) { if (!SystemSupportsOCSPStapling()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP stapling"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; // Stapled response indicates good, but response is too old. cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}); // AIA OCSP url is included, and returns a successful ocsp response. cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } static const struct OCSPVerifyTestData { EmbeddedTestServer::OCSPConfig ocsp_config; OCSPVerifyResult::ResponseStatus expected_response_status; // |expected_cert_status| is only used if |expected_response_status| is // PROVIDED. OCSPRevocationStatus expected_cert_status; } kOCSPVerifyData[] = { // 0 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::GOOD}, // 1 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, // 2 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, // 3 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, // 4 {EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater), OCSPVerifyResult::ERROR_RESPONSE, OCSPRevocationStatus::UNKNOWN}, // 5 {EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponse), OCSPVerifyResult::PARSE_RESPONSE_ERROR, OCSPRevocationStatus::UNKNOWN}, // 6 {EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponseData), OCSPVerifyResult::PARSE_RESPONSE_DATA_ERROR, OCSPRevocationStatus::UNKNOWN}, // 7 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, // 8 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::UNKNOWN, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::UNKNOWN}, // 9 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::UNKNOWN, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, // 10 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::UNKNOWN, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, // 11 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kBeforeCert), OCSPVerifyResult::BAD_PRODUCED_AT, OCSPRevocationStatus::UNKNOWN}, // 12 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kAfterCert), OCSPVerifyResult::BAD_PRODUCED_AT, OCSPRevocationStatus::UNKNOWN}, // 13 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}, {OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::GOOD}, // 14 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}, {OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::GOOD}, // 15 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}, {OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::GOOD}, // 16 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly}, {OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}, {OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, // 17 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::UNKNOWN, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}, {OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}, {OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::REVOKED}, // 18 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::UNKNOWN, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}, {OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::UNKNOWN}, // 19 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::UNKNOWN, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}, {OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}, {OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::UNKNOWN}, // 20 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid, EmbeddedTestServer::OCSPConfig::SingleResponse::Serial::kMismatch}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::NO_MATCHING_RESPONSE, OCSPRevocationStatus::UNKNOWN}, // 21 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kEarly, EmbeddedTestServer::OCSPConfig::SingleResponse::Serial::kMismatch}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::NO_MATCHING_RESPONSE, OCSPRevocationStatus::UNKNOWN}, // 22 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::PROVIDED, OCSPRevocationStatus::REVOKED}, // 23 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, // 24 {EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}, EmbeddedTestServer::OCSPConfig::Produced::kValid), OCSPVerifyResult::INVALID_DATE, OCSPRevocationStatus::UNKNOWN}, }; class HTTPSOCSPVerifyTest : public HTTPSOCSPTest, public testing::WithParamInterface {}; TEST_P(HTTPSOCSPVerifyTest, VerifyResult) { OCSPVerifyTestData test = GetParam(); EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.stapled_ocsp_config = test.ocsp_config; SSLInfo ssl_info; OCSPErrorTestDelegate delegate; ASSERT_NO_FATAL_FAILURE( DoConnectionWithDelegate(cert_config, &delegate, &ssl_info)); // The SSLInfo must be extracted from |delegate| on error, due to how // URLRequest caches certificate errors. if (delegate.have_certificate_errors()) { ASSERT_TRUE(delegate.on_ssl_certificate_error_called()); ssl_info = delegate.ssl_info(); } EXPECT_EQ(test.expected_response_status, ssl_info.ocsp_result.response_status); if (test.expected_response_status == OCSPVerifyResult::PROVIDED) { EXPECT_EQ(test.expected_cert_status, ssl_info.ocsp_result.revocation_status); } } INSTANTIATE_TEST_SUITE_P(OCSPVerify, HTTPSOCSPVerifyTest, testing::ValuesIn(kOCSPVerifyData)); class HTTPSAIATest : public HTTPSCertNetFetchingTest {}; TEST_F(HTTPSAIATest, AIAFetching) { EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.intermediate = EmbeddedTestServer::IntermediateType::kByAIA; test_server.SetSSLConfig(cert_config); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); TestDelegate d; d.set_allow_certificate_errors(true); std::unique_ptr r(context_->CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); CertStatus cert_status = r->ssl_info().cert_status; EXPECT_EQ(OK, d.request_status()); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); ASSERT_TRUE(r->ssl_info().cert); EXPECT_EQ(2u, r->ssl_info().cert->intermediate_buffers().size()); ASSERT_TRUE(r->ssl_info().unverified_cert); EXPECT_EQ(0u, r->ssl_info().unverified_cert->intermediate_buffers().size()); } class HTTPSHardFailTest : public HTTPSOCSPTest { protected: CertVerifier::Config GetCertVerifierConfig() override { CertVerifier::Config config; config.require_rev_checking_local_anchors = true; return config; } }; TEST_F(HTTPSHardFailTest, Valid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSHardFailTest, Revoked) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSHardFailTest, FailsOnOCSPInvalid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponse); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(CERT_STATUS_UNABLE_TO_CHECK_REVOCATION, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSHardFailTest, IntermediateResponseOldButStillValid) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); // Use an OCSP response for the intermediate that would be too old for a leaf // cert, but is still valid for an intermediate. cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLong}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSHardFailTest, IntermediateResponseTooOld) { if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.intermediate = EmbeddedTestServer::IntermediateType::kInHandshake; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); // Use an OCSP response for the intermediate that is too old. cert_config.intermediate_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kLonger}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); if (UsingBuiltinCertVerifier()) { EXPECT_EQ(CERT_STATUS_UNABLE_TO_CHECK_REVOCATION, cert_status & CERT_STATUS_ALL_ERRORS); } else { // Platform verifier are more lenient. EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); } EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSHardFailTest, ValidStapled) { if (!SystemSupportsOCSPStapling()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP stapling"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; // AIA OCSP url is included, but does not return a successful ocsp response. cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSHardFailTest, RevokedStapled) { if (!SystemSupportsOCSPStapling()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP stapling"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; // AIA OCSP url is included, but does not return a successful ocsp response. cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSHardFailTest, OldStapledAndInvalidAIA) { if (!SystemSupportsOCSPStapling()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP stapling"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; // Stapled response indicates good, but is too old. cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}); // AIA OCSP url is included, but does not return a successful ocsp response. cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kTryLater); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(CERT_STATUS_UNABLE_TO_CHECK_REVOCATION, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSHardFailTest, OldStapledButValidAIA) { if (!SystemSupportsOCSPStapling()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP stapling"; return; } if (!SystemSupportsHardFailRevocationChecking()) { LOG(WARNING) << "Skipping test because system doesn't support hard fail " << "revocation checking"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; // Stapled response indicates good, but response is too old. cert_config.stapled_ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kOld}}); // AIA OCSP url is included, and returns a successful ocsp response. cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_TRUE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } class HTTPSCRLSetTest : public HTTPSCertNetFetchingTest {}; TEST_F(HTTPSCRLSetTest, ExpiredCRLSet) { EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( EmbeddedTestServer::OCSPConfig::ResponseType::kInvalidResponse); CertVerifier::Config cert_verifier_config = GetCertVerifierConfig(); cert_verifier_config.crl_set = CRLSet::ExpiredCRLSetForTesting(); context_->cert_verifier()->SetConfig(cert_verifier_config); CertStatus cert_status; DoConnection(cert_config, &cert_status); // If we're not trying EV verification then, even if the CRLSet has expired, // we don't fall back to online revocation checks. EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSCRLSetTest, ExpiredCRLSetAndRevoked) { // Test that when online revocation checking is disabled, and the leaf // certificate is not EV, that no revocation checking actually happens. if (!SystemSupportsOCSP()) { LOG(WARNING) << "Skipping test because system doesn't support OCSP"; return; } EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::REVOKED, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); CertVerifier::Config cert_verifier_config = GetCertVerifierConfig(); cert_verifier_config.crl_set = CRLSet::ExpiredCRLSetForTesting(); context_->cert_verifier()->SetConfig(cert_verifier_config); CertStatus cert_status; DoConnection(cert_config, &cert_status); EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSCRLSetTest, CRLSetRevoked) { if (!SystemSupportsCRLSets()) { LOG(WARNING) << "Skipping test because system doesn't support CRLSets"; return; } EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); test_server.SetSSLConfig(cert_config); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); CertVerifier::Config cert_verifier_config = GetCertVerifierConfig(); SHA256HashValue root_cert_spki_hash; ASSERT_TRUE(GetTestRootCertSPKIHash(&root_cert_spki_hash)); cert_verifier_config.crl_set = CRLSet::ForTesting(false, &root_cert_spki_hash, test_server.GetCertificate()->serial_number(), "", {}); context_->cert_verifier()->SetConfig(cert_verifier_config); TestDelegate d; d.set_allow_certificate_errors(true); std::unique_ptr r(context_->CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); CertStatus cert_status = r->ssl_info().cert_status; // If the certificate is recorded as revoked in the CRLSet, that should be // reflected without online revocation checking. EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } TEST_F(HTTPSCRLSetTest, CRLSetRevokedBySubject) { if (!SystemSupportsCRLSets()) { LOG(WARNING) << "Skipping test because system doesn't support CRLSets"; return; } EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS); EmbeddedTestServer::ServerCertificateConfig cert_config; cert_config.ocsp_config = EmbeddedTestServer::OCSPConfig( {{OCSPRevocationStatus::GOOD, EmbeddedTestServer::OCSPConfig::SingleResponse::Date::kValid}}); test_server.SetSSLConfig(cert_config); RegisterDefaultHandlers(&test_server); ASSERT_TRUE(test_server.Start()); std::string common_name = test_server.GetCertificate()->subject().common_name; { CertVerifier::Config cert_verifier_config = GetCertVerifierConfig(); cert_verifier_config.crl_set = CRLSet::ForTesting(false, nullptr, "", common_name, {}); ASSERT_TRUE(cert_verifier_config.crl_set); context_->cert_verifier()->SetConfig(cert_verifier_config); TestDelegate d; d.set_allow_certificate_errors(true); std::unique_ptr r(context_->CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); CertStatus cert_status = r->ssl_info().cert_status; // If the certificate is recorded as revoked in the CRLSet, that should be // reflected without online revocation checking. EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS); EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV); EXPECT_FALSE(cert_status & CERT_STATUS_REV_CHECKING_ENABLED); } HashValue spki_hash_value; ASSERT_TRUE(x509_util::CalculateSha256SpkiHash( test_server.GetCertificate()->cert_buffer(), &spki_hash_value)); std::string spki_hash(spki_hash_value.data(), spki_hash_value.data() + spki_hash_value.size()); { CertVerifier::Config cert_verifier_config = GetCertVerifierConfig(); cert_verifier_config.crl_set = CRLSet::ForTesting(false, nullptr, "", common_name, {spki_hash}); context_->cert_verifier()->SetConfig(cert_verifier_config); TestDelegate d; d.set_allow_certificate_errors(true); std::unique_ptr r(context_->CreateRequest( test_server.GetURL("/defaultresponse"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); CertStatus cert_status = r->ssl_info().cert_status; // When the correct SPKI hash is specified in // |acceptable_spki_hashes_for_cn|, the connection should succeed even // though the subject is listed in the CRLSet. EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS); } } using HTTPSLocalCRLSetTest = TestWithTaskEnvironment; // Use a real CertVerifier to attempt to connect to the TestServer, and ensure // that when a CRLSet is provided that marks a given SPKI (the TestServer's // root SPKI) as known for interception, that it's adequately flagged. TEST_F(HTTPSLocalCRLSetTest, KnownInterceptionBlocked) { auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier( CertVerifier::CreateDefault(/*cert_net_fetcher=*/nullptr)); auto context = context_builder->Build(); // Verify the connection succeeds without being flagged. EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); RegisterDefaultHandlers(&https_server); https_server.SetSSLConfig(EmbeddedTestServer::CERT_OK_BY_INTERMEDIATE); ASSERT_TRUE(https_server.Start()); { TestDelegate d; std::unique_ptr req( context->CreateRequest(https_server.GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.request_failed()); EXPECT_FALSE(d.have_certificate_errors()); EXPECT_FALSE(req->ssl_info().cert_status & CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); } // Configure a CRL that will mark |root_ca_cert| as a blocked interception // root. std::string crl_set_bytes; scoped_refptr crl_set; ASSERT_TRUE( base::ReadFileToString(GetTestCertsDirectory().AppendASCII( "crlset_blocked_interception_by_root.raw"), &crl_set_bytes)); ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set)); CertVerifier::Config config_with_crlset; config_with_crlset.crl_set = crl_set; context->cert_verifier()->SetConfig(config_with_crlset); // Verify the connection fails as being a known interception root. { TestDelegate d; d.set_allow_certificate_errors(true); std::unique_ptr req( context->CreateRequest(https_server.GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.request_failed()); if (SystemSupportsCRLSets()) { EXPECT_TRUE(d.have_certificate_errors()); EXPECT_FALSE(d.certificate_errors_are_fatal()); EXPECT_EQ(ERR_CERT_KNOWN_INTERCEPTION_BLOCKED, d.certificate_net_error()); EXPECT_TRUE(req->ssl_info().cert_status & CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); } else { EXPECT_FALSE(d.have_certificate_errors()); EXPECT_TRUE(req->ssl_info().cert_status & CERT_STATUS_KNOWN_INTERCEPTION_DETECTED); } } } TEST_F(HTTPSLocalCRLSetTest, InterceptionBlockedAllowOverrideOnHSTS) { constexpr char kHSTSHost[] = "include-subdomains-hsts-preloaded.test"; constexpr char kHSTSSubdomainWithKnownInterception[] = "www.include-subdomains-hsts-preloaded.test"; EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK_BY_INTERMEDIATE); https_server.ServeFilesFromSourceDirectory(base::FilePath(kTestFilePath)); ASSERT_TRUE(https_server.Start()); // Configure the CertVerifier to simulate: // - For the test server host, that the certificate is issued by an // unknown authority; this SHOULD NOT be a fatal error when signaled // to the delegate. // - For |kHSTSHost|, that the certificate is issued by an unknown // authority; this SHOULD be a fatal error. // Combined, these two states represent the baseline: non-fatal for non-HSTS // hosts, fatal for HSTS host. // - For |kHSTSSubdomainWithKnownInterception|, that the certificate is // issued by a known interception cert. This SHOULD be an error, but // SHOULD NOT be a fatal error auto cert_verifier = std::make_unique(); scoped_refptr cert = https_server.GetCertificate(); ASSERT_TRUE(cert); HashValue filler_hash; ASSERT_TRUE(filler_hash.FromString( "sha256/3333333333333333333333333333333333333333333=")); CertVerifyResult fake_result; fake_result.verified_cert = cert; fake_result.is_issued_by_known_root = false; // Configure for the test server's default host. CertVerifyResult test_result = fake_result; test_result.public_key_hashes.push_back(filler_hash); test_result.cert_status |= CERT_STATUS_AUTHORITY_INVALID; cert_verifier->AddResultForCertAndHost( cert.get(), https_server.host_port_pair().host(), test_result, ERR_CERT_AUTHORITY_INVALID); // Configure for kHSTSHost. CertVerifyResult sts_base_result = fake_result; sts_base_result.public_key_hashes.push_back(filler_hash); sts_base_result.cert_status |= CERT_STATUS_AUTHORITY_INVALID; cert_verifier->AddResultForCertAndHost(cert.get(), kHSTSHost, sts_base_result, ERR_CERT_AUTHORITY_INVALID); // Configure for kHSTSSubdomainWithKnownInterception CertVerifyResult sts_sub_result = fake_result; SHA256HashValue root_hash; ASSERT_TRUE(GetTestRootCertSPKIHash(&root_hash)); sts_sub_result.public_key_hashes.push_back(HashValue(root_hash)); sts_sub_result.cert_status |= CERT_STATUS_REVOKED | CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED; cert_verifier->AddResultForCertAndHost( cert.get(), kHSTSSubdomainWithKnownInterception, sts_sub_result, ERR_CERT_KNOWN_INTERCEPTION_BLOCKED); // Configure the initial context. auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCertVerifier(std::move(cert_verifier)); auto context = context_builder->Build(); // Enable preloaded HSTS for |kHSTSHost|. ASSERT_TRUE(context->transport_security_state()); TransportSecurityState& security_state = *context->transport_security_state(); security_state.EnableStaticPinsForTesting(); security_state.SetPinningListAlwaysTimelyForTesting(true); SetTransportSecurityStateSourceForTesting(&test_default::kHSTSSource); // Connect to the test server and see the certificate error flagged, but // not fatal. { TestDelegate d; std::unique_ptr req( context->CreateRequest(https_server.GetURL("/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_TRUE(d.request_failed()); EXPECT_TRUE(d.have_certificate_errors()); EXPECT_FALSE(d.certificate_errors_are_fatal()); EXPECT_FALSE(req->ssl_info().cert_status & CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); } // Connect to kHSTSHost and see the certificate errors are flagged, and are // fatal. { TestDelegate d; std::unique_ptr req(context->CreateRequest( https_server.GetURL(kHSTSHost, "/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_TRUE(d.request_failed()); EXPECT_TRUE(d.have_certificate_errors()); EXPECT_TRUE(d.certificate_errors_are_fatal()); EXPECT_FALSE(req->ssl_info().cert_status & CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); } // Verify the connection fails as being a known interception root. { TestDelegate d; d.set_allow_certificate_errors(true); std::unique_ptr req(context->CreateRequest( https_server.GetURL(kHSTSSubdomainWithKnownInterception, "/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.request_failed()); EXPECT_TRUE(d.have_certificate_errors()); EXPECT_FALSE(d.certificate_errors_are_fatal()); EXPECT_EQ(ERR_CERT_KNOWN_INTERCEPTION_BLOCKED, d.certificate_net_error()); EXPECT_TRUE(req->ssl_info().cert_status & CERT_STATUS_KNOWN_INTERCEPTION_BLOCKED); } } #endif // !BUILDFLAG(IS_IOS) TEST_F(URLRequestTest, NetworkAccessedSetOnHostResolutionFailure) { auto context_builder = CreateTestURLRequestContextBuilder(); auto host_resolver = std::make_unique(); host_resolver->rules()->AddSimulatedTimeoutFailure("*"); context_builder->set_host_resolver(std::move(host_resolver)); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_FALSE(req->response_info().network_accessed); req->Start(); d.RunUntilComplete(); EXPECT_TRUE(req->response_info().network_accessed); EXPECT_THAT(req->response_info().resolve_error_info.error, IsError(ERR_DNS_TIMED_OUT)); } // Test that URLRequest is canceled correctly. // See http://crbug.com/508900 TEST_F(URLRequestTest, URLRequestRedirectJobCancelRequest) { TestDelegate d; std::unique_ptr req(default_context().CreateRequest( GURL("http://not-a-real-domain/"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); std::unique_ptr job = std::make_unique( req.get(), GURL("http://this-should-never-be-navigated-to/"), RedirectUtil::ResponseCode::REDIRECT_307_TEMPORARY_REDIRECT, "Jumbo shrimp"); TestScopedURLInterceptor interceptor(req->url(), std::move(job)); req->Start(); req->Cancel(); base::RunLoop().RunUntilIdle(); EXPECT_EQ(ERR_ABORTED, d.request_status()); EXPECT_EQ(0, d.received_redirect_count()); } #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) #define MAYBE_HeadersCallbacks DISABLED_HeadersCallbacks #else #define MAYBE_HeadersCallbacks HeadersCallbacks #endif TEST_F(URLRequestTestHTTP, MAYBE_HeadersCallbacks) { ASSERT_TRUE(http_test_server()->Start()); GURL url(http_test_server()->GetURL("/cachetime")); TestDelegate delegate; HttpRequestHeaders extra_headers; extra_headers.SetHeader("X-Foo", "bar"); { HttpRawRequestHeaders raw_req_headers; scoped_refptr raw_resp_headers; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetExtraRequestHeaders(extra_headers); r->SetRequestHeadersCallback(base::BindRepeating( &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers))); r->SetResponseHeadersCallback(base::BindRepeating( [](scoped_refptr* left, scoped_refptr right) { *left = right; }, base::Unretained(&raw_resp_headers))); r->set_isolation_info(isolation_info1_); r->Start(); while (!delegate.response_started_count()) base::RunLoop().RunUntilIdle(); EXPECT_FALSE(raw_req_headers.headers().empty()); std::string value; EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value)); EXPECT_EQ("bar", value); EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value)); EXPECT_EQ("gzip, deflate", value); EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Connection", &value)); EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Host", &value)); EXPECT_EQ("GET /cachetime HTTP/1.1\r\n", raw_req_headers.request_line()); EXPECT_EQ(raw_resp_headers.get(), r->response_headers()); } { std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetExtraRequestHeaders(extra_headers); r->SetRequestHeadersCallback(base::BindRepeating([](HttpRawRequestHeaders) { FAIL() << "Callback should not be called unless request is sent"; })); r->SetResponseHeadersCallback( base::BindRepeating([](scoped_refptr) { FAIL() << "Callback should not be called unless request is sent"; })); r->set_isolation_info(isolation_info1_); r->Start(); delegate.RunUntilComplete(); EXPECT_TRUE(r->was_cached()); } } TEST_F(URLRequestTestHTTP, HeadersCallbacksWithRedirect) { ASSERT_TRUE(http_test_server()->Start()); HttpRawRequestHeaders raw_req_headers; scoped_refptr raw_resp_headers; TestDelegate delegate; HttpRequestHeaders extra_headers; extra_headers.SetHeader("X-Foo", "bar"); GURL url(http_test_server()->GetURL("/redirect-test.html")); std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetExtraRequestHeaders(extra_headers); r->SetRequestHeadersCallback(base::BindRepeating( &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers))); r->SetResponseHeadersCallback(base::BindRepeating( [](scoped_refptr* left, scoped_refptr right) { *left = right; }, base::Unretained(&raw_resp_headers))); r->Start(); delegate.RunUntilRedirect(); ASSERT_EQ(1, delegate.received_redirect_count()); std::string value; EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value)); EXPECT_EQ("bar", value); EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value)); EXPECT_EQ("gzip, deflate", value); EXPECT_EQ(1, delegate.received_redirect_count()); EXPECT_EQ("GET /redirect-test.html HTTP/1.1\r\n", raw_req_headers.request_line()); EXPECT_TRUE(raw_resp_headers->HasHeader("Location")); EXPECT_EQ(302, raw_resp_headers->response_code()); EXPECT_EQ("Redirect", raw_resp_headers->GetStatusText()); raw_req_headers = HttpRawRequestHeaders(); raw_resp_headers = nullptr; r->FollowDeferredRedirect(absl::nullopt /* removed_headers */, absl::nullopt /* modified_headers */); delegate.RunUntilComplete(); EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value)); EXPECT_EQ("bar", value); EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value)); EXPECT_EQ("gzip, deflate", value); EXPECT_EQ("GET /with-headers.html HTTP/1.1\r\n", raw_req_headers.request_line()); EXPECT_EQ(r->response_headers(), raw_resp_headers.get()); } TEST_F(URLRequestTest, HeadersCallbacksConnectFailed) { TestDelegate request_delegate; std::unique_ptr r(default_context().CreateRequest( GURL("http://127.0.0.1:9/"), DEFAULT_PRIORITY, &request_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetRequestHeadersCallback( base::BindRepeating([](net::HttpRawRequestHeaders) { FAIL() << "Callback should not be called unless request is sent"; })); r->SetResponseHeadersCallback( base::BindRepeating([](scoped_refptr) { FAIL() << "Callback should not be called unless request is sent"; })); r->Start(); request_delegate.RunUntilComplete(); EXPECT_FALSE(r->is_pending()); } TEST_F(URLRequestTestHTTP, HeadersCallbacksAuthRetry) { ASSERT_TRUE(http_test_server()->Start()); GURL url(http_test_server()->GetURL("/auth-basic")); TestDelegate delegate; delegate.set_credentials(AuthCredentials(kUser, kSecret)); HttpRequestHeaders extra_headers; extra_headers.SetHeader("X-Foo", "bar"); using ReqHeadersVector = std::vector>; ReqHeadersVector raw_req_headers; using RespHeadersVector = std::vector>; RespHeadersVector raw_resp_headers; auto req_headers_callback = base::BindRepeating( [](ReqHeadersVector* vec, HttpRawRequestHeaders headers) { vec->emplace_back( std::make_unique(std::move(headers))); }, &raw_req_headers); auto resp_headers_callback = base::BindRepeating( [](RespHeadersVector* vec, scoped_refptr headers) { vec->push_back(headers); }, &raw_resp_headers); std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->SetExtraRequestHeaders(extra_headers); r->SetRequestHeadersCallback(req_headers_callback); r->SetResponseHeadersCallback(resp_headers_callback); r->set_isolation_info(isolation_info1_); r->Start(); delegate.RunUntilComplete(); EXPECT_FALSE(r->is_pending()); ASSERT_EQ(raw_req_headers.size(), 2u); ASSERT_EQ(raw_resp_headers.size(), 2u); std::string value; EXPECT_FALSE(raw_req_headers[0]->FindHeaderForTest("Authorization", &value)); EXPECT_TRUE(raw_req_headers[0]->FindHeaderForTest("X-Foo", &value)); EXPECT_EQ("bar", value); EXPECT_TRUE(raw_req_headers[1]->FindHeaderForTest("Authorization", &value)); EXPECT_TRUE(raw_req_headers[1]->FindHeaderForTest("X-Foo", &value)); EXPECT_EQ("bar", value); EXPECT_EQ(raw_resp_headers[1], r->response_headers()); EXPECT_NE(raw_resp_headers[0], raw_resp_headers[1]); EXPECT_EQ(401, raw_resp_headers[0]->response_code()); EXPECT_EQ("Unauthorized", raw_resp_headers[0]->GetStatusText()); std::unique_ptr r2(default_context().CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r2->SetExtraRequestHeaders(extra_headers); r2->SetRequestHeadersCallback(req_headers_callback); r2->SetResponseHeadersCallback(resp_headers_callback); r2->SetLoadFlags(LOAD_VALIDATE_CACHE); r2->set_isolation_info(isolation_info1_); r2->Start(); delegate.RunUntilComplete(); EXPECT_FALSE(r2->is_pending()); ASSERT_EQ(raw_req_headers.size(), 3u); ASSERT_EQ(raw_resp_headers.size(), 3u); EXPECT_TRUE(raw_req_headers[2]->FindHeaderForTest("If-None-Match", &value)); EXPECT_NE(raw_resp_headers[2].get(), r2->response_headers()); EXPECT_EQ(304, raw_resp_headers[2]->response_code()); EXPECT_EQ("Not Modified", raw_resp_headers[2]->GetStatusText()); } TEST_F(URLRequestTest, UpgradeIfInsecureFlagSet) { TestDelegate d; const GURL kOriginalUrl("https://original.test"); const GURL kRedirectUrl("http://redirect.test"); auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS)); network_delegate.set_redirect_url(kRedirectUrl); auto context = context_builder->Build(); std::unique_ptr r(context->CreateRequest( kOriginalUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_upgrade_if_insecure(true); r->Start(); d.RunUntilRedirect(); GURL::Replacements replacements; // Check that the redirect URL was upgraded to HTTPS since upgrade_if_insecure // was set. replacements.SetSchemeStr("https"); EXPECT_EQ(kRedirectUrl.ReplaceComponents(replacements), d.redirect_info().new_url); EXPECT_TRUE(d.redirect_info().insecure_scheme_was_upgraded); } TEST_F(URLRequestTest, UpgradeIfInsecureFlagSetExplicitPort80) { TestDelegate d; const GURL kOriginalUrl("https://original.test"); const GURL kRedirectUrl("http://redirect.test:80"); auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS)); network_delegate.set_redirect_url(kRedirectUrl); auto context = context_builder->Build(); std::unique_ptr r(context->CreateRequest( kOriginalUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_upgrade_if_insecure(true); r->Start(); d.RunUntilRedirect(); GURL::Replacements replacements; // The URL host should have not been changed. EXPECT_EQ(d.redirect_info().new_url.host(), kRedirectUrl.host()); // The scheme should now be https, and the effective port should now be 443. EXPECT_TRUE(d.redirect_info().new_url.SchemeIs("https")); EXPECT_EQ(d.redirect_info().new_url.EffectiveIntPort(), 443); EXPECT_TRUE(d.redirect_info().insecure_scheme_was_upgraded); } TEST_F(URLRequestTest, UpgradeIfInsecureFlagSetNonStandardPort) { TestDelegate d; const GURL kOriginalUrl("https://original.test"); const GURL kRedirectUrl("http://redirect.test:1234"); auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS)); network_delegate.set_redirect_url(kRedirectUrl); auto context = context_builder->Build(); std::unique_ptr r(context->CreateRequest( kOriginalUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_upgrade_if_insecure(true); r->Start(); d.RunUntilRedirect(); GURL::Replacements replacements; // Check that the redirect URL was upgraded to HTTPS since upgrade_if_insecure // was set, nonstandard port should not have been modified. replacements.SetSchemeStr("https"); EXPECT_EQ(kRedirectUrl.ReplaceComponents(replacements), d.redirect_info().new_url); EXPECT_TRUE(d.redirect_info().insecure_scheme_was_upgraded); } TEST_F(URLRequestTest, UpgradeIfInsecureFlagNotSet) { TestDelegate d; const GURL kOriginalUrl("https://original.test"); const GURL kRedirectUrl("http://redirect.test"); auto context_builder = CreateTestURLRequestContextBuilder(); auto& network_delegate = *context_builder->set_network_delegate( std::make_unique( BlockingNetworkDelegate::SYNCHRONOUS)); network_delegate.set_redirect_url(kRedirectUrl); auto context = context_builder->Build(); std::unique_ptr r(context->CreateRequest( kOriginalUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_upgrade_if_insecure(false); r->Start(); d.RunUntilRedirect(); // The redirect URL should not be changed if the upgrade_if_insecure flag is // not set. EXPECT_EQ(kRedirectUrl, d.redirect_info().new_url); EXPECT_FALSE(d.redirect_info().insecure_scheme_was_upgraded); } // Test that URLRequests get properly tagged. #if BUILDFLAG(IS_ANDROID) TEST_F(URLRequestTestHTTP, TestTagging) { if (!CanGetTaggedBytes()) { DVLOG(0) << "Skipping test - GetTaggedBytes unsupported."; return; } ASSERT_TRUE(http_test_server()->Start()); // The tag under which the system reports untagged traffic. static const int32_t UNTAGGED_TAG = 0; uint64_t old_traffic = GetTaggedBytes(UNTAGGED_TAG); // Untagged traffic should be tagged with tag UNTAGGED_TAG. TestDelegate delegate; std::unique_ptr req(default_context().CreateRequest( http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_EQ(SocketTag(), req->socket_tag()); req->Start(); delegate.RunUntilComplete(); EXPECT_GT(GetTaggedBytes(UNTAGGED_TAG), old_traffic); int32_t tag_val1 = 0x12345678; SocketTag tag1(SocketTag::UNSET_UID, tag_val1); old_traffic = GetTaggedBytes(tag_val1); // Test specific tag value. req = default_context().CreateRequest(http_test_server()->GetURL("/"), DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); req->set_socket_tag(tag1); EXPECT_EQ(tag1, req->socket_tag()); req->Start(); delegate.RunUntilComplete(); EXPECT_GT(GetTaggedBytes(tag_val1), old_traffic); } #endif namespace { class ReadBufferingListener : public test_server::EmbeddedTestServerConnectionListener { public: ReadBufferingListener() = default; ~ReadBufferingListener() override = default; void BufferNextConnection(int buffer_size) { buffer_size_ = buffer_size; } std::unique_ptr AcceptedSocket( std::unique_ptr socket) override { if (!buffer_size_) { return socket; } auto wrapped = std::make_unique(std::move(socket)); wrapped->BufferNextRead(buffer_size_); // Do not buffer subsequent connections, which may be a 0-RTT retry. buffer_size_ = 0; return wrapped; } void ReadFromSocket(const StreamSocket& socket, int rv) override {} private: int buffer_size_ = 0; }; // Provides a response to the 0RTT request indicating whether it was received // as early data, sending HTTP_TOO_EARLY if enabled. class ZeroRTTResponse : public test_server::BasicHttpResponse { public: ZeroRTTResponse(bool zero_rtt, bool send_too_early) : zero_rtt_(zero_rtt), send_too_early_(send_too_early) {} ZeroRTTResponse(const ZeroRTTResponse&) = delete; ZeroRTTResponse& operator=(const ZeroRTTResponse&) = delete; ~ZeroRTTResponse() override = default; void SendResponse( base::WeakPtr delegate) override { AddCustomHeader("Vary", "Early-Data"); set_content_type("text/plain"); AddCustomHeader("Cache-Control", "no-cache"); if (zero_rtt_) { if (send_too_early_) set_code(HTTP_TOO_EARLY); set_content("1"); } else { set_content("0"); } // Since the EmbeddedTestServer doesn't keep the socket open by default, // it is explicitly kept alive to allow the remaining leg of the 0RTT // handshake to be received after the early data. delegate->SendResponseHeaders(code(), GetHttpReasonPhrase(code()), BuildHeaders()); delegate->SendContents(content(), base::DoNothing()); } private: bool zero_rtt_; bool send_too_early_; }; std::unique_ptr HandleZeroRTTRequest( const test_server::HttpRequest& request) { DCHECK(request.ssl_info); if (request.GetURL().path() != "/zerortt") return nullptr; return std::make_unique( request.ssl_info->early_data_received, false); } } // namespace class HTTPSEarlyDataTest : public TestWithTaskEnvironment { public: HTTPSEarlyDataTest() : test_server_(net::EmbeddedTestServer::TYPE_HTTPS) { HttpNetworkSessionParams params; params.enable_early_data = true; auto cert_verifier = std::make_unique(); cert_verifier->set_default_result(OK); SSLContextConfig config; config.version_max = SSL_PROTOCOL_VERSION_TLS1_3; auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_http_network_session_params(params); context_builder->SetCertVerifier(std::move(cert_verifier)); context_builder->set_ssl_config_service( std::make_unique(config)); context_ = context_builder->Build(); test_server_.SetSSLConfig( net::EmbeddedTestServer::CERT_OK, CreateSSLServerConfig(SSL_PROTOCOL_VERSION_TLS1_3)); RegisterDefaultHandlers(&test_server_); test_server_.RegisterRequestHandler( base::BindRepeating(&HandleZeroRTTRequest)); test_server_.SetConnectionListener(&listener_); } ~HTTPSEarlyDataTest() override = default; URLRequestContext& context() { return *context_; } static SSLServerConfig CreateSSLServerConfig(uint16_t version) { SSLServerConfig ssl_config; ssl_config.version_max = version; ssl_config.early_data_enabled = true; return ssl_config; } void ResetSSLConfig(net::EmbeddedTestServer::ServerCertificate cert, uint16_t version) { SSLServerConfig ssl_config = CreateSSLServerConfig(version); test_server_.ResetSSLConfig(cert, ssl_config); } protected: std::unique_ptr context_; ReadBufferingListener listener_; EmbeddedTestServer test_server_; }; // TLSEarlyDataTest tests that we handle early data correctly. TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTest) { ASSERT_TRUE(test_server_.Start()); context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); // kParamSize must be larger than any ClientHello sent by the client, but // smaller than the maximum amount of early data allowed by the server. const int kParamSize = 4 * 1024; const GURL kUrl = test_server_.GetURL("/zerortt?" + std::string(kParamSize, 'a')); { TestDelegate d; std::unique_ptr r(context().CreateRequest( kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); base::RunLoop().Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the initial request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); } context().http_transaction_factory()->GetSession()->CloseAllConnections( ERR_FAILED, "Very good reason"); // 0-RTT inherently involves a race condition: if the server responds with the // ServerHello before the client sends the HTTP request (the client may be // busy verifying a certificate), the client will send data over 1-RTT keys // rather than 0-RTT. // // This test ensures 0-RTT is sent if relevant by making the test server wait // for both the ClientHello and 0-RTT HTTP request before responding. We use // a ReadBufferingStreamSocket and enable buffering for the 0-RTT request. The // buffer size must be larger than the ClientHello but smaller than the // ClientHello combined with the HTTP request. listener_.BufferNextConnection(kParamSize); { TestDelegate d; std::unique_ptr r(context().CreateRequest( kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); base::RunLoop().Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be a single '1' in the resumed request, and // the handler should return "1". EXPECT_EQ("1", d.data_received()); } } // TLSEarlyDataTest tests that we handle early data correctly for POST. TEST_F(HTTPSEarlyDataTest, TLSEarlyDataPOSTTest) { ASSERT_TRUE(test_server_.Start()); context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); { TestDelegate d; std::unique_ptr r(context().CreateRequest( test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); base::RunLoop().Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the initial request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); } context().http_transaction_factory()->GetSession()->CloseAllConnections( ERR_FAILED, "Very good reason"); { TestDelegate d; std::unique_ptr r(context().CreateRequest( test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_method("POST"); r->Start(); EXPECT_TRUE(r->is_pending()); base::RunLoop().Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the request, since we don't // send POSTs over early data, and the handler should return "0". EXPECT_EQ("0", d.data_received()); } } // TLSEarlyDataTest tests that the 0-RTT is enabled for idempotent POST request. TEST_F(HTTPSEarlyDataTest, TLSEarlyDataIdempotentPOSTTest) { ASSERT_TRUE(test_server_.Start()); context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); const int kParamSize = 4 * 1024; const GURL kUrl = test_server_.GetURL("/zerortt?" + std::string(kParamSize, 'a')); { TestDelegate d; std::unique_ptr r(context().CreateRequest( kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); base::RunLoop().Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the initial request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); } context().http_transaction_factory()->GetSession()->CloseAllConnections( ERR_FAILED, "Very good reason"); listener_.BufferNextConnection(kParamSize); { TestDelegate d; std::unique_ptr r(context().CreateRequest( kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->set_method("POST"); r->SetIdempotency(net::IDEMPOTENT); r->Start(); EXPECT_TRUE(r->is_pending()); base::RunLoop().Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be set since the request is set as an // idempotent POST request. EXPECT_EQ("1", d.data_received()); } } // TLSEarlyDataTest tests that the 0-RTT is disabled for non-idempotent request. TEST_F(HTTPSEarlyDataTest, TLSEarlyDataNonIdempotentRequestTest) { ASSERT_TRUE(test_server_.Start()); context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); { TestDelegate d; std::unique_ptr r(context().CreateRequest( test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); base::RunLoop().Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the initial request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); } context().http_transaction_factory()->GetSession()->CloseAllConnections( ERR_FAILED, "Very good reason"); { TestDelegate d; std::unique_ptr r(context().CreateRequest( test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); // Sets the GET request as not idempotent. r->SetIdempotency(net::NOT_IDEMPOTENT); r->Start(); EXPECT_TRUE(r->is_pending()); base::RunLoop().Run(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the initial request even // though it is a GET request, since the request is set as not idempotent. EXPECT_EQ("0", d.data_received()); } } std::unique_ptr HandleTooEarly( bool* sent_425, const test_server::HttpRequest& request) { DCHECK(request.ssl_info); if (request.GetURL().path() != "/tooearly") return nullptr; if (request.ssl_info->early_data_received) *sent_425 = true; return std::make_unique( request.ssl_info->early_data_received, true); } // Test that we handle 425 (Too Early) correctly. TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTooEarlyTest) { bool sent_425 = false; test_server_.RegisterRequestHandler( base::BindRepeating(&HandleTooEarly, base::Unretained(&sent_425))); ASSERT_TRUE(test_server_.Start()); context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); // kParamSize must be larger than any ClientHello sent by the client, but // smaller than the maximum amount of early data allowed by the server. const int kParamSize = 4 * 1024; const GURL kUrl = test_server_.GetURL("/tooearly?" + std::string(kParamSize, 'a')); { TestDelegate d; std::unique_ptr r(context().CreateRequest( kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the initial request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); EXPECT_FALSE(sent_425); } context().http_transaction_factory()->GetSession()->CloseAllConnections( ERR_FAILED, "Very good reason"); // 0-RTT inherently involves a race condition: if the server responds with the // ServerHello before the client sends the HTTP request (the client may be // busy verifying a certificate), the client will send data over 1-RTT keys // rather than 0-RTT. // // This test ensures 0-RTT is sent if relevant by making the test server wait // for both the ClientHello and 0-RTT HTTP request before responding. We use // a ReadBufferingStreamSocket and enable buffering for the 0-RTT request. The // buffer size must be larger than the ClientHello but smaller than the // ClientHello combined with the HTTP request. // // We must buffer exactly one connection because the HTTP 425 response will // trigger a retry, potentially on a new connection. listener_.BufferNextConnection(kParamSize); { TestDelegate d; std::unique_ptr r(context().CreateRequest( kUrl, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The resumption request will encounter a 425 error and retry without early // data, and the handler should return "0". EXPECT_EQ("0", d.data_received()); EXPECT_TRUE(sent_425); } } // TLSEarlyDataRejectTest tests that we gracefully handle an early data reject // and retry without early data. TEST_F(HTTPSEarlyDataTest, TLSEarlyDataRejectTest) { ASSERT_TRUE(test_server_.Start()); context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); { TestDelegate d; std::unique_ptr r(context().CreateRequest( test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the initial request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); } context().http_transaction_factory()->GetSession()->CloseAllConnections( ERR_FAILED, "Very good reason"); // The certificate in the resumption is changed to confirm that the // certificate change is observed. scoped_refptr old_cert = test_server_.GetCertificate(); ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED, SSL_PROTOCOL_VERSION_TLS1_3); { TestDelegate d; std::unique_ptr r(context().CreateRequest( test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); EXPECT_FALSE(old_cert->EqualsIncludingChain(r->ssl_info().cert.get())); // The Early-Data header should be omitted in the rejected request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); } } // TLSEarlyDataTLS12RejectTest tests that we gracefully handle an early data // reject from a TLS 1.2 server and retry without early data. TEST_F(HTTPSEarlyDataTest, TLSEarlyDataTLS12RejectTest) { ASSERT_TRUE(test_server_.Start()); context().http_transaction_factory()->GetSession()->ClearSSLSessionCache(); { TestDelegate d; std::unique_ptr r(context().CreateRequest( test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_3, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); // The Early-Data header should be omitted in the initial request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); } context().http_transaction_factory()->GetSession()->CloseAllConnections( ERR_FAILED, "Very good reason"); // The certificate in the resumption is changed to confirm that the // certificate change is observed. scoped_refptr old_cert = test_server_.GetCertificate(); ResetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED, SSL_PROTOCOL_VERSION_TLS1_2); { TestDelegate d; std::unique_ptr r(context().CreateRequest( test_server_.GetURL("/zerortt"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); EXPECT_TRUE(r->is_pending()); d.RunUntilComplete(); EXPECT_EQ(1, d.response_started_count()); EXPECT_EQ(SSL_CONNECTION_VERSION_TLS1_2, SSLConnectionStatusToVersion(r->ssl_info().connection_status)); EXPECT_TRUE(r->ssl_info().unverified_cert.get()); EXPECT_TRUE(test_server_.GetCertificate()->EqualsIncludingChain( r->ssl_info().cert.get())); EXPECT_FALSE(old_cert->EqualsIncludingChain(r->ssl_info().cert.get())); // The Early-Data header should be omitted in the rejected request, and the // handler should return "0". EXPECT_EQ("0", d.data_received()); } } // Tests that AuthChallengeInfo is available on the request. TEST_F(URLRequestTestHTTP, AuthChallengeInfo) { ASSERT_TRUE(http_test_server()->Start()); GURL url(http_test_server()->GetURL("/auth-basic")); TestDelegate delegate; std::unique_ptr r(default_context().CreateRequest( url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); r->Start(); delegate.RunUntilComplete(); ASSERT_TRUE(r->auth_challenge_info().has_value()); EXPECT_FALSE(r->auth_challenge_info()->is_proxy); EXPECT_EQ(url::SchemeHostPort(url), r->auth_challenge_info()->challenger); EXPECT_EQ("basic", r->auth_challenge_info()->scheme); EXPECT_EQ("testrealm", r->auth_challenge_info()->realm); EXPECT_EQ("Basic realm=\"testrealm\"", r->auth_challenge_info()->challenge); EXPECT_EQ("/auth-basic", r->auth_challenge_info()->path); } class URLRequestDnsAliasTest : public TestWithTaskEnvironment { protected: URLRequestDnsAliasTest() { auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->set_host_resolver(std::make_unique()); context_ = context_builder->Build(); } URLRequestContext& context() { return *context_; } MockHostResolver& host_resolver() { // This cast is safe because we provided a MockHostResolver in the ctor. return *static_cast(context_->host_resolver()); } void SetUp() override { ASSERT_TRUE(test_server_.Start()); } std::unique_ptr context_; TestDelegate test_delegate_; EmbeddedTestServer test_server_; }; TEST_F(URLRequestDnsAliasTest, WithDnsAliases) { GURL url(test_server_.GetURL("www.example.test", "/echo")); std::vector aliases({"alias1", "alias2", "host"}); host_resolver().rules()->AddIPLiteralRuleWithDnsAliases( "www.example.test", "127.0.0.1", std::move(aliases)); std::unique_ptr request(context().CreateRequest( url, DEFAULT_PRIORITY, &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); test_delegate_.RunUntilComplete(); EXPECT_THAT(test_delegate_.request_status(), IsOk()); EXPECT_THAT(request->response_info().dns_aliases, testing::ElementsAre("alias1", "alias2", "host")); } TEST_F(URLRequestDnsAliasTest, NoAdditionalDnsAliases) { GURL url(test_server_.GetURL("www.example.test", "/echo")); host_resolver().rules()->AddIPLiteralRuleWithDnsAliases( "www.example.test", "127.0.0.1", /*dns_aliases=*/std::set()); std::unique_ptr request(context().CreateRequest( url, DEFAULT_PRIORITY, &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS)); request->Start(); test_delegate_.RunUntilComplete(); EXPECT_THAT(test_delegate_.request_status(), IsOk()); EXPECT_THAT(request->response_info().dns_aliases, testing::ElementsAre("www.example.test")); } TEST_F(URLRequestTest, SetURLChain) { TestDelegate d; { GURL original_url("http://localhost"); std::unique_ptr r(default_context().CreateRequest( original_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_EQ(r->url_chain().size(), 1u); EXPECT_EQ(r->url_chain()[0], original_url); const std::vector url_chain = { GURL("http://foo.test"), GURL("http://bar.test"), GURL("http://baz.test"), }; r->SetURLChain(url_chain); EXPECT_EQ(r->url_chain().size(), 3u); EXPECT_EQ(r->url_chain()[0], url_chain[0]); EXPECT_EQ(r->url_chain()[1], url_chain[1]); EXPECT_EQ(r->url_chain()[2], original_url); } } class URLRequestMaybeAsyncFirstPartySetsTest : public URLRequestTest, public testing::WithParamInterface { public: URLRequestMaybeAsyncFirstPartySetsTest() { CHECK(test_server_.Start()); } std::unique_ptr CreateCookieStore() { auto cookie_monster = std::make_unique(/*store=*/nullptr, /*net_log=*/nullptr, /*first_party_sets_enabled=*/true); auto cookie_access_delegate = std::make_unique(); cookie_access_delegate->set_invoke_callbacks_asynchronously( invoke_callbacks_asynchronously()); cookie_monster->SetCookieAccessDelegate(std::move(cookie_access_delegate)); return cookie_monster; } bool invoke_callbacks_asynchronously() { return GetParam(); } HttpTestServer& test_server() { return test_server_; } private: HttpTestServer test_server_; }; TEST_P(URLRequestMaybeAsyncFirstPartySetsTest, SimpleRequest) { const std::string kHost = "example.test"; const url::Origin kOrigin = url::Origin::Create(test_server().GetURL(kHost, "/")); const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCookieStore(CreateCookieStore()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( test_server().GetURL(kHost, "/echo"), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info( IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(d.data_received(), "Echo"); EXPECT_THAT(d.request_status(), IsOk()); EXPECT_EQ(req->GetResponseCode(), 200); } TEST_P(URLRequestMaybeAsyncFirstPartySetsTest, SingleRedirect) { const std::string kHost = "example.test"; const url::Origin kOrigin = url::Origin::Create(test_server().GetURL(kHost, "/")); const SiteForCookies kSiteForCookies = SiteForCookies::FromOrigin(kOrigin); auto context_builder = CreateTestURLRequestContextBuilder(); context_builder->SetCookieStore(CreateCookieStore()); auto context = context_builder->Build(); TestDelegate d; std::unique_ptr req(context->CreateRequest( test_server().GetURL(kHost, base::StrCat({ "/server-redirect?", test_server().GetURL(kHost, "/echo").spec(), })), DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); req->set_isolation_info( IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, kOrigin, kSiteForCookies, {} /* party_context */)); req->Start(); d.RunUntilComplete(); EXPECT_EQ(d.data_received(), "Echo"); EXPECT_THAT(d.request_status(), IsOk()); EXPECT_EQ(req->GetResponseCode(), 200); } INSTANTIATE_TEST_SUITE_P(, URLRequestMaybeAsyncFirstPartySetsTest, testing::Bool()); } // namespace net