| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <utility> |
| |
| // This must be before Windows headers |
| #include "base/bind_helpers.h" |
| #include "base/memory/ptr_util.h" |
| #include "build/build_config.h" |
| |
| #if defined(OS_WIN) |
| #include <objbase.h> |
| #include <shlobj.h> |
| #include <windows.h> |
| #include <wrl/client.h> |
| #endif |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <limits> |
| #include <memory> |
| |
| #include "base/base64url.h" |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/format_macros.h" |
| #include "base/json/json_reader.h" |
| #include "base/location.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/optional.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.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/test/bind_test_util.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "build/buildflag.h" |
| #include "crypto/sha2.h" |
| #include "net/base/chunked_upload_data_stream.h" |
| #include "net/base/directory_listing.h" |
| #include "net/base/elements_upload_data_stream.h" |
| #include "net/base/escape.h" |
| #include "net/base/features.h" |
| #include "net/base/hash_value.h" |
| #include "net/base/ip_address.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/isolation_info.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/load_timing_info.h" |
| #include "net/base/load_timing_info_test_util.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_module.h" |
| #include "net/base/proxy_server.h" |
| #include "net/base/request_priority.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/base/transport_info.h" |
| #include "net/base/upload_bytes_element_reader.h" |
| #include "net/base/upload_data_stream.h" |
| #include "net/base/upload_file_element_reader.h" |
| #include "net/base/url_util.h" |
| #include "net/cert/asn1_util.h" |
| #include "net/cert/cert_net_fetcher.h" |
| #include "net/cert/crl_set.h" |
| #include "net/cert/ct_policy_enforcer.h" |
| #include "net/cert/ct_policy_status.h" |
| #include "net/cert/do_nothing_ct_verifier.h" |
| #include "net/cert/ev_root_ca_metadata.h" |
| #include "net/cert/mock_cert_verifier.h" |
| #include "net/cert/multi_log_ct_verifier.h" |
| #include "net/cert/signed_certificate_timestamp_and_status.h" |
| #include "net/cert/test_root_certs.h" |
| #include "net/cert/x509_util.h" |
| #include "net/cert_net/cert_net_fetcher_url_request.h" |
| #include "net/cookies/canonical_cookie_test_helpers.h" |
| #include "net/cookies/cookie_inclusion_status.h" |
| #include "net/cookies/cookie_monster.h" |
| #include "net/cookies/cookie_store_test_helpers.h" |
| #include "net/cookies/test_cookie_access_delegate.h" |
| #include "net/disk_cache/disk_cache.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/http/http_byte_range.h" |
| #include "net/http/http_cache.h" |
| #include "net/http/http_network_layer.h" |
| #include "net/http/http_network_session.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_server_properties.h" |
| #include "net/http/http_util.h" |
| #include "net/log/file_net_log_observer.h" |
| #include "net/log/net_log_event_type.h" |
| #include "net/log/net_log_source.h" |
| #include "net/log/test_net_log.h" |
| #include "net/log/test_net_log_util.h" |
| #include "net/net_buildflags.h" |
| #include "net/proxy_resolution/configured_proxy_resolution_service.h" |
| #include "net/quic/mock_crypto_client_stream_factory.h" |
| #include "net/quic/quic_server_info.h" |
| #include "net/socket/read_buffering_stream_socket.h" |
| #include "net/socket/socket_test_util.h" |
| #include "net/socket/ssl_client_socket.h" |
| #include "net/ssl/client_cert_identity_test_util.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| #include "net/ssl/ssl_private_key.h" |
| #include "net/ssl/ssl_server_config.h" |
| #include "net/ssl/test_ssl_config_service.h" |
| #include "net/test/cert_test_util.h" |
| #include "net/test/embedded_test_server/default_handlers.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/embedded_test_server_connection_listener.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/referrer_policy.h" |
| #include "net/url_request/static_http_user_agent_settings.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_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_job_factory_impl.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 "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/platform_test.h" |
| #include "url/url_util.h" |
| |
| #if !BUILDFLAG(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) |
| #include "net/ftp/ftp_auth_cache.h" |
| #include "net/ftp/ftp_network_layer.h" |
| #include "net/url_request/ftp_protocol_handler.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "base/win/scoped_com_initializer.h" |
| #endif |
| |
| #if defined(OS_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) |
| |
| using net::test::IsError; |
| using net::test::IsOk; |
| using net::test_server::RegisterDefaultHandlers; |
| using testing::AnyOf; |
| using testing::ElementsAre; |
| using testing::IsEmpty; |
| |
| 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 base::string16 kChrome(ASCIIToUTF16("chrome")); |
| const base::string16 kSecret(ASCIIToUTF16("secret")); |
| const base::string16 kUser(ASCIIToUTF16("user")); |
| |
| const base::FilePath::CharType kTestFilePath[] = |
| FILE_PATH_LITERAL("net/data/url_request_unittest"); |
| |
| #if !BUILDFLAG(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) && \ |
| !defined(OS_FUCHSIA) |
| // Test file used in most FTP tests. |
| const char kFtpTestFile[] = "BullRunSpeech.txt"; |
| #endif |
| |
| // 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()); |
| } |
| |
| #if !BUILDFLAG(DISABLE_FTP_SUPPORT) && !defined(OS_ANDROID) && \ |
| !defined(OS_FUCHSIA) |
| // Tests load timing in the case that there is no HTTP response. This can be |
| // used to test in the case of errors or non-HTTP requests. |
| void TestLoadTimingNoHttpResponse(const LoadTimingInfo& load_timing_info) { |
| EXPECT_FALSE(load_timing_info.socket_reused); |
| EXPECT_EQ(NetLogSource::kInvalidId, load_timing_info.socket_log_id); |
| |
| // Only the request times should be non-null. |
| 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_TRUE(load_timing_info.proxy_resolve_start.is_null()); |
| EXPECT_TRUE(load_timing_info.proxy_resolve_end.is_null()); |
| EXPECT_TRUE(load_timing_info.send_start.is_null()); |
| EXPECT_TRUE(load_timing_info.send_end.is_null()); |
| EXPECT_TRUE(load_timing_info.receive_headers_start.is_null()); |
| EXPECT_TRUE(load_timing_info.receive_headers_end.is_null()); |
| } |
| #endif |
| |
| // Less verbose way of running a simple testserver for the tests below. |
| class HttpTestServer : public EmbeddedTestServer { |
| public: |
| explicit HttpTestServer(const base::FilePath& document_root) { |
| AddDefaultHandlers(document_root); |
| } |
| |
| HttpTestServer() { RegisterDefaultHandlers(this); } |
| }; |
| |
| // 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, |
| NetworkDelegate* network_delegate, |
| RequestPriority* request_priority) |
| : URLRequestTestJob(request, network_delegate), |
| request_priority_(request_priority) { |
| *request_priority_ = DEFAULT_PRIORITY; |
| } |
| |
| void SetPriority(RequestPriority priority) override { |
| *request_priority_ = priority; |
| URLRequestTestJob::SetPriority(priority); |
| } |
| |
| private: |
| RequestPriority* const request_priority_; |
| }; |
| |
| // Do a case-insensitive search through |haystack| for |needle|. |
| bool ContainsString(const std::string& haystack, const char* needle) { |
| std::string::const_iterator it = std::search( |
| haystack.begin(), haystack.end(), needle, needle + strlen(needle), |
| base::CaseInsensitiveCompareASCII<char>()); |
| return it != haystack.end(); |
| } |
| |
| std::unique_ptr<UploadDataStream> CreateSimpleUploadData(const char* data) { |
| std::unique_ptr<UploadElementReader> reader( |
| new 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); |
| |
| // 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, |
| CompletionOnceCallback callback, |
| HttpRequestHeaders* headers) override; |
| |
| int OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| base::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_; |
| |
| GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest. |
| int block_on_; // 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_; |
| |
| // Callback objects stored during blocking stages. |
| CompletionOnceCallback callback_; |
| |
| // Closure to run to exit RunUntilBlocked(). |
| base::OnceClosure on_blocked_; |
| |
| base::WeakPtrFactory<BlockingNetworkDelegate> weak_factory_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(BlockingNetworkDelegate); |
| }; |
| |
| BlockingNetworkDelegate::BlockingNetworkDelegate(BlockMode block_mode) |
| : block_mode_(block_mode), |
| retval_(OK), |
| block_on_(0), |
| stage_blocked_for_callback_(NOT_BLOCKED) {} |
| |
| void BlockingNetworkDelegate::RunUntilBlocked() { |
| base::RunLoop run_loop; |
| on_blocked_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| void BlockingNetworkDelegate::DoCallback(int response) { |
| ASSERT_EQ(USER_CALLBACK, block_mode_); |
| ASSERT_NE(NOT_BLOCKED, stage_blocked_for_callback_); |
| CompletionOnceCallback callback = std::move(callback_); |
| Reset(); |
| |
| // |callback| may trigger completion of a request, so post it as a task, so |
| // it will run under a subsequent TestDelegate::RunUntilComplete() loop. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback, |
| weak_factory_.GetWeakPtr(), response, |
| std::move(callback))); |
| } |
| |
| void BlockingNetworkDelegate::OnBlocked() { |
| // If this fails due to |on_blocked_| being null then OnBlocked() was run by |
| // a RunLoop other than RunUntilBlocked(), indicating a bug in the calling |
| // test. |
| std::move(on_blocked_).Run(); |
| } |
| |
| void BlockingNetworkDelegate::RunCallback(int response, |
| CompletionOnceCallback callback) { |
| std::move(callback).Run(response); |
| } |
| |
| int BlockingNetworkDelegate::OnBeforeURLRequest(URLRequest* request, |
| CompletionOnceCallback callback, |
| GURL* new_url) { |
| if (redirect_url_ == request->url()) |
| return OK; // We've already seen this request and redirected elsewhere. |
| |
| // TestNetworkDelegate always completes synchronously. |
| CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeURLRequest( |
| request, base::NullCallback(), new_url)); |
| |
| if (!redirect_url_.is_empty()) |
| *new_url = redirect_url_; |
| |
| return MaybeBlockStage(ON_BEFORE_URL_REQUEST, std::move(callback)); |
| } |
| |
| int BlockingNetworkDelegate::OnBeforeStartTransaction( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| HttpRequestHeaders* headers) { |
| // TestNetworkDelegate always completes synchronously. |
| CHECK_NE(ERR_IO_PENDING, TestNetworkDelegate::OnBeforeStartTransaction( |
| request, base::NullCallback(), headers)); |
| |
| return MaybeBlockStage(ON_BEFORE_SEND_HEADERS, std::move(callback)); |
| } |
| |
| int BlockingNetworkDelegate::OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| base::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::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::RunCallback, |
| weak_factory_.GetWeakPtr(), retval_, |
| std::move(callback))); |
| return ERR_IO_PENDING; |
| |
| case USER_CALLBACK: |
| callback_ = std::move(callback); |
| stage_blocked_for_callback_ = stage; |
| // We may reach here via a callback prior to RunUntilBlocked(), so post |
| // a task to fetch and run the |on_blocked_| closure. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&BlockingNetworkDelegate::OnBlocked, |
| weak_factory_.GetWeakPtr())); |
| return ERR_IO_PENDING; |
| } |
| NOTREACHED(); |
| return 0; |
| } |
| |
| class TestURLRequestContextWithProxy : public TestURLRequestContext { |
| public: |
| // Does not own |delegate|. |
| TestURLRequestContextWithProxy(const std::string& proxy, |
| NetworkDelegate* delegate, |
| bool delay_initialization = false) |
| : TestURLRequestContext(true) { |
| context_storage_.set_proxy_resolution_service( |
| ConfiguredProxyResolutionService::CreateFixed( |
| proxy, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| set_network_delegate(delegate); |
| if (!delay_initialization) |
| Init(); |
| } |
| ~TestURLRequestContextWithProxy() override = default; |
| }; |
| |
| // A mock ReportSenderInterface that just remembers the latest report |
| // URI and report to be sent. |
| class MockCertificateReportSender |
| : public TransportSecurityState::ReportSenderInterface { |
| public: |
| MockCertificateReportSender() = default; |
| ~MockCertificateReportSender() override = default; |
| |
| void Send( |
| const GURL& report_uri, |
| base::StringPiece content_type, |
| base::StringPiece report, |
| base::OnceCallback<void()> success_callback, |
| base::OnceCallback<void(const GURL&, int, int)> error_callback) override { |
| latest_report_uri_ = report_uri; |
| latest_report_.assign(report.data(), report.size()); |
| latest_content_type_.assign(content_type.data(), content_type.size()); |
| } |
| const GURL& latest_report_uri() { return latest_report_uri_; } |
| const std::string& latest_report() { return latest_report_; } |
| const std::string& latest_content_type() { return latest_content_type_; } |
| |
| private: |
| GURL latest_report_uri_; |
| std::string latest_report_; |
| std::string latest_content_type_; |
| }; |
| |
| // 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_; |
| }; |
| |
| } // namespace |
| |
| // Inherit PlatformTest since we require the autorelease pool on Mac OS X. |
| class URLRequestTest : public PlatformTest, public WithTaskEnvironment { |
| public: |
| URLRequestTest() |
| : default_context_(std::make_unique<TestURLRequestContext>(true)) { |
| default_context_->set_network_delegate(&default_network_delegate_); |
| default_context_->set_net_log(&net_log_); |
| job_factory_impl_ = new URLRequestJobFactoryImpl(); |
| job_factory_.reset(job_factory_impl_); |
| } |
| |
| ~URLRequestTest() override { |
| // URLRequestJobs may post clean-up tasks on destruction. |
| base::RunLoop().RunUntilIdle(); |
| |
| SetTransportSecurityStateSourceForTesting(nullptr); |
| } |
| |
| void SetUp() override { |
| SetUpFactory(); |
| default_context_->set_job_factory(job_factory_.get()); |
| default_context_->Init(); |
| PlatformTest::SetUp(); |
| } |
| |
| void TearDown() override { default_context_.reset(); } |
| |
| virtual void SetUpFactory() {} |
| |
| TestNetworkDelegate* default_network_delegate() { |
| return &default_network_delegate_; |
| } |
| |
| TestURLRequestContext& default_context() const { return *default_context_; } |
| |
| // Adds the TestJobInterceptor to the default context. |
| TestJobInterceptor* AddTestInterceptor() { |
| TestJobInterceptor* protocol_handler_ = new TestJobInterceptor(); |
| job_factory_impl_->SetProtocolHandler("http", nullptr); |
| job_factory_impl_->SetProtocolHandler("http", |
| base::WrapUnique(protocol_handler_)); |
| return protocol_handler_; |
| } |
| |
| // Creates a temp test file and writes |data| to the file. The file will be |
| // deleted after the test completes. |
| void CreateTestFile(const char* data, |
| size_t data_size, |
| base::FilePath* test_file) { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| // Get an absolute path since |temp_dir| can contain a symbolic link. As of |
| // now, Mac and Android bots return a path with a symbolic link. |
| base::FilePath absolute_temp_dir = |
| base::MakeAbsoluteFilePath(temp_dir_.GetPath()); |
| |
| ASSERT_TRUE(base::CreateTemporaryFileInDir(absolute_temp_dir, test_file)); |
| ASSERT_EQ(static_cast<int>(data_size), |
| base::WriteFile(*test_file, data, data_size)); |
| } |
| |
| protected: |
| RecordingTestNetLog net_log_; |
| TestNetworkDelegate default_network_delegate_; // Must outlive URLRequest. |
| URLRequestJobFactoryImpl* job_factory_impl_; |
| std::unique_ptr<URLRequestJobFactory> job_factory_; |
| std::unique_ptr<TestURLRequestContext> 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_F(URLRequestTest, InvalidReferrerTest) { |
| TestURLRequestContext context; |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_cancel_request_with_policy_violating_referrer(true); |
| context.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| 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) { |
| TestURLRequestContext context; |
| TestNetworkDelegate network_delegate; |
| network_delegate.set_cancel_request_with_policy_violating_referrer(false); |
| context.set_network_delegate(&network_delegate); |
| 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::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) { |
| TestURLRequestContext context; |
| TestNetworkDelegate network_delegate; |
| context.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| 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) { |
| TestURLRequestContext context; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate.set_redirect_url(GURL("http://redirect.com/")); |
| context.set_network_delegate(&network_delegate); |
| 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=*/base::nullopt, |
| /*modified_headers=*/base::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) { |
| TestURLRequestContext context; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate.set_cancel_request_with_policy_violating_referrer(true); |
| context.set_network_delegate(&network_delegate); |
| network_delegate.set_redirect_url(GURL("http://redirect.com/")); |
| 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=*/base::nullopt, |
| /*modified_headers=*/base::nullopt); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsReferrerWithInformativeQuery) { |
| TestURLRequestContext context; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate.set_cancel_request_with_policy_violating_referrer(true); |
| context.set_network_delegate(&network_delegate); |
| network_delegate.set_redirect_url(GURL("http://redirect.com/")); |
| 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=*/base::nullopt, |
| /*modified_headers=*/base::nullopt); |
| d.RunUntilComplete(); |
| histograms.ExpectUniqueSample( |
| "Net.URLRequest.ReferrerHasInformativePath.CrossOrigin", true, 1); |
| } |
| |
| TEST_F(URLRequestTest, RecordsReferrerWithoutInformativePathOrQuery) { |
| TestURLRequestContext context; |
| BlockingNetworkDelegate network_delegate( |
| BlockingNetworkDelegate::SYNCHRONOUS); |
| network_delegate.set_cancel_request_with_policy_violating_referrer(false); |
| context.set_network_delegate(&network_delegate); |
| network_delegate.set_redirect_url(GURL("http://origin.com/")); |
| 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=*/base::nullopt, |
| /*modified_headers=*/base::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: |
| URLRequestJob* MaybeInterceptRequest( |
| URLRequest* request, |
| NetworkDelegate* network_delegate) const override { |
| URLRequestTestJob* job = new URLRequestTestJob( |
| request, network_delegate, 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: |
| URLRequestInterceptorWithLoadTimingInfo* 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::TimeDelta::FromDays(1); |
| load_timing.proxy_resolve_end = now + base::TimeDelta::FromDays(2); |
| } |
| |
| LoadTimingInfo::ConnectTiming& connect_timing = load_timing.connect_timing; |
| if (connect_time_flags & CONNECT_TIMING_HAS_DNS_TIMES) { |
| connect_timing.dns_start = now + base::TimeDelta::FromDays(3); |
| connect_timing.dns_end = now + base::TimeDelta::FromDays(4); |
| } |
| connect_timing.connect_start = now + base::TimeDelta::FromDays(5); |
| if (connect_time_flags & CONNECT_TIMING_HAS_SSL_TIMES) { |
| connect_timing.ssl_start = now + base::TimeDelta::FromDays(6); |
| connect_timing.ssl_end = now + base::TimeDelta::FromDays(7); |
| } |
| connect_timing.connect_end = now + base::TimeDelta::FromDays(8); |
| |
| load_timing.send_start = now + base::TimeDelta::FromDays(9); |
| load_timing.send_end = now + base::TimeDelta::FromDays(10); |
| load_timing.receive_headers_start = now + base::TimeDelta::FromDays(11); |
| load_timing.receive_headers_end = now + base::TimeDelta::FromDays(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::TimeDelta::FromDays(1); |
| load_timing.proxy_resolve_end = now + base::TimeDelta::FromDays(2); |
| } |
| |
| load_timing.send_start = now + base::TimeDelta::FromDays(9); |
| load_timing.send_end = now + base::TimeDelta::FromDays(10); |
| load_timing.receive_headers_start = now + base::TimeDelta::FromDays(11); |
| load_timing.receive_headers_end = now + base::TimeDelta::FromDays(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.dns_start, |
| load_timing_result.connect_timing.dns_start); |
| EXPECT_EQ(job_load_timing.connect_timing.dns_end, |
| load_timing_result.connect_timing.dns_end); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_end, |
| load_timing_result.connect_timing.connect_end); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_start, |
| load_timing_result.connect_timing.ssl_start); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_end, |
| load_timing_result.connect_timing.ssl_end); |
| |
| // Redundant sanity check. |
| TestLoadTimingNotReused(load_timing_result, CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| |
| // Another basic test, with proxy and SSL times, but no DNS times. |
| TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingProxy) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_SSL_TIMES, true); |
| |
| LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Nothing should have been changed by the URLRequest. |
| EXPECT_EQ(job_load_timing.proxy_resolve_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(job_load_timing.proxy_resolve_end, |
| load_timing_result.proxy_resolve_end); |
| EXPECT_EQ(job_load_timing.connect_timing.dns_start, |
| load_timing_result.connect_timing.dns_start); |
| EXPECT_EQ(job_load_timing.connect_timing.dns_end, |
| load_timing_result.connect_timing.dns_end); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(job_load_timing.connect_timing.connect_end, |
| load_timing_result.connect_timing.connect_end); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_start, |
| load_timing_result.connect_timing.ssl_start); |
| EXPECT_EQ(job_load_timing.connect_timing.ssl_end, |
| load_timing_result.connect_timing.ssl_end); |
| |
| // Redundant sanity check. |
| TestLoadTimingNotReusedWithProxy(load_timing_result, |
| CONNECT_TIMING_HAS_SSL_TIMES); |
| } |
| |
| // Make sure that URLRequest correctly adjusts proxy times when they're before |
| // |request_start|, due to already having a connected socket. This happens in |
| // the case of reusing a SPDY session. The connected socket is not considered |
| // reused in this test (May be a preconnect). |
| // |
| // To mix things up from the test above, assumes DNS times but no SSL times. |
| TEST_F(URLRequestLoadTimingTest, InterceptLoadTimingEarlyProxyResolution) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = |
| NormalLoadTimingInfo(now, CONNECT_TIMING_HAS_DNS_TIMES, true); |
| job_load_timing.proxy_resolve_start = now - base::TimeDelta::FromDays(6); |
| job_load_timing.proxy_resolve_end = now - base::TimeDelta::FromDays(5); |
| job_load_timing.connect_timing.dns_start = now - base::TimeDelta::FromDays(4); |
| job_load_timing.connect_timing.dns_end = now - base::TimeDelta::FromDays(3); |
| job_load_timing.connect_timing.connect_start = |
| now - base::TimeDelta::FromDays(2); |
| job_load_timing.connect_timing.connect_end = |
| now - base::TimeDelta::FromDays(1); |
| |
| LoadTimingInfo load_timing_result = RunURLRequestInterceptorLoadTimingTest( |
| job_load_timing, default_context(), interceptor()); |
| |
| // Proxy times, connect times, and DNS times should all be replaced with |
| // request_start. |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.proxy_resolve_end); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.dns_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.dns_end); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_start); |
| EXPECT_EQ(load_timing_result.request_start, |
| load_timing_result.connect_timing.connect_end); |
| |
| // Other times should have been left null. |
| TestLoadTimingNotReusedWithProxy(load_timing_result, |
| CONNECT_TIMING_HAS_DNS_TIMES); |
| } |
| |
| // Same as above, but in the reused case. |
| TEST_F(URLRequestLoadTimingTest, |
| InterceptLoadTimingEarlyProxyResolutionReused) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| LoadTimingInfo job_load_timing = NormalLoadTimingInfoReused(now, true); |
| job_load_timing.proxy_resolve_start = now - base::TimeDelta::FromDays(4); |
| job_load_timing.proxy_resolve_end = now - base::TimeDelta::FromDays(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::TimeDelta::FromDays(1); |
| job_load_timing.connect_timing.ssl_start = now - base::TimeDelta::FromDays(2); |
| job_load_timing.connect_timing.ssl_end = now - base::TimeDelta::FromDays(3); |
| job_load_timing.connect_timing.connect_end = |
| now - base::TimeDelta::FromDays(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::TimeDelta::FromDays(1); |
| job_load_timing.connect_timing.connect_end = |
| now - base::TimeDelta::FromDays(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) { |
| MockHostResolver host_resolver; |
| host_resolver.rules()->AddSimulatedTimeoutFailure("*"); |
| |
| TestNetworkDelegate network_delegate; // Must outlive URLRequests. |
| TestURLRequestContextWithProxy context("myproxy:70", &network_delegate, |
| true /* delay_initialization */); |
| context.set_host_resolver(&host_resolver); |
| context.Init(); |
| |
| 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 server should be set before failure. |
| EXPECT_EQ(ProxyServer::FromPacString("PROXY myproxy:70"), |
| req->proxy_server()); |
| EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, d.request_status()); |
| EXPECT_THAT(req->response_info().resolve_error_info.error, |
| IsError(ERR_DNS_TIMED_OUT)); |
| |
| EXPECT_EQ(1, network_delegate.error_count()); |
| EXPECT_THAT(network_delegate.last_error(), |
| IsError(ERR_PROXY_CONNECTION_FAILED)); |
| EXPECT_EQ(1, network_delegate.completed_requests()); |
| } |
| |
| TEST_F(URLRequestTest, SkipSecureDnsDisabledByDefault) { |
| MockHostResolver host_resolver; |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_host_resolver(&host_resolver); |
| context.Init(); |
| |
| 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_FALSE(host_resolver.last_secure_dns_mode_override().has_value()); |
| } |
| |
| TEST_F(URLRequestTest, SkipSecureDnsEnabled) { |
| MockHostResolver host_resolver; |
| TestNetworkDelegate network_delegate; // Must outlive URLRequest. |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.set_host_resolver(&host_resolver); |
| context.Init(); |
| |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req( |
| context.CreateRequest(GURL("http://example.com"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetDisableSecureDns(true); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(net::DnsConfig::SecureDnsMode::OFF, |
| host_resolver.last_secure_dns_mode_override().value()); |
| } |
| |
| // 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(new PriorityMonitoringURLRequestJob( |
| req.get(), &default_network_delegate_, &job_priority)); |
| AddTestInterceptor()->set_main_intercept_job(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(new PriorityMonitoringURLRequestJob( |
| req.get(), &default_network_delegate_, &job_priority)); |
| AddTestInterceptor()->set_main_intercept_job(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(new PriorityMonitoringURLRequestJob( |
| req.get(), &default_network_delegate_, &job_priority)); |
| AddTestInterceptor()->set_main_intercept_job(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() callback |
| // is called once for simple redirect-less requests. |
| TEST_F(URLRequestTest, NotifyDelegateConnectedOnce) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate delegate; |
| |
| auto request = default_context().CreateRequest(test_server.GetURL("/echo"), |
| DEFAULT_PRIORITY, &delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| delegate.RunUntilComplete(); |
| |
| TransportInfo expected_transport; |
| expected_transport.endpoint = |
| IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); |
| EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); |
| } |
| |
| // This test verifies that URLRequest::Delegate's OnConnected() callback is |
| // called after each redirect. |
| TEST_F(URLRequestTest, NotifyDelegateConnectedOnEachRedirect) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate delegate; |
| |
| // Fetch a page that redirects us once. |
| GURL url = test_server.GetURL("/server-redirect?" + |
| test_server.GetURL("/echo").spec()); |
| auto request = default_context().CreateRequest( |
| url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| delegate.RunUntilRedirect(); |
| |
| TransportInfo expected_transport; |
| expected_transport.endpoint = |
| IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); |
| EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); |
| |
| request->FollowDeferredRedirect(/*removed_headers=*/{}, |
| /*modified_headers=*/{}); |
| delegate.RunUntilComplete(); |
| |
| EXPECT_THAT(delegate.transports(), |
| ElementsAre(expected_transport, expected_transport)); |
| } |
| |
| // This test verifies that when the URLRequest Delegate returns an error from |
| // OnBeforeSendHeaders(), the entire request fails with that error. |
| TEST_F(URLRequestTest, NotifyDelegateConnectedReturnError) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestDelegate delegate; |
| delegate.set_on_connected_result(ERR_NOT_IMPLEMENTED); |
| |
| auto request = default_context().CreateRequest(test_server.GetURL("/echo"), |
| DEFAULT_PRIORITY, &delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| delegate.RunUntilComplete(); |
| |
| TransportInfo expected_transport; |
| expected_transport.endpoint = |
| IPEndPoint(IPAddress::IPv4Localhost(), test_server.port()); |
| EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport)); |
| |
| EXPECT_TRUE(delegate.request_failed()); |
| EXPECT_THAT(delegate.request_status(), IsError(ERR_NOT_IMPLEMENTED)); |
| } |
| |
| TEST_F(URLRequestTest, DelayedCookieCallback) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestURLRequestContext context; |
| std::unique_ptr<DelayedCookieMonster> delayed_cm(new DelayedCookieMonster()); |
| context.set_cookie_store(delayed_cm.get()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| context.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, network_delegate.blocked_get_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. |
| { |
| TestNetworkDelegate network_delegate; |
| context.set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != |
| std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| class FilteringTestNetworkDelegate : public TestNetworkDelegate { |
| public: |
| FilteringTestNetworkDelegate() |
| : set_cookie_called_count_(0), |
| blocked_set_cookie_count_(0), |
| block_get_cookies_(false), |
| get_cookie_called_count_(0), |
| blocked_get_cookie_count_(0) {} |
| ~FilteringTestNetworkDelegate() override = default; |
| |
| bool OnCanSetCookie(const URLRequest& request, |
| const net::CanonicalCookie& cookie, |
| CookieOptions* options, |
| bool allowed_from_caller) override { |
| // Filter out cookies with the same name as |cookie_name_filter_| and |
| // combine with |allowed_from_caller|. |
| bool allowed = |
| allowed_from_caller && !(cookie.Name() == cookie_name_filter_); |
| |
| ++set_cookie_called_count_; |
| |
| if (!allowed) |
| ++blocked_set_cookie_count_; |
| |
| return TestNetworkDelegate::OnCanSetCookie(request, cookie, options, |
| allowed); |
| } |
| |
| void SetCookieFilter(std::string filter) { |
| cookie_name_filter_ = std::move(filter); |
| } |
| |
| int set_cookie_called_count() { return set_cookie_called_count_; } |
| |
| int blocked_set_cookie_count() { return blocked_set_cookie_count_; } |
| |
| void ResetSetCookieCalledCount() { set_cookie_called_count_ = 0; } |
| |
| void ResetBlockedSetCookieCount() { blocked_set_cookie_count_ = 0; } |
| |
| bool OnCanGetCookies(const URLRequest& request, |
| bool allowed_from_caller) override { |
| // Filter out cookies if |block_get_cookies_| is set and |
| // combine with |allowed_from_caller|. |
| bool allowed = allowed_from_caller && !block_get_cookies_; |
| |
| ++get_cookie_called_count_; |
| |
| if (!allowed) |
| ++blocked_get_cookie_count_; |
| |
| return TestNetworkDelegate::OnCanGetCookies(request, allowed); |
| } |
| |
| void set_block_get_cookies() { block_get_cookies_ = true; } |
| |
| void unset_block_get_cookies() { block_get_cookies_ = false; } |
| |
| int get_cookie_called_count() const { return get_cookie_called_count_; } |
| |
| int blocked_get_cookie_count() const { return blocked_get_cookie_count_; } |
| |
| void ResetGetCookieCalledCount() { get_cookie_called_count_ = 0; } |
| |
| void ResetBlockedGetCookieCount() { blocked_get_cookie_count_ = 0; } |
| |
| private: |
| std::string cookie_name_filter_; |
| int set_cookie_called_count_; |
| int blocked_set_cookie_count_; |
| |
| bool block_get_cookies_; |
| int get_cookie_called_count_; |
| int blocked_get_cookie_count_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FilteringTestNetworkDelegate); |
| }; |
| |
| TEST_F(URLRequestTest, DelayedCookieCallbackAsync) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestURLRequestContext async_context; |
| std::unique_ptr<DelayedCookieMonster> delayed_cm = |
| std::make_unique<DelayedCookieMonster>(); |
| async_context.set_cookie_store(delayed_cm.get()); |
| FilteringTestNetworkDelegate async_filter_network_delegate; |
| async_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie"); |
| async_context.set_network_delegate(&async_filter_network_delegate); |
| TestDelegate async_delegate; |
| |
| TestURLRequestContext sync_context; |
| std::unique_ptr<CookieMonster> cm = |
| std::make_unique<CookieMonster>(nullptr, nullptr); |
| sync_context.set_cookie_store(cm.get()); |
| FilteringTestNetworkDelegate sync_filter_network_delegate; |
| sync_filter_network_delegate.SetCookieFilter("CookieBlockedOnCanGetCookie"); |
| sync_context.set_network_delegate(&sync_filter_network_delegate); |
| TestDelegate sync_delegate; |
| |
| // 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(), |
| base::nullopt /* server_time */); |
| delayed_cm->SetCanonicalCookieAsync(std::move(cookie1), url, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| auto cookie2 = CanonicalCookie::Create(url, "AlreadySetCookie=1;Secure", |
| base::Time::Now(), |
| base::nullopt /* server_time */); |
| cm->SetCanonicalCookieAsync(std::move(cookie2), url, |
| net::CookieOptions::MakeAllInclusive(), |
| CookieStore::SetCookiesCallback()); |
| |
| 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 = |
| async_context.CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?" + first_cookie_line + "&" + |
| second_cookie_line), |
| DEFAULT_PRIORITY, &async_delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| request->Start(); |
| async_delegate.RunUntilComplete(); |
| EXPECT_THAT(async_delegate.request_status(), IsOk()); |
| |
| // Run with the regular cookie monster. |
| request = sync_context.CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?" + first_cookie_line + "&" + |
| second_cookie_line), |
| DEFAULT_PRIORITY, &sync_delegate, TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| 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. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != |
| std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie isn't sent when LOAD_DO_NOT_SEND_COOKIES is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetLoadFlags(LOAD_DO_NOT_SEND_COOKIES); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == |
| std::string::npos); |
| |
| // LOAD_DO_NOT_SEND_COOKIES does not trigger OnGetCookies. |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(1, network_delegate.set_cookie_count()); |
| } |
| |
| // Try to set-up another cookie and update the previous cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetLoadFlags(LOAD_DO_NOT_SAVE_COOKIES); |
| req->Start(); |
| |
| d.RunUntilComplete(); |
| |
| // LOAD_DO_NOT_SAVE_COOKIES does not trigger OnSetCookie. |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(0, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(0, network_delegate.set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != |
| std::string::npos); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| auto entries = net_log_.GetEntries(); |
| for (const auto& entry : entries) { |
| EXPECT_NE(entry.type, |
| NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE); |
| } |
| } |
| |
| // Verify that the cookie isn't sent. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| network_delegate.set_cookie_options(TestNetworkDelegate::NO_GET_COOKIES); |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == |
| std::string::npos); |
| |
| EXPECT_EQ(1, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| auto entries = net_log_.GetEntries(); |
| ExpectLogContainsSomewhereAfter( |
| entries, 0, NetLogEventType::COOKIE_GET_BLOCKED_BY_NETWORK_DELEGATE, |
| NetLogEventPhase::NONE); |
| } |
| } |
| |
| // TODO(crbug.com/564656) This test is flaky on iOS. |
| #if defined(OS_IOS) |
| #define MAYBE_DoNotSaveCookies_ViaPolicy FLAKY_DoNotSaveCookies_ViaPolicy |
| #else |
| #define MAYBE_DoNotSaveCookies_ViaPolicy DoNotSaveCookies_ViaPolicy |
| #endif |
| TEST_F(URLRequestTest, DoNotSaveCookies_ViaPolicy) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| auto entries = net_log_.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. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| network_delegate.set_cookie_options(TestNetworkDelegate::NO_SET_COOKIE); |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(2, network_delegate.blocked_set_cookie_count()); |
| auto entries = net_log_.GetEntries(); |
| ExpectLogContainsSomewhereAfter( |
| entries, 0, NetLogEventType::COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE, |
| NetLogEventPhase::NONE); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveEmptyCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up an empty cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(0, network_delegate.set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSendCookies_ViaPolicy_Async) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSend=1"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("CookieToNotSend=1") != |
| std::string::npos); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie isn't sent. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| network_delegate.set_cookie_options(TestNetworkDelegate::NO_GET_COOKIES); |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1") == |
| std::string::npos); |
| |
| EXPECT_EQ(1, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, DoNotSaveCookies_ViaPolicy_Async) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up a cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotUpdate=2"), DEFAULT_PRIORITY, |
| &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Try to set-up another cookie and update the previous cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| network_delegate.set_cookie_options(TestNetworkDelegate::NO_SET_COOKIE); |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/set-cookie?CookieToNotSave=1&CookieToNotUpdate=1"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(2, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify the cookies weren't saved or updated. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SameSiteCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| |
| const std::string kHost = "example.test"; |
| const std::string kSubHost = "subdomain.example.test"; |
| const std::string kCrossHost = "cross-origin.test"; |
| |
| // Set up two 'SameSite' cookies on 'example.test' |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" |
| "LaxSameSiteCookie=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); |
| req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/"))); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| EXPECT_EQ(2, network_delegate.set_cookie_count()); |
| } |
| |
| // Verify that both cookies are sent for same-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( |
| SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); |
| req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/"))); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, 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( |
| SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, 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(url::Origin::Create(test_server.GetURL(kSubHost, "/"))); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, 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( |
| SiteForCookies::FromUrl(test_server.GetURL(kCrossHost, "/"))); |
| req->set_initiator( |
| url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the lax cookie is sent for cross-site initiators when the |
| // method is "safe". |
| { |
| 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(kHost, "/"))); |
| req->set_initiator( |
| url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that neither cookie is sent for cross-site initiators when the |
| // method is unsafe (e.g. POST). |
| { |
| 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(kHost, "/"))); |
| req->set_initiator( |
| url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); |
| 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, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SettingSameSiteCookies) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| |
| const std::string kHost = "example.test"; |
| const std::string kSubHost = "subdomain.example.test"; |
| const std::string kCrossHost = "cross-origin.test"; |
| |
| int expected_cookies = 0; |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?Strict1=1;SameSite=Strict&" |
| "Lax1=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); |
| req->set_initiator(url::Origin::Create(test_server.GetURL(kHost, "/"))); |
| |
| // 'SameSite' cookies are settable from strict same-site contexts |
| // (same-origin site_for_cookies, same-origin initiator), so this request |
| // should result in two cookies being set. |
| expected_cookies += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count()); |
| } |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?Strict2=1;SameSite=Strict&" |
| "Lax2=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(test_server.GetURL(kHost, "/"))); |
| req->set_initiator( |
| url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); |
| |
| // 'SameSite' cookies are settable from lax same-site contexts (same-origin |
| // site_for_cookies, cross-site initiator), so this request should result in |
| // two cookies being set. |
| expected_cookies += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count()); |
| } |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?Strict3=1;SameSite=Strict&" |
| "Lax3=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); |
| req->set_initiator( |
| url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); |
| |
| // 'SameSite' cookies are settable from lax same-site contexts (same-site |
| // site_for_cookies, cross-site initiator), so this request should result in |
| // two cookies being set. |
| expected_cookies += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count()); |
| } |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?Strict4=1;SameSite=Strict&" |
| "Lax4=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(test_server.GetURL(kSubHost, "/"))); |
| |
| // 'SameSite' cookies are settable from strict same-site contexts (same-site |
| // site_for_cookies, no initiator), so this request should result in two |
| // cookies being set. |
| expected_cookies += 2; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| EXPECT_EQ(expected_cookies, network_delegate.set_cookie_count()); |
| } |
| |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL(kHost, |
| "/set-cookie?Strict5=1;SameSite=Strict&" |
| "Lax5=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(test_server.GetURL(kCrossHost, "/"))); |
| req->set_initiator( |
| url::Origin::Create(test_server.GetURL(kCrossHost, "/"))); |
| |
| // 'SameSite' cookies are not settable from cross-site contexts, so this |
| // should not result in any new cookies being set. |
| expected_cookies += 0; |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| // This counts the number of cookies actually set. |
| EXPECT_EQ(expected_cookies, |
| static_cast<int>(GetAllCookies(&default_context()).size())); |
| // This counts the number of successful calls to CanSetCookie() when |
| // attempting to set a cookie. The two cookies above were created and |
| // attempted to be set, and were not rejected by the NetworkDelegate, so the |
| // count here is 2 more than the number of cookies actually set. |
| EXPECT_EQ(expected_cookies + 2, network_delegate.set_cookie_count()); |
| } |
| } |
| |
| // Tests special chrome:// scheme that is supposed to always attach SameSite |
| // cookies if the requested site is secure. |
| TEST_F(URLRequestTest, SameSiteCookiesSpecialScheme) { |
| url::ScopedSchemeRegistryForTests scoped_registry; |
| url::AddStandardScheme("chrome", url::SchemeType::SCHEME_WITH_HOST); |
| |
| EmbeddedTestServer https_test_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_test_server); |
| ASSERT_TRUE(https_test_server.Start()); |
| EmbeddedTestServer http_test_server(EmbeddedTestServer::TYPE_HTTP); |
| RegisterDefaultHandlers(&http_test_server); |
| // Ensure they are on different ports. |
| ASSERT_TRUE(http_test_server.Start(https_test_server.port() + 1)); |
| // Both hostnames should be 127.0.0.1 (so that we can use the same set of |
| // cookies on both, for convenience). |
| ASSERT_EQ(https_test_server.host_port_pair().host(), |
| http_test_server.host_port_pair().host()); |
| |
| // Set up special schemes |
| auto cad = std::make_unique<TestCookieAccessDelegate>(); |
| cad->SetIgnoreSameSiteRestrictionsScheme("chrome", true); |
| |
| CookieMonster cm(nullptr, nullptr); |
| cm.SetCookieAccessDelegate(std::move(cad)); |
| |
| TestURLRequestContext context(true); |
| context.set_cookie_store(&cm); |
| context.Init(); |
| |
| // SameSite cookies are not set for 'chrome' scheme if requested origin is not |
| // secure. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateRequest( |
| http_test_server.GetURL( |
| "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" |
| "LaxSameSiteCookie=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(GURL("chrome://whatever/"))); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0u, GetAllCookies(&context).size()); |
| } |
| |
| // But they are set for 'chrome' scheme if the requested origin is secure. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateRequest( |
| https_test_server.GetURL( |
| "/set-cookie?StrictSameSiteCookie=1;SameSite=Strict&" |
| "LaxSameSiteCookie=1;SameSite=Lax"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(GURL("chrome://whatever/"))); |
| req->Start(); |
| d.RunUntilComplete(); |
| CookieList cookies = GetAllCookies(&context); |
| EXPECT_EQ(2u, cookies.size()); |
| } |
| |
| // Verify that they are both sent when the site_for_cookies scheme is |
| // 'chrome' and the requested origin is secure. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateRequest( |
| https_test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(GURL("chrome://whatever/"))); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_NE(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie=1")); |
| EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1")); |
| } |
| |
| // Verify that they are not sent when the site_for_cookies scheme is |
| // 'chrome' and the requested origin is not secure. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateRequest( |
| http_test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_site_for_cookies( |
| SiteForCookies::FromUrl(GURL("chrome://whatever/"))); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(std::string::npos, |
| d.data_received().find("StrictSameSiteCookie")); |
| EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie")); |
| } |
| } |
| |
| // Tests that __Secure- cookies can't be set on non-secure origins. |
| TEST_F(URLRequestTest, SecureCookiePrefixOnNonsecureOrigin) { |
| EmbeddedTestServer http_server; |
| RegisterDefaultHandlers(&http_server); |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a Secure __Secure- cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| http_server.GetURL("/set-cookie?__Secure-nonsecure-origin=1;Secure"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.data_received().find("__Secure-nonsecure-origin=1"), |
| std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SecureCookiePrefixNonsecure) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a non-Secure __Secure- cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| https_server.GetURL("/set-cookie?__Secure-foo=1"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, network_delegate.set_cookie_count()); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.data_received().find("__Secure-foo=1"), std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| TEST_F(URLRequestTest, SecureCookiePrefixSecure) { |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a Secure __Secure- cookie. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| https_server.GetURL("/set-cookie?__Secure-bar=1;Secure"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_NE(d.data_received().find("__Secure-bar=1"), std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| // Tests that secure cookies can't be set on non-secure origins if strict secure |
| // cookies are enabled. |
| TEST_F(URLRequestTest, StrictSecureCookiesOnNonsecureOrigin) { |
| EmbeddedTestServer http_server; |
| RegisterDefaultHandlers(&http_server); |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| RegisterDefaultHandlers(&https_server); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| TestNetworkDelegate network_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_delegate(&network_delegate); |
| context.Init(); |
| |
| // Try to set a Secure cookie, with experimental features enabled. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| http_server.GetURL("/set-cookie?nonsecure-origin=1;Secure"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| |
| // Verify that the cookie is not set. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(context.CreateFirstPartyRequest( |
| https_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(d.data_received().find("nonsecure-origin=1"), std::string::npos); |
| EXPECT_EQ(0, network_delegate.blocked_get_cookies_count()); |
| EXPECT_EQ(0, network_delegate.blocked_set_cookie_count()); |
| } |
| } |
| |
| // FixedDateNetworkDelegate swaps out the server's HTTP Date response header |
| // value for the |fixed_date| argument given to the constructor. |
| class FixedDateNetworkDelegate : public TestNetworkDelegate { |
| public: |
| explicit FixedDateNetworkDelegate(const std::string& fixed_date) |
| : fixed_date_(fixed_date) {} |
| ~FixedDateNetworkDelegate() override = default; |
| |
| // NetworkDelegate implementation |
| int OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| base::Optional<GURL>* preserve_fragment_on_redirect_url) override; |
| |
| private: |
| std::string fixed_date_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FixedDateNetworkDelegate); |
| }; |
| |
| int FixedDateNetworkDelegate::OnHeadersReceived( |
| URLRequest* request, |
| CompletionOnceCallback callback, |
| const HttpResponseHeaders* original_response_headers, |
| scoped_refptr<HttpResponseHeaders>* override_response_headers, |
| const IPEndPoint& endpoint, |
| base::Optional<GURL>* preserve_fragment_on_redirect_url) { |
| HttpResponseHeaders* new_response_headers = |
| new HttpResponseHeaders(original_response_headers->raw_headers()); |
| |
| new_response_headers->SetHeader("Date", fixed_date_); |
| |
| *override_response_headers = new_response_headers; |
| return TestNetworkDelegate::OnHeadersReceived( |
| request, std::move(callback), original_response_headers, |
| override_response_headers, endpoint, preserve_fragment_on_redirect_url); |
| } |
| |
| // Test that cookie expiration times are adjusted for server/client clock |
| // skew and that we handle incorrect timezone specifier "UTC" in HTTP Date |
| // headers by defaulting to GMT. (crbug.com/135131) |
| TEST_F(URLRequestTest, AcceptClockSkewCookieWithWrongDateTimezone) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Set up an expired cookie. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL( |
| "/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| } |
| // Verify that the cookie is not set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("StillGood=1") == std::string::npos); |
| } |
| // Set up a cookie with clock skew and "UTC" HTTP Date timezone specifier. |
| { |
| FixedDateNetworkDelegate network_delegate("18-Apr-1977 22:49:13 UTC"); |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL( |
| "/set-cookie?StillGood=1;expires=Mon,18-Apr-1977,22:50:13,GMT"), |
| DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| } |
| // Verify that the cookie is set. |
| { |
| TestNetworkDelegate network_delegate; |
| default_context().set_network_delegate(&network_delegate); |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| test_server.GetURL("/echoheader?Cookie"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_TRUE(d.data_received().find("StillGood=1") != std::string::npos); |
| } |
| } |
| |
| // Check that it is impossible to change the referrer in the extra headers of |
| // an URLRequest. |
| TEST_F(URLRequestTest, DoNotOverrideReferrer) { |
| HttpTestServer test_server; |
| ASSERT_TRUE(test_server.Start()); |
| |
| // If extra headers contain referer and the request contains a referer, |
| // only the latter shall be respected. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->SetReferrer("http://foo.com/"); |
| |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kReferer, "http://bar.com/"); |
| req->SetExtraRequestHeaders(headers); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ("http://foo.com/", d.data_received()); |
| } |
| |
| // If extra headers contain a referer but the request does not, no referer |
| // shall be sent in the header. |
| { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateRequest( |
| test_server.GetURL("/echoheader?Referer"), DEFAULT_PRIORITY, &d, |
| TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kReferer, "http://bar.com/"); |
| req->SetExtraRequestHeaders(headers); |
| req->SetLoadFlags(LOAD_VALIDATE_CACHE); |
| |
| req->Start(); |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ("None", d.data_received()); |
| } |
| } |
| |
| class URLRequestTestHTTP : public URLRequestTest { |
| public: |
| const url::Origin origin1_; |
| const url::Origin origin2_; |
| const IsolationInfo isolation_info1_; |
| const IsolationInfo isolation_info2_; |
| |
| URLRequestTestHTTP() |
| : origin1_(url::Origin::Create(GURL("https://foo.test/"))), |
| origin2_(url::Origin::Create(GURL("https://bar.test/"))), |
| isolation_info1_(IsolationInfo::CreateForInternalRequest(origin1_)), |
| isolation_info2_(IsolationInfo::CreateForInternalRequest(origin2_)), |
| test_server_(base::FilePath(kTestFilePath)) {} |
| |
| protected: |
| // ProtocolHandler for the scheme that's unsafe to redirect to. |
| class NET_EXPORT UnsafeRedirectProtocolHandler |
| : public URLRequestJobFactory::ProtocolHandler { |
| public: |
| UnsafeRedirectProtocolHandler() = default; |
| ~UnsafeRedirectProtocolHandler() override = default; |
| |
| // URLRequestJobFactory::ProtocolHandler implementation: |
| |
| URLRequestJob* MaybeCreateJob( |
| URLRequest* request, |
| NetworkDelegate* network_delegate) const override { |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| bool IsSafeRedirectTarget(const GURL& location) const override { |
| return false; |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(UnsafeRedirectProtocolHandler); |
| }; |
| |
| // URLRequestTest interface: |
| void SetUpFactory() override { |
| // Add FTP support to the default URLRequestContext. |
| job_factory_impl_->SetProtocolHandler( |
| "unsafe", std::make_unique<UnsafeRedirectProtocolHandler>()); |
| } |
| |
| // Requests |redirect_url|, which must return a HTTP 3xx redirect. |
| // |request_method| is the method to use for the initial request. |
| // |redirect_method| is the method that is expected to be used for the second |
| // request, after redirection. |
| // If |include_data| is true, data is uploaded with the request. The |
| // response body is expected to match it exactly, if and only if |
| // |request_method| == |redirect_method|. |
| void HTTPRedirectMethodTest(const GURL& redirect_url, |
| const std::string& request_method, |
| const std::string& redirect_method, |
| bool include_data) { |
| static const char kData[] = "hello world"; |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_method(request_method); |
| if (include_data) { |
| req->set_upload(CreateSimpleUploadData(kData)); |
| HttpRequestHeaders headers; |
| headers.SetHeader(HttpRequestHeaders::kContentLength, |
| base::NumberToString(base::size(kData) - 1)); |
| headers.SetHeader(HttpRequestHeaders::kContentType, "text/plain"); |
| req->SetExtraRequestHeaders(headers); |
| } |
| req->Start(); |
| d.RunUntilComplete(); |
| EXPECT_EQ(redirect_method, req->method()); |
| EXPECT_EQ(OK, d.request_status()); |
| if (include_data) { |
| if (request_method == redirect_method) { |
| EXPECT_TRUE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentLength)); |
| EXPECT_TRUE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentType)); |
| EXPECT_EQ(kData, d.data_received()); |
| } else { |
| EXPECT_FALSE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentLength)); |
| EXPECT_FALSE(req->extra_request_headers().HasHeader( |
| HttpRequestHeaders::kContentType)); |
| EXPECT_NE(kData, d.data_received()); |
| } |
| } |
| if (HasFailure()) |
| LOG(WARNING) << "Request method was: " << request_method; |
| } |
| |
| // Requests |redirect_url|, which must return a HTTP 3xx redirect. It's also |
| // used as the initial origin. |
| // |request_method| is the method to use for the initial request. |
| // |redirect_method| is the method that is expected to be used for the second |
| // request, after redirection. |
| // |expected_origin_value| is the expected value for the Origin header after |
| // redirection. If empty, expects that there will be no Origin header. |
| void HTTPRedirectOriginHeaderTest(const GURL& redirect_url, |
| const std::string& request_method, |
| const std::string& redirect_method, |
| const std::string& expected_origin_value) { |
| TestDelegate d; |
| std::unique_ptr<URLRequest> req(default_context().CreateFirstPartyRequest( |
| redirect_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| req->set_method(request_method); |
| req->SetExtraRequestHeaderByName(HttpRequestHeaders::kOrigin, |
| redirect_url.GetOrigin().spec(), false); |
| req->Start(); |
| |
| d.RunUntilComplete(); |
| |
| EXPECT_EQ(redirect_method, req->method()); |
| // Note that there is no check for request success here because, for |
| // purposes of testing, the request very well may fail. For example, if the |
| // test redirects to an HTTPS server from an HTTP origin, thus it is cross |
| // origin, there is not an HTTPS server in this unit test framework, so the |
| // request would fail. However, that's fine, as long as the request headers |
| // are in order and pass the checks below. |
| if (expected_origin_value.empty()) { |
| EXPECT_FALSE( |
| req->extra_request_headers().HasHeader(HttpRequestHeaders::kOrigin)); |
| } else { |
| std::string origin_header; |
| EXPECT_TRUE(req->extra_request_headers().GetHeader( |
| HttpRequestHeaders::kOrigin, &origin_header)); |
| EXPECT_EQ(expected_origin_value, origin_header); |
| } |
| } |
| |
| void HTTPUploadDataOperationTest(const std::string& method) { |
|