| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/url_request/url_request.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <iterator> |
| #include <limits> |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| |
| #include "base/base64url.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/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/path_service.h" |
| #include "base/ranges/algorithm.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/test/test_future.h" |
| #include "base/test/values_test_util.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "build/build_config.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_chain.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/caching_cert_verifier.h" |
| #include "net/cert/cert_net_fetcher.h" |
| #include "net/cert/cert_verifier.h" |
| #include "net/cert/coalescing_cert_verifier.h" |
| #include "net/cert/crl_set.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_setting_override.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/mock_host_resolver.h" |
| #include "net/dns/public/host_resolver_results.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_connection_info.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.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_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/boringssl/src/include/openssl/ssl.h" |
| #include "url/url_constants.h" |
| #include "url/url_util.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include <objbase.h> |
| #include <shlobj.h> |
| #include <windows.h> |
| #include <wrl/client.h> |
| |
| #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 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<RequestPriority> 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 = |
| base::ranges::search(haystack, base::StringPiece(needle), |
| base::CaseInsensitiveCompareASCII<char>()); |
| return it != haystack.end(); |
| } |
| |
| std::unique_ptr<UploadDataStream> CreateSimpleUploadData(const char* data) { |
| auto reader = std::make_unique<UploadBytesElementReader>(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<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| std::optional<GURL>* 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<BlockingNetworkDelegate> 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::SingleThreadTaskRunner::GetCurrentDefault()->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, std::nullopt); |
| }, |
| std::move(callback))); |
| } |
| |
| int BlockingNetworkDelegate::OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| std::optional<GURL>* 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::SingleThreadTaskRunner::GetCurrentDefault()->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::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::OnBlocked, |
| weak_factory_.GetWeakPtr())); |
| return ERR_IO_PENDING; |
| } |
| NOTREACHED(); |
| return 0; |
| } |
| |
| // 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<X509Certificate> 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<TestNetworkDelegate>()); |
| 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<TestNetworkDelegate*>( |
| 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_TRUE( |
| base::WriteFile(*test_file, base::StringPiece(data, data_size))); |
| } |
| |
| static std::unique_ptr<ConfiguredProxyResolutionService> |
| CreateFixedProxyResolutionService(const std::string& proxy) { |
| return ConfiguredProxyResolutionService::CreateFixedForTest( |
| proxy, TRAFFIC_ANNOTATION_FOR_TESTS); |
| } |
| |
| std::unique_ptr<URLRequest> 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<URLRequestContext> default_context_; |
| base::ScopedTempDir temp_dir_; |
| }; |
| |
| TEST_F(URLRequestTest, AboutBlankTest) { |
| TestDelegate d; |
| { |
| std::unique_ptr<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<int>(ReferrerPolicy::NEVER_CLEAR), 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsCrossOriginReferrerHistogram) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> 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<int>( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), |
| 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsReferrerHistogramAgainOnRedirect) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto network_delegate = std::make_unique<BlockingNetworkDelegate>( |
| 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<URLRequest> 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<int>( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), |
| 1); |
| req->FollowDeferredRedirect(/*removed_headers=*/std::nullopt, |
| /*modified_headers=*/std::nullopt); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerPolicyForRequest.CrossOrigin", |
| static_cast<int>( |
| ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE), |
| 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsReferrrerWithInformativePath) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto network_delegate = std::make_unique<BlockingNetworkDelegate>( |
| 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<URLRequest> 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=*/std::nullopt, |
| /*modified_headers=*/std::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>( |
| 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<URLRequest> 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=*/std::nullopt, |
| /*modified_headers=*/std::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>( |
| 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<URLRequest> 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=*/std::nullopt, |
| /*modified_headers=*/std::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<URLRequestJob> MaybeInterceptRequest( |
| URLRequest* request) const override { |
| std::unique_ptr<URLRequestTestJob> job = |
| std::make_unique<URLRequestTestJob>(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<URLRequestInterceptorWithLoadTimingInfo> interceptor = |
| std::make_unique<URLRequestInterceptorWithLoadTimingInfo>(); |
| 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<URLRequestInterceptorWithLoadTimingInfo, DanglingUntriaged> |
| 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.domain_lookup_start = now + base::Days(3); |
| connect_timing.domain_lookup_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<URLRequest> 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.domain_lookup_start, |
| load_timing_result.connect_timing.domain_lookup_start); |
| EXPECT_EQ(job_load_timing.connect_timing.domain_lookup_end, |
| load_timing_result.connect_timing.domain_lookup_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.domain_lookup_start, |
| load_timing_result.connect_timing.domain_lookup_start); |
| EXPECT_EQ(job_load_timing.connect_timing.domain_lookup_end, |
| load_timing_result.connect_timing.domain_lookup_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.domain_lookup_start = now - base::Days(4); |
| job_load_timing.connect_timing.domain_lookup_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.domain_lookup_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.domain_lookup_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<TestNetworkDelegate>()); |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| host_resolver->rules()->AddSimulatedTimeoutFailure("*"); |
| context_builder->set_host_resolver(std::move(host_resolver)); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> 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 chain should be set before failure. |
| EXPECT_EQ(PacResultElementToProxyChain("PROXY myproxy:70"), |
| req->proxy_chain()); |
| 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) { |
| 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<MockHostResolver>(); |
| 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<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> 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) { |
| 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<MockHostResolver>(); |
| 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<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> 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=*/std::nullopt, |
| /*modified_headers=*/std::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) { |
| 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<MockHostResolver>(); |
| 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<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> 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<TestWebSocketHandshakeStreamCreateHelper>(); |
| 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)); |
| } |
| |
| // Test that same-site requests with "wss" scheme retain the |
| // `kStorageAccessGrantEligible` override, even if the initiator origin uses the |
| // HTTPS version of the site. |
| TEST_F(URLRequestTest, WssRequestsAreEligibleForStorageAccess) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.SetSSLConfig(EmbeddedTestServer::CERT_TEST_NAMES); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(https_server.Start()); |
| |
| const GURL https_url = https_server.GetURL("a.test", "/defaultresponse"); |
| GURL::Replacements replacements; |
| replacements.SetSchemeStr(url::kWssScheme); |
| |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context->CreateRequest(https_url.ReplaceComponents(replacements), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS, |
| /*is_for_websockets=*/true)); |
| |
| req->SetExtraRequestHeaders(WebSocketCommonTestHeaders()); |
| |
| auto websocket_stream_create_helper = |
| std::make_unique<TestWebSocketHandshakeStreamCreateHelper>(); |
| req->SetUserData(kWebSocketHandshakeUserDataKey, |
| std::move(websocket_stream_create_helper)); |
| |
| req->set_has_storage_access(true); |
| req->set_initiator(url::Origin::Create(https_url)); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| // Expect failure because test server is not set up to provide websocket |
| // responses. |
| ASSERT_EQ(network_delegate.error_count(), 1); |
| ASSERT_EQ(network_delegate.last_error(), ERR_INVALID_RESPONSE); |
| |
| CookieSettingOverrides expected_overides; |
| expected_overides.Put(CookieSettingOverride::kStorageAccessGrantEligible); |
| |
| EXPECT_THAT(network_delegate.cookie_setting_overrides_records(), |
| testing::ElementsAre(expected_overides, expected_overides)); |
| } |
| #endif // BUILDFLAG(ENABLE_WEBSOCKETS) |
| |
| TEST_F(URLRequestTest, DnsHttpsRecordAbsentNoSchemeUpgrade) { |
| 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<MockHostResolver>(); |
| 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<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> 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<MockHostResolver>(); |
| 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<URLRequest> req( |
| context->CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(SecureDnsPolicy::kAllow, |
| static_cast<MockHostResolver*>(context->host_resolver()) |
| ->last_secure_dns_policy()); |
| } |
| |
| TEST_F(URLRequestTest, SkipSecureDnsEnabled) { |
| auto context_builder = CreateTestURLRequestContextBuilder(); |
| auto host_resolver = std::make_unique<MockHostResolver>(); |
| 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<URLRequest> 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<MockHostResolver*>(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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequestJob> job = |
| std::make_unique<PriorityMonitoringURLRequestJob>(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<URLRequest> req(default_context().CreateRequest( |
| GURL("http://test_intercept/foo"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| RequestPriority job_priority; |
| std::unique_ptr<URLRequestJob> job = |
| std::make_unique<PriorityMonitoringURLRequestJob>(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<URLRequest> 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<URLRequestJob> job = |
| std::make_unique<PriorityMonitoringURLRequestJob>(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()); |
| expected_transport.negotiated_protocol = kProtoUnknown; |
| 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()); |
| expected_transport.negotiated_protocol = kProtoUnknown; |
| 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()); |
| expected_transport.negotiated_protocol = kProtoUnknown; |
| 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<URLRequest> 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<URLRequest> 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<DelayedCookieMonster>()); |
| auto& network_delegate = *context_builder->set_network_delegate( |
| std::make_unique<TestNetworkDelegate>()); |
| auto context = context_builder->Build(); |
| |
| // Set up a cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> 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<URLRequest> 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(), |
| std::nullopt /* server_time */, std::nullopt /* cookie_partition_key */); |
| auto delayed_cm = std::make_unique<DelayedCookieMonster>(); |
| delayed_cm->SetCanonicalCookieAsync(std::move(cookie1), url, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| |
| auto cookie2 = CanonicalCookie::Create( |
| url, "AlreadySetCookie=1;Secure", base::Time::Now(), |
| std::nullopt /* server_time */, std::nullopt /* cookie_partition_key */); |
| auto cm = std::make_unique<CookieMonster>(nullptr, nullptr); |
| 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<FilteringTestNetworkDelegate>()); |
| 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<FilteringTestNetworkDelegate>()); |
| auto sync_context = sync_context_builder->Build(); |
| sync_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie"); |
| TestDelegate sync_delegate; |
| |
| std::vector<std::string> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<bool> { |
| 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<URLRequest> 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<URLRequest> 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)); |
| 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<URLRequest> 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<URLRequest> 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<URLRequest> 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<URLRequest> 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)); |
| 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<URLRequest> 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)); |
| 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<URLRequest> 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)); |
| 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<URLRequest> 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)); |
| 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<URLRequest> 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<URLRequest> 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<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, |
| kOrigin, kSiteForCookies)); |
| 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<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kSameSiteOrigin, |
| kSameSiteOrigin, kSiteForCookies)); |
| 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<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, |
| kHttpOrigin, kHttpOrigin, kHttpSiteForCookies)); |
| 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<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, |
| kHttpOrigin, kHttpOrigin, kHttpSiteForCookies)); |
| 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<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kMainFrame, kCrossSiteOrigin, |
| kCrossSiteOrigin, kCrossSiteForCookies)); |
| 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<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info( |
| IsolationInfo::Create(IsolationInfo::RequestType::kMainFrame, kOrigin, |
| kOrigin, kSiteForCookies)); |
| 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<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies)); |
| 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<URLRequest> req(default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_isolation_info(IsolationInfo::Create( |
| IsolationInfo::RequestType::kOther, kOrigin, kOrigin, kSiteForCookies)); |
| 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; |
|